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