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
8#include "app/emu/emulator.h"
10#include "imgui/imgui.h"
11
12namespace yaze {
13namespace editor {
14
26 public:
28 : player_(player) {}
29
30 // ==========================================================================
31 // EditorPanel Identity
32 // ==========================================================================
33
34 std::string GetId() const override { return "music.audio_debug"; }
35 std::string GetDisplayName() const override { return "Audio Debug"; }
36 std::string GetIcon() const override { return ICON_MD_BUG_REPORT; }
37 std::string GetEditorCategory() const override { return "Music"; }
38 int GetPriority() const override { return 95; } // Just before Help
39
40 // ==========================================================================
41 // EditorPanel Drawing
42 // ==========================================================================
43
44 void Draw(bool* p_open) override {
45 if (!player_) {
46 ImGui::TextDisabled("Music player not available");
47 return;
48 }
49
50 emu::Emulator* debug_emu = player_->emulator();
51 if (!debug_emu || !debug_emu->is_snes_initialized()) {
52 ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.4f, 1.0f),
53 ICON_MD_INFO " Play a song to initialize audio");
54 ImGui::Separator();
55 ImGui::TextDisabled("Audio emulator not initialized");
56 return;
57 }
58
59 auto* audio_backend = debug_emu->audio_backend();
60 if (!audio_backend) {
61 ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f),
62 ICON_MD_ERROR " No audio backend!");
63 return;
64 }
65
66 DrawBackendInfo(audio_backend);
67 ImGui::Separator();
68 DrawQueueStatus(audio_backend);
69 ImGui::Separator();
70 DrawResamplingStatus(audio_backend);
71 ImGui::Separator();
73 ImGui::Separator();
75 ImGui::Separator();
77 }
78
79 private:
81 auto config = backend->GetConfig();
82
83 ImGui::Text(ICON_MD_SPEAKER " Backend Configuration");
84 ImGui::Indent();
85 ImGui::Text("Backend: %s", backend->GetBackendName().c_str());
86 ImGui::Text("Device Rate: %d Hz", config.sample_rate);
87 ImGui::Text("Native Rate: 32040 Hz (SPC700)");
88 ImGui::Text("Channels: %d", config.channels);
89 ImGui::Text("Buffer Frames: %d", config.buffer_frames);
90 ImGui::Unindent();
91 }
92
94 auto status = backend->GetStatus();
95
96 ImGui::Text(ICON_MD_QUEUE_MUSIC " Queue Status");
97 ImGui::Indent();
98
99 if (status.is_playing) {
100 ImGui::TextColored(ImVec4(0.3f, 0.9f, 0.3f, 1.0f), "Status: Playing");
101 } else {
102 ImGui::TextColored(ImVec4(0.9f, 0.9f, 0.3f, 1.0f), "Status: Stopped");
103 }
104
105 ImGui::Text("Queued Frames: %u", status.queued_frames);
106 ImGui::Text("Queued Bytes: %u", status.queued_bytes);
107
108 if (status.has_underrun) {
109 ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f),
110 ICON_MD_WARNING " Underrun detected!");
111 }
112
113 ImGui::Unindent();
114 }
115
117 auto config = backend->GetConfig();
118 bool resampling_enabled = backend->IsAudioStreamEnabled();
119
120 ImGui::Text(ICON_MD_TRANSFORM " Resampling");
121 ImGui::Indent();
122
123 if (resampling_enabled) {
124 float ratio = static_cast<float>(config.sample_rate) / 32040.0f;
125 ImGui::TextColored(ImVec4(0.3f, 0.9f, 0.3f, 1.0f),
126 "Status: ENABLED (32040 -> %d Hz)", config.sample_rate);
127 ImGui::Text("Ratio: %.3f", ratio);
128
129 // Check for correct ratio (should be ~1.498 for 32040->48000)
130 if (ratio < 1.4f || ratio > 1.6f) {
131 ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f),
132 ICON_MD_WARNING " Unexpected ratio!");
133 }
134 } else {
135 ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f),
136 "Status: DISABLED");
137 ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f),
138 ICON_MD_WARNING " Audio will play at 1.5x speed!");
139 }
140
141 // Playback speed info
142 auto player_state = player_->GetState();
143 ImGui::Text("Playback Speed: %.2fx", player_state.playback_speed);
144 ImGui::Text("Effective Rate: %.0f Hz", 32040.0f * player_state.playback_speed);
145
146 ImGui::Unindent();
147 }
148
150 auto dsp_status = player_->GetDspStatus();
151
152 ImGui::Text(ICON_MD_MEMORY " DSP Status");
153 ImGui::Indent();
154
155 ImGui::Text("Sample Offset: %u", dsp_status.sample_offset);
156 ImGui::Text("Frame Boundary: %u", dsp_status.frame_boundary);
157 ImGui::Text("Master Vol L/R: %d / %d", dsp_status.master_vol_l,
158 dsp_status.master_vol_r);
159
160 if (dsp_status.mute) {
161 ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f), "Muted");
162 }
163 if (dsp_status.reset) {
164 ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "Reset");
165 }
166
167 ImGui::Text("Echo: %s (delay: %u)", dsp_status.echo_enabled ? "ON" : "OFF",
168 dsp_status.echo_delay);
169
170 ImGui::Unindent();
171 }
172
174 auto apu_status = player_->GetApuStatus();
175
176 ImGui::Text(ICON_MD_TIMER " APU Status");
177 ImGui::Indent();
178
179 ImGui::Text("Cycles: %llu", apu_status.cycles);
180
181 // Timers in columns
182 if (ImGui::BeginTable("ApuTimers", 4, ImGuiTableFlags_Borders)) {
183 ImGui::TableSetupColumn("Timer");
184 ImGui::TableSetupColumn("Enabled");
185 ImGui::TableSetupColumn("Counter");
186 ImGui::TableSetupColumn("Target");
187 ImGui::TableHeadersRow();
188
189 // Timer 0
190 ImGui::TableNextRow();
191 ImGui::TableSetColumnIndex(0);
192 ImGui::Text("T0");
193 ImGui::TableSetColumnIndex(1);
194 ImGui::Text("%s", apu_status.timer0_enabled ? "ON" : "OFF");
195 ImGui::TableSetColumnIndex(2);
196 ImGui::Text("%u", apu_status.timer0_counter);
197 ImGui::TableSetColumnIndex(3);
198 ImGui::Text("%u", apu_status.timer0_target);
199
200 // Timer 1
201 ImGui::TableNextRow();
202 ImGui::TableSetColumnIndex(0);
203 ImGui::Text("T1");
204 ImGui::TableSetColumnIndex(1);
205 ImGui::Text("%s", apu_status.timer1_enabled ? "ON" : "OFF");
206 ImGui::TableSetColumnIndex(2);
207 ImGui::Text("%u", apu_status.timer1_counter);
208 ImGui::TableSetColumnIndex(3);
209 ImGui::Text("%u", apu_status.timer1_target);
210
211 // Timer 2
212 ImGui::TableNextRow();
213 ImGui::TableSetColumnIndex(0);
214 ImGui::Text("T2");
215 ImGui::TableSetColumnIndex(1);
216 ImGui::Text("%s", apu_status.timer2_enabled ? "ON" : "OFF");
217 ImGui::TableSetColumnIndex(2);
218 ImGui::Text("%u", apu_status.timer2_counter);
219 ImGui::TableSetColumnIndex(3);
220 ImGui::Text("%u", apu_status.timer2_target);
221
222 ImGui::EndTable();
223 }
224
225 // Ports
226 ImGui::Text("Ports In: %02X %02X", apu_status.port0_in, apu_status.port1_in);
227 ImGui::Text("Ports Out: %02X %02X", apu_status.port0_out, apu_status.port1_out);
228
229 ImGui::Unindent();
230 }
231
233 ImGui::Text(ICON_MD_BUILD " Debug Actions");
234 ImGui::Indent();
235
236 if (ImGui::Button("Clear Audio Queue")) {
238 }
239 ImGui::SameLine();
240 if (ImGui::Button("Reset DSP Buffer")) {
242 }
243 ImGui::SameLine();
244 if (ImGui::Button("Force NewFrame")) {
246 }
247
248 if (ImGui::Button("Reinit Audio")) {
250 }
251
252 ImGui::Unindent();
253 }
254
256};
257
258} // namespace editor
259} // namespace yaze
260
261#endif // YAZE_APP_EDITOR_MUSIC_PANELS_MUSIC_AUDIO_DEBUG_PANEL_H_
262
Base interface for all logical panel components.
EditorPanel 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)
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:39
bool is_snes_initialized() const
Definition emulator.h:122
audio::IAudioBackend * audio_backend()
Definition emulator.h:74
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