1 #include "core/audioapi.hpp"
2 #include "core/dispatch.hpp"
3 #include "core/window.hpp"
5 #include "library/string.hpp"
6 #include "platform/wxwidgets/platform.hpp"
10 #include <wx/control.h>
11 #include <wx/combobox.h>
15 bool vumeter_open
= false;
17 unsigned vu_to_pixels(float vu
)
23 unsigned _vu
= 2 * (vu
+ 100);
29 int to_db(float value
)
33 int v
= 20 / log(10) * log(value
);
41 void connect_events(wxSlider
* s
, wxObjectEventFunction fun
, wxEvtHandler
* obj
)
43 s
->Connect(wxEVT_SCROLL_THUMBTRACK
, fun
, NULL
, obj
);
44 s
->Connect(wxEVT_SCROLL_PAGEDOWN
, fun
, NULL
, obj
);
45 s
->Connect(wxEVT_SCROLL_PAGEUP
, fun
, NULL
, obj
);
46 s
->Connect(wxEVT_SCROLL_LINEDOWN
, fun
, NULL
, obj
);
47 s
->Connect(wxEVT_SCROLL_LINEUP
, fun
, NULL
, obj
);
48 s
->Connect(wxEVT_SCROLL_TOP
, fun
, NULL
, obj
);
49 s
->Connect(wxEVT_SCROLL_BOTTOM
, fun
, NULL
, obj
);
52 uint32_t game_text_buf
[16] = {
53 0x00000000, 0x00000000, 0x3c000000, 0x66000000,
54 0xc2000000, 0xc078ec7c, 0xc00cfec6, 0xde7cd6fe,
55 0xc6ccd6c0, 0xc6ccd6c0, 0x66ccd6c6, 0x3a76c67c,
56 0x00000000, 0x00000000, 0x00000000, 0x00000000
59 uint32_t vout_text_buf
[16] = {
60 0x00000000, 0x00000000, 0xc6000010, 0xc6000030,
61 0xc6000030, 0xc67cccfc, 0xc6c6cc30, 0xc6c6cc30,
62 0xc6c6cc30, 0x6cc6cc30, 0x38c6cc36, 0x107c761c,
63 0x00000000, 0x00000000, 0x00000000, 0x00000000
66 uint32_t vin_text_buf
[16] = {
67 0x00000000, 0x00000000, 0xc6180000, 0xc6180000,
68 0xc6000000, 0xc638dc00, 0xc6186600, 0xc6186600,
69 0xc6186600, 0xc6186600, 0x38186600, 0x103c6600,
70 0x00000000, 0x00000000, 0x00000000, 0x00000000
73 unsigned get_strip_color(unsigned i
)
78 return 51 * (i
- 80) / 4;
82 return 510 - 51 * (i
- 160) / 4;
87 class wxwin_vumeter
: public wxDialog
90 wxwin_vumeter(wxWindow
* parent
);
91 ~wxwin_vumeter() throw();
92 bool ShouldPreventAppExit() const;
93 void on_close(wxCommandEvent
& e
);
94 void on_wclose(wxCloseEvent
& e
);
96 void on_game_change(wxScrollEvent
& e
);
97 void on_vout_change(wxScrollEvent
& e
);
98 void on_vin_change(wxScrollEvent
& e
);
99 void on_game_reset(wxCommandEvent
& e
);
100 void on_vout_reset(wxCommandEvent
& e
);
101 void on_vin_reset(wxCommandEvent
& e
);
102 void on_devsel(wxCommandEvent
& e
);
103 void on_mute(wxCommandEvent
& e
);
105 struct _vupanel
: public wxPanel
107 _vupanel(wxwin_vumeter
* v
)
108 : wxPanel(v
, wxID_ANY
, wxDefaultPosition
, wxSize(320, 64))
111 buffer
.resize(61440);
113 for(unsigned i
= 0; i
< 320; i
++) {
114 unsigned color
= get_strip_color(i
);
116 colorstrip
[3 * i
+ 0] = 255;
117 colorstrip
[3 * i
+ 1] = color
;
119 colorstrip
[3 * i
+ 0] = 255 - (color
- 255);
120 colorstrip
[3 * i
+ 1] = 255;
122 colorstrip
[3 * i
+ 2] = 0;
124 this->Connect(wxEVT_PAINT
, wxPaintEventHandler(_vupanel::on_paint
), NULL
, this);
127 void signal_repaint()
129 mleft
= vu_to_pixels(audioapi_vu_mleft
);
130 mright
= vu_to_pixels(audioapi_vu_mright
);
131 vout
= vu_to_pixels(audioapi_vu_vout
);
132 vin
= vu_to_pixels(audioapi_vu_vin
);
134 obj
->update_sent
= false;
137 void on_paint(wxPaintEvent
& e
)
141 memset(&buffer
[0], 255, buffer
.size());
142 draw_text(0, 8, 32, 16, game_text_buf
);
143 draw_text(0, 24, 32, 16, vout_text_buf
);
144 draw_text(0, 40, 32, 16, vin_text_buf
);
145 for(unsigned i
= 32; i
<= 272; i
+= 40)
146 draw_vline(i
, 0, 63);
147 draw_vline(231, 0, 63); //0dB is thicker.
148 draw_meter(32, 8, 8, mleft
);
149 draw_meter(32, 16, 8, mright
);
150 draw_meter(32, 24, 16, vout
);
151 draw_meter(32, 40, 16, vin
);
152 wxBitmap
bmp2(wxImage(320, 64, &buffer
[0], true));
153 dc
.DrawBitmap(bmp2
, 0, 0, false);
157 volatile unsigned mleft
;
158 volatile unsigned mright
;
159 volatile unsigned vout
;
160 volatile unsigned vin
;
161 std::vector
<unsigned char> buffer
;
162 unsigned char colorstrip
[960];
164 void draw_text(unsigned x
, unsigned y
, unsigned w
, unsigned h
, const uint32_t* buf
)
167 unsigned pos
= y
* bufferstride
+ 3 * x
;
168 for(unsigned j
= 0; j
< h
; j
++) {
169 for(unsigned i
= 0; i
< w
; i
++) {
170 unsigned _spos
= spos
+ i
;
171 unsigned char val
= ((buf
[_spos
>> 5] >> (31 - (_spos
& 31))) & 1) ? 0 : 255;
172 buffer
[pos
+ 3 * i
+ 0] = val
;
173 buffer
[pos
+ 3 * i
+ 1] = val
;
174 buffer
[pos
+ 3 * i
+ 2] = val
;
177 spos
+= 32 * ((w
+ 31) / 32);
180 void draw_vline(unsigned x
, unsigned y1
, unsigned y2
)
182 unsigned pos
= y1
* bufferstride
+ 3 * x
;
183 for(unsigned j
= y1
; j
< y2
; j
++) {
190 void draw_meter(unsigned x
, unsigned y
, unsigned h
, unsigned val
)
194 unsigned pos
= y
* bufferstride
+ 3 * x
;
195 for(unsigned j
= 0; j
< h
; j
++) {
197 memcpy(&buffer
[pos
], colorstrip
, 3 * val
);
202 volatile bool update_sent
;
204 wxButton
* closebutton
;
205 struct dispatch_target
<> vulistener
;
217 struct dispatch_target
<bool> unmuted
;
218 struct dispatch_target
<std::pair
<std::string
, std::string
>> devchange
;
221 wxwin_vumeter::wxwin_vumeter(wxWindow
* parent
)
222 : wxDialog(parent
, wxID_ANY
, wxT("lsnes: VU meter"), wxDefaultPosition
, wxSize(-1, -1))
227 wxFlexGridSizer
* top_s
= new wxFlexGridSizer(5, 1, 0, 0);
230 top_s
->Add(vupanel
= new _vupanel(this));
231 top_s
->Add(rate
= new wxStaticText(this, wxID_ANY
, wxT("")), 0, wxGROW
);
233 wxFlexGridSizer
* slier_s
= new wxFlexGridSizer(3, 3, 0, 0);
234 slier_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Game:")), 0, wxGROW
);
235 slier_s
->Add(gamevol
= new wxSlider(this, wxID_ANY
, to_db(audioapi_music_volume()), -100, 50,
236 wxDefaultPosition
, wxSize(320, -1)), 1, wxGROW
);
237 slier_s
->Add(dgamevol
= new wxButton(this, wxID_ANY
, wxT("Reset")));
238 slier_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Voice out:")), 1, wxGROW
);
239 slier_s
->Add(voutvol
= new wxSlider(this, wxID_ANY
, to_db(audioapi_voicep_volume()), -100, 50,
240 wxDefaultPosition
, wxSize(320, -1)), 1, wxGROW
);
241 slier_s
->Add(dvoutvol
= new wxButton(this, wxID_ANY
, wxT("Reset")));
242 slier_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Voice in:")), 1, wxGROW
);
243 slier_s
->Add(vinvol
= new wxSlider(this, wxID_ANY
, to_db(audioapi_voicer_volume()), -100, 50,
244 wxDefaultPosition
, wxSize(320, -1)), 1, wxGROW
);
245 slier_s
->Add(dvinvol
= new wxButton(this, wxID_ANY
, wxT("Reset")));
246 top_s
->Add(slier_s
, 1, wxGROW
);
248 gamevol
->SetLineSize(1);
249 vinvol
->SetLineSize(1);
250 voutvol
->SetLineSize(1);
251 gamevol
->SetPageSize(10);
252 vinvol
->SetPageSize(10);
253 voutvol
->SetPageSize(10);
254 connect_events(gamevol
, wxScrollEventHandler(wxwin_vumeter::on_game_change
), this);
255 connect_events(voutvol
, wxScrollEventHandler(wxwin_vumeter::on_vout_change
), this);
256 connect_events(vinvol
, wxScrollEventHandler(wxwin_vumeter::on_vin_change
), this);
257 dgamevol
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(wxwin_vumeter::on_game_reset
), NULL
,
259 dvoutvol
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(wxwin_vumeter::on_vout_reset
), NULL
,
261 dvinvol
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(wxwin_vumeter::on_vin_reset
), NULL
,
264 auto pdev_map
= audioapi_driver_get_devices(false);
265 auto rdev_map
= audioapi_driver_get_devices(true);
266 std::string current_pdev
= pdev_map
[audioapi_driver_get_device(false)];
267 std::string current_rdev
= rdev_map
[audioapi_driver_get_device(true)];
268 std::vector
<wxString
> available_pdevs
;
269 std::vector
<wxString
> available_rdevs
;
270 for(auto i
: pdev_map
)
271 available_pdevs
.push_back(towxstring(i
.second
));
272 for(auto i
: rdev_map
)
273 available_rdevs
.push_back(towxstring(i
.second
));
275 wxSizer
* hw_s
= new wxFlexGridSizer(3, 2, 0, 0);
276 hw_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Input device:")), 0, wxGROW
);
277 hw_s
->Add(rdev
= new wxComboBox(this, wxID_ANY
, towxstring(current_rdev
), wxDefaultPosition
,
278 wxSize(-1, -1), available_rdevs
.size(), &available_rdevs
[0], wxCB_READONLY
), 1, wxGROW
);
279 hw_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Output device:")), 0, wxGROW
);
280 hw_s
->Add(pdev
= new wxComboBox(this, wxID_ANY
, towxstring(current_pdev
), wxDefaultPosition
,
281 wxSize(-1, -1), available_pdevs
.size(), &available_pdevs
[0], wxCB_READONLY
), 1, wxGROW
);
282 hw_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("")), 0, wxGROW
);
283 hw_s
->Add(mute
= new wxCheckBox(this, wxID_ANY
, wxT("Mute sounds")), 1, wxGROW
);
284 mute
->SetValue(!platform::is_sound_enabled());
287 wxSizer
* pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
288 pbutton_s
->AddStretchSpacer();
289 pbutton_s
->Add(closebutton
= new wxButton(this, wxID_OK
, wxT("Close")), 0, wxGROW
);
290 top_s
->Add(pbutton_s
, 1, wxGROW
);
291 pbutton_s
->SetSizeHints(this);
293 closebutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
294 wxCommandEventHandler(wxwin_vumeter::on_close
), NULL
, this);
295 rdev
->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED
,
296 wxCommandEventHandler(wxwin_vumeter::on_devsel
), NULL
, this);
297 pdev
->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED
,
298 wxCommandEventHandler(wxwin_vumeter::on_devsel
), NULL
, this);
299 mute
->Connect(wxEVT_CHECKBOX
,
300 wxCommandEventHandler(wxwin_vumeter::on_mute
), NULL
, this);
301 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(wxwin_vumeter::on_wclose
));
303 unmuted
.set(notify_sound_unmute
, [this](bool unmute
) {
304 runuifun([this, unmute
]() { if(!this->closing
) this->mute
->SetValue(!unmute
); });
306 devchange
.set(notify_sound_change
, [this](std::pair
<std::string
, std::string
> d
) {
307 runuifun([this, d
]() {
308 if(this->closing
) return;
309 auto pdevs
= audioapi_driver_get_devices(false);
310 if(pdevs
.count(d
.second
)) this->pdev
->SetValue(pdevs
[d
.second
]);
311 auto rdevs
= audioapi_driver_get_devices(true);
312 if(rdevs
.count(d
.first
)) this->rdev
->SetValue(rdevs
[d
.first
]);
316 top_s
->SetSizeHints(this);
318 vulistener
.set(notify_vu_change
, [this]() {
319 if(!this->update_sent
) {
320 this->update_sent
= true;
321 runuifun([this]() -> void { this->refresh(); });
327 wxwin_vumeter::~wxwin_vumeter() throw()
331 void wxwin_vumeter::on_devsel(wxCommandEvent
& e
)
333 std::string newpdev
= tostdstring(pdev
->GetValue());
334 std::string newrdev
= tostdstring(rdev
->GetValue());
335 std::string _newpdev
= "null";
336 std::string _newrdev
= "null";
337 for(auto i
: audioapi_driver_get_devices(false))
338 if(i
.second
== newpdev
)
340 for(auto i
: audioapi_driver_get_devices(true))
341 if(i
.second
== newrdev
)
343 platform::set_sound_device(_newpdev
, _newrdev
);
346 void wxwin_vumeter::on_game_change(wxScrollEvent
& e
)
348 audioapi_music_volume(pow(10, gamevol
->GetValue() / 20.0));
351 void wxwin_vumeter::on_vout_change(wxScrollEvent
& e
)
353 audioapi_voicep_volume(pow(10, voutvol
->GetValue() / 20.0));
356 void wxwin_vumeter::on_vin_change(wxScrollEvent
& e
)
358 audioapi_voicer_volume(pow(10, vinvol
->GetValue() / 20.0));
361 void wxwin_vumeter::on_game_reset(wxCommandEvent
& e
)
363 audioapi_music_volume(1);
364 gamevol
->SetValue(0);
367 void wxwin_vumeter::on_vout_reset(wxCommandEvent
& e
)
369 audioapi_voicep_volume(1);
370 voutvol
->SetValue(0);
373 void wxwin_vumeter::on_vin_reset(wxCommandEvent
& e
)
375 audioapi_voicer_volume(1);
379 void wxwin_vumeter::on_mute(wxCommandEvent
& e
)
381 platform::sound_enable(!mute
->GetValue());
384 void wxwin_vumeter::refresh()
386 auto rate_cur
= audioapi_voice_rate();
387 unsigned rate_nom
= audioapi_orig_voice_rate();
388 rate
->SetLabel(towxstring((stringfmt() << "Current: " << rate_cur
.second
<< "Hz (nominal " << rate_nom
389 << "Hz), record: " << rate_cur
.first
<< "Hz").str()));
390 vupanel
->signal_repaint();
393 void wxwin_vumeter::on_close(wxCommandEvent
& e
)
397 vumeter_open
= false;
400 void wxwin_vumeter::on_wclose(wxCloseEvent
& e
)
406 vumeter_open
= false;
409 bool wxwin_vumeter::ShouldPreventAppExit() const { return false; }
411 void open_vumeter_window(wxWindow
* parent
)
415 wxwin_vumeter
* v
= new wxwin_vumeter(parent
);