yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
music_audio_debug_panel.h
Go to the documentation of this file.
1#ifndef YAZE_APP_EDITOR_MUSIC_PANELS_MUSIC_AUDIO_DEBUG_PANEL_H_
2#define YAZE_APP_EDITOR_MUSIC_PANELS_MUSIC_AUDIO_DEBUG_PANEL_H_
3
4#include <string>
5#include "util/i18n/tr.h"
6
9#include "app/emu/emulator.h"
10#include "app/gui/core/icons.h"
11#include "imgui/imgui.h"
12
13namespace yaze {
14namespace editor {
15
27 public:
29 : player_(player) {}
30
31 // ==========================================================================
32 // WindowContent Identity
33 // ==========================================================================
34
35 std::string GetId() const override { return "music.audio_debug"; }
36 std::string GetDisplayName() const override { return "Audio Debug"; }
37 std::string GetIcon() const override { return ICON_MD_BUG_REPORT; }
38 std::string GetEditorCategory() const override { return "Music"; }
39 int GetPriority() const override { return 95; } // Just before Help
40
41 // ==========================================================================
42 // WindowContent Drawing
43 // ==========================================================================
44
45 void Draw(bool* p_open) override {
46 if (!player_) {
47 ImGui::TextDisabled(tr("Music player not available"));
48 return;
49 }
50
51 emu::Emulator* debug_emu = player_->emulator();
52 if (!debug_emu || !debug_emu->is_snes_initialized()) {
53 ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.4f, 1.0f),
54 ICON_MD_INFO " Play a song to initialize audio");
55 ImGui::Separator();
56 ImGui::TextDisabled(tr("Audio emulator not initialized"));
57 return;
58 }
59
60 auto* audio_backend = debug_emu->audio_backend();
61 if (!audio_backend) {
62 ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f),
63 ICON_MD_ERROR " No audio backend!");
64 return;
65 }
66
67 DrawBackendInfo(audio_backend);
68 ImGui::Separator();
69 DrawQueueStatus(audio_backend);
70 ImGui::Separator();
71 DrawResamplingStatus(audio_backend);
72 ImGui::Separator();
74 ImGui::Separator();
76 ImGui::Separator();
78 }
79
80 private:
82 auto config = backend->GetConfig();
83
84 ImGui::Text(ICON_MD_SPEAKER " Backend Configuration");
85 ImGui::Indent();
86 ImGui::Text(tr("Backend: %s"), backend->GetBackendName().c_str());
87 ImGui::Text(tr("Device Rate: %d Hz"), config.sample_rate);
88 ImGui::Text(tr("Native Rate: 32040 Hz (SPC700)"));
89 ImGui::Text(tr("Channels: %d"), config.channels);
90 ImGui::Text(tr("Buffer Frames: %d"), config.buffer_frames);
91 ImGui::Unindent();
92 }
93
95 auto status = backend->GetStatus();
96
97 ImGui::Text(ICON_MD_QUEUE_MUSIC " Queue Status");
98 ImGui::Indent();
99
100 if (status.is_playing) {
101 ImGui::TextColored(ImVec4(0.3f, 0.9f, 0.3f, 1.0f), tr("Status: Playing"));
102 } else {
103 ImGui::TextColored(ImVec4(0.9f, 0.9f, 0.3f, 1.0f), tr("Status: Stopped"));
104 }
105
106 ImGui::Text(tr("Queued Frames: %u"), status.queued_frames);
107 ImGui::Text(tr("Queued Bytes: %u"), status.queued_bytes);
108
109 if (status.has_underrun) {
110 ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f),
111 ICON_MD_WARNING " Underrun detected!");
112 }
113
114 ImGui::Unindent();
115 }
116
118 auto config = backend->GetConfig();
119 bool resampling_enabled = backend->IsAudioStreamEnabled();
120
121 ImGui::Text(ICON_MD_TRANSFORM " Resampling");
122 ImGui::Indent();
123
124 if (resampling_enabled) {
125 float ratio = static_cast<float>(config.sample_rate) / 32040.0f;
126 ImGui::TextColored(ImVec4(0.3f, 0.9f, 0.3f, 1.0f),
127 tr("Status: ENABLED (32040 -> %d Hz)"),
128 config.sample_rate);
129 ImGui::Text(tr("Ratio: %.3f"), ratio);
130
131 // Check for correct ratio (should be ~1.498 for 32040->48000)
132 if (ratio < 1.4f || ratio > 1.6f) {
133 ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f),
134 ICON_MD_WARNING " Unexpected ratio!");
135 }
136 } else {
137 ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f),
138 tr("Status: DISABLED"));
139 ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f),
140 ICON_MD_WARNING " Audio will play at 1.5x speed!");
141 }
142
143 // Playback speed info
144 auto player_state = player_->GetState();
145 ImGui::Text(tr("Playback Speed: %.2fx"), player_state.playback_speed);
146 ImGui::Text(tr("Effective Rate: %.0f Hz"),
147 32040.0f * player_state.playback_speed);
148
149 ImGui::Unindent();
150 }
151
153 auto dsp_status = player_->GetDspStatus();
154
155 ImGui::Text(ICON_MD_MEMORY " DSP Status");
156 ImGui::Indent();
157
158 ImGui::Text(tr("Sample Offset: %u"), dsp_status.sample_offset);
159 ImGui::Text(tr("Frame Boundary: %u"), dsp_status.frame_boundary);
160 ImGui::Text(tr("Master Vol L/R: %d / %d"), dsp_status.master_vol_l,
161 dsp_status.master_vol_r);
162
163 if (dsp_status.mute) {
164 ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f), tr("Muted"));
165 }
166 if (dsp_status.reset) {
167 ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), tr("Reset"));
168 }
169
170 ImGui::Text(tr("Echo: %s (delay: %u)"),
171 dsp_status.echo_enabled ? "ON" : "OFF", dsp_status.echo_delay);
172
173 ImGui::Unindent();
174 }
175
177 auto apu_status = player_->GetApuStatus();
178
179 ImGui::Text(ICON_MD_TIMER " APU Status");
180 ImGui::Indent();
181
182 ImGui::Text(tr("Cycles: %llu"), apu_status.cycles);
183
184 // Timers in columns
185 if (ImGui::BeginTable("ApuTimers", 4, ImGuiTableFlags_Borders)) {
186 ImGui::TableSetupColumn("Timer");
187 ImGui::TableSetupColumn("Enabled");
188 ImGui::TableSetupColumn("Counter");
189 ImGui::TableSetupColumn("Target");
190 ImGui::TableHeadersRow();
191
192 // Timer 0
193 ImGui::TableNextRow();
194 ImGui::TableSetColumnIndex(0);
195 ImGui::Text(tr("T0"));
196 ImGui::TableSetColumnIndex(1);
197 ImGui::Text("%s", apu_status.timer0_enabled ? "ON" : "OFF");
198 ImGui::TableSetColumnIndex(2);
199 ImGui::Text("%u", apu_status.timer0_counter);
200 ImGui::TableSetColumnIndex(3);
201 ImGui::Text("%u", apu_status.timer0_target);
202
203 // Timer 1
204 ImGui::TableNextRow();
205 ImGui::TableSetColumnIndex(0);
206 ImGui::Text(tr("T1"));
207 ImGui::TableSetColumnIndex(1);
208 ImGui::Text("%s", apu_status.timer1_enabled ? "ON" : "OFF");
209 ImGui::TableSetColumnIndex(2);
210 ImGui::Text("%u", apu_status.timer1_counter);
211 ImGui::TableSetColumnIndex(3);
212 ImGui::Text("%u", apu_status.timer1_target);
213
214 // Timer 2
215 ImGui::TableNextRow();
216 ImGui::TableSetColumnIndex(0);
217 ImGui::Text(tr("T2"));
218 ImGui::TableSetColumnIndex(1);
219 ImGui::Text("%s", apu_status.timer2_enabled ? "ON" : "OFF");
220 ImGui::TableSetColumnIndex(2);
221 ImGui::Text("%u", apu_status.timer2_counter);
222 ImGui::TableSetColumnIndex(3);
223 ImGui::Text("%u", apu_status.timer2_target);
224
225 ImGui::EndTable();
226 }
227
228 // Ports
229 ImGui::Text(tr("Ports In: %02X %02X"), apu_status.port0_in,
230 apu_status.port1_in);
231 ImGui::Text(tr("Ports Out: %02X %02X"), apu_status.port0_out,
232 apu_status.port1_out);
233
234 ImGui::Unindent();
235 }
236
238 ImGui::Text(ICON_MD_BUILD " Debug Actions");
239 ImGui::Indent();
240
241 if (ImGui::Button(tr("Clear Audio Queue"))) {
243 }
244 ImGui::SameLine();
245 if (ImGui::Button(tr("Reset DSP Buffer"))) {
247 }
248 ImGui::SameLine();
249 if (ImGui::Button(tr("Force NewFrame"))) {
251 }
252
253 if (ImGui::Button(tr("Reinit Audio"))) {
255 }
256
257 ImGui::Unindent();
258 }
259
261};
262
263} // namespace editor
264} // namespace yaze
265
266#endif // YAZE_APP_EDITOR_MUSIC_PANELS_MUSIC_AUDIO_DEBUG_PANEL_H_
WindowContent providing audio diagnostics for debugging the music editor.
void DrawBackendInfo(emu::audio::IAudioBackend *backend)
void DrawResamplingStatus(emu::audio::IAudioBackend *backend)
std::string GetDisplayName() const override
Human-readable name shown in menus and title bars.
void Draw(bool *p_open) override
Draw the panel content.
int GetPriority() const override
Get display priority for menu ordering.
std::string GetId() const override
Unique identifier for this panel.
MusicAudioDebugPanel(editor::music::MusicPlayer *player)
std::string GetIcon() const override
Material Design icon for this panel.
std::string GetEditorCategory() const override
Editor category this panel belongs to.
void DrawQueueStatus(emu::audio::IAudioBackend *backend)
Base interface for all logical window content components.
Handles audio playback for the music editor using the SNES APU emulator.
ApuDebugStatus GetApuStatus() const
Get APU timing diagnostic status.
void ReinitAudio()
Reinitialize the audio system.
void ForceNewFrame()
Force a DSP NewFrame() call.
DspDebugStatus GetDspStatus() const
Get DSP buffer diagnostic status.
PlaybackState GetState() const
void ResetDspBuffer()
Reset the DSP sample buffer.
void ClearAudioQueue()
Clear the audio queue (stops sound immediately).
A class for emulating and debugging SNES games.
Definition emulator.h:41
bool is_snes_initialized() const
Definition emulator.h:129
audio::IAudioBackend * audio_backend()
Definition emulator.h:77
Abstract audio backend interface.
virtual std::string GetBackendName() const =0
virtual AudioStatus GetStatus() const =0
virtual AudioConfig GetConfig() const =0
virtual bool IsAudioStreamEnabled() const
#define ICON_MD_INFO
Definition icons.h:993
#define ICON_MD_MEMORY
Definition icons.h:1195
#define ICON_MD_WARNING
Definition icons.h:2123
#define ICON_MD_QUEUE_MUSIC
Definition icons.h:1538
#define ICON_MD_BUG_REPORT
Definition icons.h:327
#define ICON_MD_ERROR
Definition icons.h:686
#define ICON_MD_TIMER
Definition icons.h:1982
#define ICON_MD_SPEAKER
Definition icons.h:1812
#define ICON_MD_TRANSFORM
Definition icons.h:2008
#define ICON_MD_BUILD
Definition icons.h:328