yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
sample_editor_view.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cmath>
5
6#include "absl/strings/str_format.h"
9#include "imgui/imgui.h"
10#include "implot.h"
11
12namespace yaze {
13namespace editor {
14namespace music {
15
16using namespace yaze::zelda3::music;
17
18static void HelpMarker(const char* desc) {
19 ImGui::TextDisabled("(?)");
20 if (ImGui::IsItemHovered()) {
21 ImGui::BeginTooltip();
22 ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
23 ImGui::TextUnformatted(desc);
24 ImGui::PopTextWrapPos();
25 ImGui::EndTooltip();
26 }
27}
28
30 // Layout: List (20%), Properties (25%), Waveform (Rest)
31 float total_w = ImGui::GetContentRegionAvail().x;
32 float list_w = std::max(150.0f, total_w * 0.2f);
33 float props_w = std::max(220.0f, total_w * 0.25f);
34
35 ImGui::BeginChild("SampleList", ImVec2(list_w, 0), true);
36 DrawSampleList(bank);
37 ImGui::EndChild();
38
39 ImGui::SameLine();
40
41 ImGui::BeginChild("SampleProps", ImVec2(props_w, 0), true);
42 if (selected_sample_index_ >= 0 &&
43 selected_sample_index_ < static_cast<int>(bank.GetSampleCount())) {
45 } else {
46 ImGui::TextDisabled("Select a sample");
47 }
48 ImGui::EndChild();
49
50 ImGui::SameLine();
51
52 ImGui::BeginChild("SampleWaveform", ImVec2(0, 0), true);
53 if (selected_sample_index_ >= 0 &&
54 selected_sample_index_ < static_cast<int>(bank.GetSampleCount())) {
56 }
57 ImGui::EndChild();
58}
59
61 if (ImGui::Button("Import WAV/BRR")) {
62 // TODO: Implement file dialog for BRR import
63 // For now, we simulate an import with a dummy sine wave
64 auto result = bank.ImportSampleFromWav("dummy.wav", "New Sample");
65 if (result.ok()) {
66 selected_sample_index_ = result.value();
67 if (on_edit_)
68 on_edit_();
69 }
70 }
71
72 ImGui::Separator();
73
74 for (size_t i = 0; i < bank.GetSampleCount(); ++i) {
75 const auto* sample = bank.GetSample(i);
76 std::string label = absl::StrFormat("%02X: %s", i, sample->name.c_str());
77 if (ImGui::Selectable(label.c_str(),
78 selected_sample_index_ == static_cast<int>(i))) {
79 selected_sample_index_ = static_cast<int>(i);
80 }
81 }
82}
83
85 bool changed = false;
86
87 ImGui::Text("Sample Properties");
88 ImGui::Separator();
89
90 // Name
91 char name_buf[64];
92 strncpy(name_buf, sample.name.c_str(), sizeof(name_buf));
93 if (ImGui::InputText("Name", name_buf, sizeof(name_buf))) {
94 sample.name = name_buf;
95 changed = true;
96 }
97
98 ImGui::Spacing();
99
100 // Size Info
101 ImGui::Text("BRR Size: %zu bytes", sample.brr_data.size());
102 int blocks = static_cast<int>(sample.brr_data.size() / 9);
103 ImGui::Text("Blocks: %d", blocks);
104 ImGui::Text("Duration: %.3f s", (blocks * 16) / 32040.0f);
105
106 ImGui::Spacing();
107 ImGui::Separator();
108 ImGui::Text("Loop Settings");
109 ImGui::SameLine();
110 HelpMarker(
111 "SNES samples can loop. The loop point is defined in BRR blocks (groups "
112 "of 16 samples).");
113
114 // Loop Flag
115 bool loops = sample.loops;
116 if (ImGui::Checkbox("Loop Enabled", &loops)) {
117 sample.loops = loops;
118 changed = true;
119 }
120
121 // Loop Point
122 // Stored as byte offset in brr_data (must be multiple of 9)
123 int loop_block = sample.loop_point / 9;
124 int max_block = std::max(0, blocks - 1);
125
126 if (loops) {
127 if (ImGui::SliderInt("Loop Start (Block)", &loop_block, 0, max_block)) {
128 sample.loop_point = loop_block * 9;
129 changed = true;
130 }
131 ImGui::TextDisabled("Offset: $%04X bytes", sample.loop_point);
132 ImGui::TextDisabled("Sample: %d", loop_block * 16);
133 } else {
134 ImGui::BeginDisabled();
135 ImGui::SliderInt("Loop Start (Block)", &loop_block, 0, max_block);
136 ImGui::EndDisabled();
137 }
138
139 ImGui::Spacing();
140 ImGui::Separator();
141
142 if (on_preview_) {
143 if (ImGui::Button(ICON_MD_PLAY_ARROW " Preview Sample")) {
145 }
146 if (ImGui::IsItemHovered()) {
147 ImGui::SetTooltip(
148 "Play this sample at default pitch (requires ROM loaded)");
149 }
150 } else {
151 ImGui::BeginDisabled();
152 ImGui::Button(ICON_MD_PLAY_ARROW " Preview Sample");
153 ImGui::EndDisabled();
154 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
155 ImGui::SetTooltip("Preview not available - load a ROM first");
156 }
157 }
158
159 if (changed && on_edit_) {
160 on_edit_();
161 }
162}
163
165 // Ensure ImPlot context exists before plotting
167
168 if (sample.pcm_data.empty()) {
169 ImGui::TextDisabled("Empty sample (No PCM data)");
170 ImGui::TextWrapped("Import a WAV file or BRR sample to view waveform.");
171 return;
172 }
173
174 // Decode BRR for visualization (simplified)
175 // For now, just plot raw bytes as signed values to show *something*
176 // A real BRR decoder is needed for accurate waveform
177
178 plot_x_.clear();
179 plot_y_.clear();
180
181 // Downsample for performance if needed
182 int step = 1;
183 if (sample.pcm_data.size() > 4000)
184 step = static_cast<int>(sample.pcm_data.size()) / 4000;
185 if (step < 1)
186 step = 1;
187
188 for (size_t i = 0; i < sample.pcm_data.size(); i += step) {
189 plot_x_.push_back(static_cast<float>(i));
190 plot_y_.push_back(static_cast<float>(sample.pcm_data[i]) / 32768.0f);
191 }
192
193 if (ImPlot::BeginPlot("Waveform", ImVec2(-1, -1))) {
194 ImPlot::SetupAxes("Sample", "Amplitude");
195 ImPlot::SetupAxesLimits(0, sample.pcm_data.size(), -1.1, 1.1);
196
197 ImPlot::PlotLine("PCM", plot_x_.data(), plot_y_.data(),
198 static_cast<int>(plot_x_.size()));
199
200 // Draw Loop Point
201 if (sample.loops) {
202 double loop_sample = (sample.loop_point / 9.0) * 16.0;
203 ImPlot::TagX(loop_sample, ImVec4(0, 1, 0, 1), "Loop Start");
204 ImPlot::SetNextLineStyle(ImVec4(0, 1, 0, 0.5));
205 double loop_x[] = {loop_sample, loop_sample};
206 double loop_y[] = {-1.0, 1.0};
207 ImPlot::PlotLine("Loop", loop_x, loop_y, 2);
208 }
209
210 ImPlot::EndPlot();
211 }
212}
213
214} // namespace music
215} // namespace editor
216} // namespace yaze
void DrawWaveform(const MusicSample &sample)
void Draw(MusicBank &bank)
Draw the sample editor.
Manages the collection of songs, instruments, and samples from a ROM.
Definition music_bank.h:27
size_t GetSampleCount() const
Get the number of samples.
Definition music_bank.h:198
MusicSample * GetSample(int index)
Get a sample by index.
absl::StatusOr< int > ImportSampleFromWav(const std::string &filepath, const std::string &name)
Import a WAV file as a new sample.
#define ICON_MD_PLAY_ARROW
Definition icons.h:1479
Contains classes and functions for handling music data in Zelda 3.
A BRR-encoded audio sample.
Definition song_data.h:388
std::vector< uint8_t > brr_data
Definition song_data.h:390
std::vector< int16_t > pcm_data
Definition song_data.h:389