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_) on_edit_();
68 }
69 }
70
71 ImGui::Separator();
72
73 for (size_t i = 0; i < bank.GetSampleCount(); ++i) {
74 const auto* sample = bank.GetSample(i);
75 std::string label = absl::StrFormat("%02X: %s", i, sample->name.c_str());
76 if (ImGui::Selectable(label.c_str(), selected_sample_index_ == static_cast<int>(i))) {
77 selected_sample_index_ = static_cast<int>(i);
78 }
79 }
80}
81
83 bool changed = false;
84
85 ImGui::Text("Sample Properties");
86 ImGui::Separator();
87
88 // Name
89 char name_buf[64];
90 strncpy(name_buf, sample.name.c_str(), sizeof(name_buf));
91 if (ImGui::InputText("Name", name_buf, sizeof(name_buf))) {
92 sample.name = name_buf;
93 changed = true;
94 }
95
96 ImGui::Spacing();
97
98 // Size Info
99 ImGui::Text("BRR Size: %zu bytes", sample.brr_data.size());
100 int blocks = static_cast<int>(sample.brr_data.size() / 9);
101 ImGui::Text("Blocks: %d", blocks);
102 ImGui::Text("Duration: %.3f s", (blocks * 16) / 32040.0f);
103
104 ImGui::Spacing();
105 ImGui::Separator();
106 ImGui::Text("Loop Settings");
107 ImGui::SameLine();
108 HelpMarker("SNES samples can loop. The loop point is defined in BRR blocks (groups of 16 samples).");
109
110 // Loop Flag
111 bool loops = sample.loops;
112 if (ImGui::Checkbox("Loop Enabled", &loops)) {
113 sample.loops = loops;
114 changed = true;
115 }
116
117 // Loop Point
118 // Stored as byte offset in brr_data (must be multiple of 9)
119 int loop_block = sample.loop_point / 9;
120 int max_block = std::max(0, blocks - 1);
121
122 if (loops) {
123 if (ImGui::SliderInt("Loop Start (Block)", &loop_block, 0, max_block)) {
124 sample.loop_point = loop_block * 9;
125 changed = true;
126 }
127 ImGui::TextDisabled("Offset: $%04X bytes", sample.loop_point);
128 ImGui::TextDisabled("Sample: %d", loop_block * 16);
129 } else {
130 ImGui::BeginDisabled();
131 ImGui::SliderInt("Loop Start (Block)", &loop_block, 0, max_block);
132 ImGui::EndDisabled();
133 }
134
135 ImGui::Spacing();
136 ImGui::Separator();
137
138 if (on_preview_) {
139 if (ImGui::Button(ICON_MD_PLAY_ARROW " Preview Sample")) {
141 }
142 if (ImGui::IsItemHovered()) {
143 ImGui::SetTooltip("Play this sample at default pitch (requires ROM loaded)");
144 }
145 } else {
146 ImGui::BeginDisabled();
147 ImGui::Button(ICON_MD_PLAY_ARROW " Preview Sample");
148 ImGui::EndDisabled();
149 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
150 ImGui::SetTooltip("Preview not available - load a ROM first");
151 }
152 }
153
154 if (changed && on_edit_) {
155 on_edit_();
156 }
157}
158
160 // Ensure ImPlot context exists before plotting
162
163 if (sample.pcm_data.empty()) {
164 ImGui::TextDisabled("Empty sample (No PCM data)");
165 ImGui::TextWrapped("Import a WAV file or BRR sample to view waveform.");
166 return;
167 }
168
169 // Decode BRR for visualization (simplified)
170 // For now, just plot raw bytes as signed values to show *something*
171 // A real BRR decoder is needed for accurate waveform
172
173 plot_x_.clear();
174 plot_y_.clear();
175
176 // Downsample for performance if needed
177 int step = 1;
178 if (sample.pcm_data.size() > 4000) step = static_cast<int>(sample.pcm_data.size()) / 4000;
179 if (step < 1) step = 1;
180
181 for (size_t i = 0; i < sample.pcm_data.size(); i += step) {
182 plot_x_.push_back(static_cast<float>(i));
183 plot_y_.push_back(static_cast<float>(sample.pcm_data[i]) / 32768.0f);
184 }
185
186 if (ImPlot::BeginPlot("Waveform", ImVec2(-1, -1))) {
187 ImPlot::SetupAxes("Sample", "Amplitude");
188 ImPlot::SetupAxesLimits(0, sample.pcm_data.size(), -1.1, 1.1);
189
190 ImPlot::PlotLine("PCM", plot_x_.data(), plot_y_.data(), static_cast<int>(plot_x_.size()));
191
192 // Draw Loop Point
193 if (sample.loops) {
194 double loop_sample = (sample.loop_point / 9.0) * 16.0;
195 ImPlot::TagX(loop_sample, ImVec4(0, 1, 0, 1), "Loop Start");
196 ImPlot::SetNextLineStyle(ImVec4(0, 1, 0, 0.5));
197 double loop_x[] = {loop_sample, loop_sample};
198 double loop_y[] = {-1.0, 1.0};
199 ImPlot::PlotLine("Loop", loop_x, loop_y, 2);
200 }
201
202 ImPlot::EndPlot();
203 }
204}
205
206} // namespace music
207} // namespace editor
208} // 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