3 #include <wx/control.h>
4 #include <wx/combobox.h>
6 #include "core/audioapi.hpp"
7 #include "core/audioapi-driver.hpp"
8 #include "core/dispatch.hpp"
9 #include "core/instance.hpp"
10 #include "core/window.hpp"
12 #include "library/string.hpp"
13 #include "platform/wxwidgets/platform.hpp"
17 std::set
<emulator_instance
*> vumeter_open
;
19 unsigned vu_to_pixels(float vu
)
25 unsigned _vu
= 2 * (vu
+ 100);
31 int to_db(float value
)
35 int v
= 20 / log(10) * log(value
);
43 void connect_events(wxSlider
* s
, wxObjectEventFunction fun
, wxEvtHandler
* obj
)
46 s
->Connect(wxEVT_SCROLL_THUMBTRACK
, fun
, NULL
, obj
);
47 s
->Connect(wxEVT_SCROLL_PAGEDOWN
, fun
, NULL
, obj
);
48 s
->Connect(wxEVT_SCROLL_PAGEUP
, fun
, NULL
, obj
);
49 s
->Connect(wxEVT_SCROLL_LINEDOWN
, fun
, NULL
, obj
);
50 s
->Connect(wxEVT_SCROLL_LINEUP
, fun
, NULL
, obj
);
51 s
->Connect(wxEVT_SCROLL_TOP
, fun
, NULL
, obj
);
52 s
->Connect(wxEVT_SCROLL_BOTTOM
, fun
, NULL
, obj
);
55 uint32_t game_text_buf
[16] = {
56 0x00000000, 0x00000000, 0x3c000000, 0x66000000,
57 0xc2000000, 0xc078ec7c, 0xc00cfec6, 0xde7cd6fe,
58 0xc6ccd6c0, 0xc6ccd6c0, 0x66ccd6c6, 0x3a76c67c,
59 0x00000000, 0x00000000, 0x00000000, 0x00000000
62 uint32_t vout_text_buf
[16] = {
63 0x00000000, 0x00000000, 0xc6000010, 0xc6000030,
64 0xc6000030, 0xc67cccfc, 0xc6c6cc30, 0xc6c6cc30,
65 0xc6c6cc30, 0x6cc6cc30, 0x38c6cc36, 0x107c761c,
66 0x00000000, 0x00000000, 0x00000000, 0x00000000
69 uint32_t vin_text_buf
[16] = {
70 0x00000000, 0x00000000, 0xc6180000, 0xc6180000,
71 0xc6000000, 0xc638dc00, 0xc6186600, 0xc6186600,
72 0xc6186600, 0xc6186600, 0x38186600, 0x103c6600,
73 0x00000000, 0x00000000, 0x00000000, 0x00000000
76 unsigned get_strip_color(unsigned i
)
81 return 51 * (i
- 80) / 4;
85 return 510 - 51 * (i
- 160) / 4;
90 class wxwin_vumeter
: public wxDialog
93 wxwin_vumeter(wxWindow
* parent
, emulator_instance
& _inst
);
94 ~wxwin_vumeter() throw();
95 bool ShouldPreventAppExit() const;
96 void on_close(wxCommandEvent
& e
);
97 void on_wclose(wxCloseEvent
& e
);
99 void on_game_change(wxScrollEvent
& e
);
100 void on_vout_change(wxScrollEvent
& e
);
101 void on_vin_change(wxScrollEvent
& e
);
102 void on_game_reset(wxCommandEvent
& e
);
103 void on_vout_reset(wxCommandEvent
& e
);
104 void on_vin_reset(wxCommandEvent
& e
);
105 void on_devsel(wxCommandEvent
& e
);
106 void on_mute(wxCommandEvent
& e
);
108 struct _vupanel
: public wxPanel
110 _vupanel(wxwin_vumeter
* v
, audioapi_instance
& _audio
)
111 : wxPanel(v
, wxID_ANY
, wxDefaultPosition
, wxSize(320, 64)), audio(_audio
)
115 buffer
.resize(61440);
117 for(unsigned i
= 0; i
< 320; i
++) {
118 unsigned color
= get_strip_color(i
);
120 colorstrip
[3 * i
+ 0] = 255;
121 colorstrip
[3 * i
+ 1] = color
;
123 colorstrip
[3 * i
+ 0] = 255 - (color
- 255);
124 colorstrip
[3 * i
+ 1] = 255;
126 colorstrip
[3 * i
+ 2] = 0;
128 this->Connect(wxEVT_PAINT
, wxPaintEventHandler(_vupanel::on_paint
), NULL
, this);
131 void signal_repaint()
134 mleft
= vu_to_pixels(audio
.vu_mleft
);
135 mright
= vu_to_pixels(audio
.vu_mright
);
136 vout
= vu_to_pixels(audio
.vu_vout
);
137 vin
= vu_to_pixels(audio
.vu_vin
);
139 obj
->update_sent
= false;
142 void on_paint(wxPaintEvent
& e
)
147 memset(&buffer
[0], 255, buffer
.size());
148 draw_text(0, 8, 32, 16, game_text_buf
);
149 draw_text(0, 24, 32, 16, vout_text_buf
);
150 draw_text(0, 40, 32, 16, vin_text_buf
);
151 for(unsigned i
= 32; i
<= 272; i
+= 40)
152 draw_vline(i
, 0, 63);
153 draw_vline(231, 0, 63); //0dB is thicker.
154 draw_meter(32, 8, 8, mleft
);
155 draw_meter(32, 16, 8, mright
);
156 draw_meter(32, 24, 16, vout
);
157 draw_meter(32, 40, 16, vin
);
158 wxBitmap
bmp2(wxImage(320, 64, &buffer
[0], true));
159 dc
.DrawBitmap(bmp2
, 0, 0, false);
163 volatile unsigned mleft
;
164 volatile unsigned mright
;
165 volatile unsigned vout
;
166 volatile unsigned vin
;
167 std::vector
<unsigned char> buffer
;
168 unsigned char colorstrip
[960];
170 audioapi_instance
& audio
;
171 void draw_text(unsigned x
, unsigned y
, unsigned w
, unsigned h
, const uint32_t* buf
)
174 unsigned pos
= y
* bufferstride
+ 3 * x
;
175 for(unsigned j
= 0; j
< h
; j
++) {
176 for(unsigned i
= 0; i
< w
; i
++) {
177 unsigned _spos
= spos
+ i
;
178 unsigned char val
= ((buf
[_spos
>> 5] >> (31 - (_spos
& 31))) & 1) ? 0 : 255;
179 buffer
[pos
+ 3 * i
+ 0] = val
;
180 buffer
[pos
+ 3 * i
+ 1] = val
;
181 buffer
[pos
+ 3 * i
+ 2] = val
;
184 spos
+= 32 * ((w
+ 31) / 32);
187 void draw_vline(unsigned x
, unsigned y1
, unsigned y2
)
189 unsigned pos
= y1
* bufferstride
+ 3 * x
;
190 for(unsigned j
= y1
; j
< y2
; j
++) {
197 void draw_meter(unsigned x
, unsigned y
, unsigned h
, unsigned val
)
201 unsigned pos
= y
* bufferstride
+ 3 * x
;
202 for(unsigned j
= 0; j
< h
; j
++) {
204 memcpy(&buffer
[pos
], colorstrip
, 3 * val
);
209 emulator_instance
& inst
;
210 volatile bool update_sent
;
212 wxButton
* closebutton
;
213 struct dispatch::target
<> vulistener
;
225 struct dispatch::target
<bool> unmuted
;
226 struct dispatch::target
<std::pair
<std::string
, std::string
>> devchange
;
229 wxwin_vumeter::wxwin_vumeter(wxWindow
* parent
, emulator_instance
& _inst
)
230 : wxDialog(parent
, wxID_ANY
, wxT("lsnes: VU meter"), wxDefaultPosition
, wxSize(-1, -1)), inst(_inst
)
236 wxFlexGridSizer
* top_s
= new wxFlexGridSizer(5, 1, 0, 0);
239 top_s
->Add(vupanel
= new _vupanel(this, *inst
.audio
));
240 top_s
->Add(rate
= new wxStaticText(this, wxID_ANY
, wxT("")), 0, wxGROW
);
242 wxFlexGridSizer
* slier_s
= new wxFlexGridSizer(3, 3, 0, 0);
243 slier_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Game:")), 0, wxGROW
);
244 slier_s
->Add(gamevol
= new wxSlider(this, wxID_ANY
, to_db(inst
.audio
->music_volume()), -100, 50,
245 wxDefaultPosition
, wxSize(320, -1)), 1, wxGROW
);
246 slier_s
->Add(dgamevol
= new wxButton(this, wxID_ANY
, wxT("Reset")));
247 slier_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Voice out:")), 1, wxGROW
);
248 slier_s
->Add(voutvol
= new wxSlider(this, wxID_ANY
, to_db(inst
.audio
->voicep_volume()), -100, 50,
249 wxDefaultPosition
, wxSize(320, -1)), 1, wxGROW
);
250 slier_s
->Add(dvoutvol
= new wxButton(this, wxID_ANY
, wxT("Reset")));
251 slier_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Voice in:")), 1, wxGROW
);
252 slier_s
->Add(vinvol
= new wxSlider(this, wxID_ANY
, to_db(inst
.audio
->voicer_volume()), -100, 50,
253 wxDefaultPosition
, wxSize(320, -1)), 1, wxGROW
);
254 slier_s
->Add(dvinvol
= new wxButton(this, wxID_ANY
, wxT("Reset")));
255 top_s
->Add(slier_s
, 1, wxGROW
);
257 gamevol
->SetLineSize(1);
258 vinvol
->SetLineSize(1);
259 voutvol
->SetLineSize(1);
260 gamevol
->SetPageSize(10);
261 vinvol
->SetPageSize(10);
262 voutvol
->SetPageSize(10);
263 connect_events(gamevol
, wxScrollEventHandler(wxwin_vumeter::on_game_change
), this);
264 connect_events(voutvol
, wxScrollEventHandler(wxwin_vumeter::on_vout_change
), this);
265 connect_events(vinvol
, wxScrollEventHandler(wxwin_vumeter::on_vin_change
), this);
266 dgamevol
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(wxwin_vumeter::on_game_reset
), NULL
,
268 dvoutvol
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(wxwin_vumeter::on_vout_reset
), NULL
,
270 dvinvol
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(wxwin_vumeter::on_vin_reset
), NULL
,
273 auto pdev_map
= audioapi_driver_get_devices(false);
274 auto rdev_map
= audioapi_driver_get_devices(true);
275 std::string current_pdev
= pdev_map
[audioapi_driver_get_device(false)];
276 std::string current_rdev
= rdev_map
[audioapi_driver_get_device(true)];
277 std::vector
<wxString
> available_pdevs
;
278 std::vector
<wxString
> available_rdevs
;
279 for(auto i
: pdev_map
)
280 available_pdevs
.push_back(towxstring(i
.second
));
281 for(auto i
: rdev_map
)
282 available_rdevs
.push_back(towxstring(i
.second
));
284 wxSizer
* hw_s
= new wxFlexGridSizer(3, 2, 0, 0);
285 hw_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Input device:")), 0, wxGROW
);
286 hw_s
->Add(rdev
= new wxComboBox(this, wxID_ANY
, towxstring(current_rdev
), wxDefaultPosition
,
287 wxSize(-1, -1), available_rdevs
.size(), &available_rdevs
[0], wxCB_READONLY
), 1, wxGROW
);
288 hw_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Output device:")), 0, wxGROW
);
289 hw_s
->Add(pdev
= new wxComboBox(this, wxID_ANY
, towxstring(current_pdev
), wxDefaultPosition
,
290 wxSize(-1, -1), available_pdevs
.size(), &available_pdevs
[0], wxCB_READONLY
), 1, wxGROW
);
291 hw_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("")), 0, wxGROW
);
292 hw_s
->Add(mute
= new wxCheckBox(this, wxID_ANY
, wxT("Mute sounds")), 1, wxGROW
);
293 mute
->SetValue(!platform::is_sound_enabled());
296 wxSizer
* pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
297 pbutton_s
->AddStretchSpacer();
298 pbutton_s
->Add(closebutton
= new wxButton(this, wxID_OK
, wxT("Close")), 0, wxGROW
);
299 top_s
->Add(pbutton_s
, 1, wxGROW
);
300 pbutton_s
->SetSizeHints(this);
302 closebutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
303 wxCommandEventHandler(wxwin_vumeter::on_close
), NULL
, this);
304 rdev
->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED
,
305 wxCommandEventHandler(wxwin_vumeter::on_devsel
), NULL
, this);
306 pdev
->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED
,
307 wxCommandEventHandler(wxwin_vumeter::on_devsel
), NULL
, this);
308 mute
->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED
,
309 wxCommandEventHandler(wxwin_vumeter::on_mute
), NULL
, this);
310 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(wxwin_vumeter::on_wclose
));
312 unmuted
.set(inst
.dispatch
->sound_unmute
, [this](bool unmute
) {
313 runuifun([this, unmute
]() { if(!this->closing
) this->mute
->SetValue(!unmute
); });
315 devchange
.set(inst
.dispatch
->sound_change
, [this](std::pair
<std::string
, std::string
> d
) {
316 runuifun([this, d
]() {
317 if(this->closing
) return;
318 auto pdevs
= audioapi_driver_get_devices(false);
319 if(pdevs
.count(d
.second
)) this->pdev
->SetValue(towxstring(pdevs
[d
.second
]));
320 auto rdevs
= audioapi_driver_get_devices(true);
321 if(rdevs
.count(d
.first
)) this->rdev
->SetValue(towxstring(rdevs
[d
.first
]));
325 top_s
->SetSizeHints(this);
327 vulistener
.set(inst
.dispatch
->vu_change
, [this]() {
328 if(!this->update_sent
) {
329 this->update_sent
= true;
330 runuifun([this]() -> void { this->refresh(); });
336 wxwin_vumeter::~wxwin_vumeter() throw()
340 void wxwin_vumeter::on_devsel(wxCommandEvent
& e
)
343 std::string newpdev
= tostdstring(pdev
->GetValue());
344 std::string newrdev
= tostdstring(rdev
->GetValue());
345 std::string _newpdev
= "null";
346 std::string _newrdev
= "null";
347 for(auto i
: audioapi_driver_get_devices(false))
348 if(i
.second
== newpdev
)
350 for(auto i
: audioapi_driver_get_devices(true))
351 if(i
.second
== newrdev
)
353 platform::set_sound_device(_newpdev
, _newrdev
);
356 void wxwin_vumeter::on_game_change(wxScrollEvent
& e
)
359 inst
.audio
->music_volume(pow(10, gamevol
->GetValue() / 20.0));
362 void wxwin_vumeter::on_vout_change(wxScrollEvent
& e
)
365 inst
.audio
->voicep_volume(pow(10, voutvol
->GetValue() / 20.0));
368 void wxwin_vumeter::on_vin_change(wxScrollEvent
& e
)
371 inst
.audio
->voicer_volume(pow(10, vinvol
->GetValue() / 20.0));
374 void wxwin_vumeter::on_game_reset(wxCommandEvent
& e
)
377 inst
.audio
->music_volume(1);
378 gamevol
->SetValue(0);
381 void wxwin_vumeter::on_vout_reset(wxCommandEvent
& e
)
384 inst
.audio
->voicep_volume(1);
385 voutvol
->SetValue(0);
388 void wxwin_vumeter::on_vin_reset(wxCommandEvent
& e
)
391 inst
.audio
->voicer_volume(1);
395 void wxwin_vumeter::on_mute(wxCommandEvent
& e
)
398 platform::sound_enable(!mute
->GetValue());
401 void wxwin_vumeter::refresh()
404 auto rate_cur
= inst
.audio
->voice_rate();
405 unsigned rate_nom
= inst
.audio
->orig_voice_rate();
406 rate
->SetLabel(towxstring((stringfmt() << "Current: " << rate_cur
.second
<< "Hz (nominal " << rate_nom
407 << "Hz), record: " << rate_cur
.first
<< "Hz").str()));
408 vupanel
->signal_repaint();
411 void wxwin_vumeter::on_close(wxCommandEvent
& e
)
416 vumeter_open
.erase(&inst
);
419 void wxwin_vumeter::on_wclose(wxCloseEvent
& e
)
426 vumeter_open
.erase(&inst
);
429 bool wxwin_vumeter::ShouldPreventAppExit() const { return false; }
431 void open_vumeter_window(wxWindow
* parent
, emulator_instance
& inst
)
434 if(vumeter_open
.count(&inst
))
436 wxwin_vumeter
* v
= new wxwin_vumeter(parent
, inst
);
438 vumeter_open
.insert(&inst
);