3 #include <wx/control.h>
4 #include <wx/combobox.h>
6 #include "core/dispatch.hpp"
7 #include "core/instance.hpp"
8 #include "core/inthread.hpp"
9 #include "core/project.hpp"
10 #include "core/ui-services.hpp"
11 #include "library/string.hpp"
13 #include "platform/wxwidgets/platform.hpp"
14 #include "platform/wxwidgets/loadsave.hpp"
20 #define NOTHING 0xFFFFFFFFFFFFFFFFULL
24 std::set
<emulator_instance
*> voicesub_open
;
27 class wxeditor_voicesub
: public wxDialog
30 wxeditor_voicesub(wxWindow
* parent
, emulator_instance
& _inst
);
31 ~wxeditor_voicesub() throw();
32 bool ShouldPreventAppExit() const;
33 void on_select(wxCommandEvent
& e
);
34 void on_play(wxCommandEvent
& e
);
35 void on_delete(wxCommandEvent
& e
);
36 void on_export(wxCommandEvent
& e
);
37 void on_export_s(wxCommandEvent
& e
);
38 void on_import(wxCommandEvent
& e
);
39 void on_change_ts(wxCommandEvent
& e
);
40 void on_change_gain(wxCommandEvent
& e
);
41 void on_load(wxCommandEvent
& e
);
42 void on_unload(wxCommandEvent
& e
);
43 void on_refresh(wxCommandEvent
& e
);
44 void on_close(wxCommandEvent
& e
);
45 void on_wclose(wxCloseEvent
& e
);
48 emulator_instance
& inst
;
51 std::map
<int, uint64_t> smap
;
54 wxButton
* deletebutton
;
55 wxButton
* exportpbutton
;
56 wxButton
* exportsbutton
;
57 wxButton
* importpbutton
;
58 wxButton
* changetsbutton
;
59 wxButton
* changegainbutton
;
61 wxButton
* unloadbutton
;
62 wxButton
* refreshbutton
;
63 wxButton
* closebutton
;
64 struct dispatch::target
<> corechange
;
65 struct dispatch::target
<> vstreamchange
;
68 wxeditor_voicesub::wxeditor_voicesub(wxWindow
* parent
, emulator_instance
& _inst
)
69 : wxDialog(parent
, wxID_ANY
, wxT("lsnes: Edit commentary track"), wxDefaultPosition
, wxSize(-1, -1)),
75 wxFlexGridSizer
* top_s
= new wxFlexGridSizer(6, 1, 0, 0);
78 top_s
->Add(subtitles
= new wxListBox(this, wxID_ANY
, wxDefaultPosition
, wxSize(300, 400), 0, NULL
,
79 wxLB_SINGLE
), 1, wxGROW
);
81 wxBoxSizer
* pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
82 pbutton_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Stream")), 0, wxGROW
);
83 pbutton_s
->Add(playbutton
= new wxButton(this, wxID_ANY
, wxT("Play")), 0, wxGROW
);
84 pbutton_s
->Add(deletebutton
= new wxButton(this, wxID_ANY
, wxT("Delete")), 0, wxGROW
);
85 top_s
->Add(pbutton_s
, 1, wxGROW
);
86 pbutton_s
->SetSizeHints(this);
88 pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
89 pbutton_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("I/O")), 0, wxGROW
);
90 pbutton_s
->Add(importpbutton
= new wxButton(this, wxID_ANY
, wxT("Import")), 0, wxGROW
);
91 pbutton_s
->Add(exportpbutton
= new wxButton(this, wxID_ANY
, wxT("Export")), 0, wxGROW
);
92 pbutton_s
->Add(exportsbutton
= new wxButton(this, wxID_ANY
, wxT("Export all")), 0, wxGROW
);
93 top_s
->Add(pbutton_s
, 1, wxGROW
);
94 pbutton_s
->SetSizeHints(this);
96 pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
97 pbutton_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Misc.")), 0, wxGROW
);
98 pbutton_s
->Add(changetsbutton
= new wxButton(this, wxID_ANY
, wxT("Change time")), 0, wxGROW
);
99 pbutton_s
->Add(changegainbutton
= new wxButton(this, wxID_ANY
, wxT("Change gain")), 0, wxGROW
);
100 top_s
->Add(pbutton_s
, 1, wxGROW
);
101 pbutton_s
->SetSizeHints(this);
103 pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
104 pbutton_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Collection")), 0, wxGROW
);
105 pbutton_s
->Add(loadbutton
= new wxButton(this, wxID_ANY
, wxT("Load")), 0, wxGROW
);
106 pbutton_s
->Add(unloadbutton
= new wxButton(this, wxID_ANY
, wxT("Unload")), 0, wxGROW
);
107 top_s
->Add(pbutton_s
, 1, wxGROW
);
108 pbutton_s
->SetSizeHints(this);
110 pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
111 pbutton_s
->AddStretchSpacer();
112 pbutton_s
->Add(refreshbutton
= new wxButton(this, wxID_OK
, wxT("Refresh")), 0, wxGROW
);
113 pbutton_s
->Add(closebutton
= new wxButton(this, wxID_OK
, wxT("Close")), 0, wxGROW
);
114 top_s
->Add(pbutton_s
, 1, wxGROW
);
115 pbutton_s
->SetSizeHints(this);
117 playbutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
118 wxCommandEventHandler(wxeditor_voicesub::on_play
), NULL
, this);
119 deletebutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
120 wxCommandEventHandler(wxeditor_voicesub::on_delete
), NULL
, this);
121 exportpbutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
122 wxCommandEventHandler(wxeditor_voicesub::on_export
), NULL
, this);
123 exportsbutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
124 wxCommandEventHandler(wxeditor_voicesub::on_export_s
), NULL
, this);
125 importpbutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
126 wxCommandEventHandler(wxeditor_voicesub::on_import
), NULL
, this);
127 changetsbutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
128 wxCommandEventHandler(wxeditor_voicesub::on_change_ts
), NULL
, this);
129 changegainbutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
130 wxCommandEventHandler(wxeditor_voicesub::on_change_gain
), NULL
, this);
131 loadbutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
132 wxCommandEventHandler(wxeditor_voicesub::on_load
), NULL
, this);
133 unloadbutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
134 wxCommandEventHandler(wxeditor_voicesub::on_unload
), NULL
, this);
135 refreshbutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
136 wxCommandEventHandler(wxeditor_voicesub::on_refresh
), NULL
, this);
137 closebutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
138 wxCommandEventHandler(wxeditor_voicesub::on_close
), NULL
, this);
139 subtitles
->Connect(wxEVT_COMMAND_LISTBOX_SELECTED
,
140 wxCommandEventHandler(wxeditor_voicesub::on_select
), NULL
, this);
141 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(wxeditor_voicesub::on_wclose
));
143 top_s
->SetSizeHints(this);
145 vstreamchange
.set(inst
.dispatch
->voice_stream_change
, [this]() {
146 runuifun([this]() -> void { this->refresh(); });
148 corechange
.set(inst
.dispatch
->core_change
, [this]() {
149 runuifun([this]() -> void { this->refresh(); });
154 wxeditor_voicesub::~wxeditor_voicesub() throw()
158 void wxeditor_voicesub::on_select(wxCommandEvent
& e
)
163 uint64_t id
= get_id();
164 bool valid
= (id
!= NOTHING
);
165 playbutton
->Enable(valid
);
166 deletebutton
->Enable(valid
);
167 exportpbutton
->Enable(valid
);
168 changetsbutton
->Enable(valid
);
169 changegainbutton
->Enable(valid
);
172 void wxeditor_voicesub::on_play(wxCommandEvent
& e
)
175 uint64_t id
= get_id();
179 inst
.commentary
->play_stream(id
);
180 } catch(std::exception
& e
) {
181 show_message_ok(this, "Error playing", e
.what(), wxICON_EXCLAMATION
);
185 void wxeditor_voicesub::on_delete(wxCommandEvent
& e
)
188 uint64_t id
= get_id();
192 inst
.commentary
->delete_stream(id
);
193 } catch(std::exception
& e
) {
194 show_message_ok(this, "Error deleting", e
.what(), wxICON_EXCLAMATION
);
203 typedef std::pair
<std::string
, enum voice_commentary::external_stream_format
> returntype
;
205 filedialog_input_params
input(bool save
) const
207 filedialog_input_params p
;
208 p
.types
.push_back(filedialog_type_entry("Opus streams", "*.opus", "opus"));
209 p
.types
.push_back(filedialog_type_entry("SoX files", "*.sox", "sox"));
213 std::pair
<std::string
, enum voice_commentary::external_stream_format
> output(
214 const filedialog_output_params
& p
, bool save
) const
216 return std::make_pair(p
.path
, (p
.typechoice
== 1) ? voice_commentary::EXTFMT_SOX
:
217 voice_commentary::EXTFMT_OGGOPUS
);
222 void wxeditor_voicesub::on_export(wxCommandEvent
& e
)
225 uint64_t id
= get_id();
229 auto filename
= choose_file_save(this, "Select file to epxort", UI_get_project_otherpath(inst
),
231 inst
.commentary
->export_stream(id
, filename
.first
, filename
.second
);
232 } catch(canceled_exception
& e
) {
233 } catch(std::exception
& e
) {
234 show_message_ok(this, "Error exporting", e
.what(), wxICON_EXCLAMATION
);
238 void wxeditor_voicesub::on_export_s(wxCommandEvent
& e
)
242 std::string filename
;
243 filename
= choose_file_save(this, "Select file to export superstream",
244 UI_get_project_otherpath(inst
), filetype_sox
);
245 inst
.commentary
->export_superstream(filename
);
246 } catch(canceled_exception
& e
) {
247 } catch(std::exception
& e
) {
248 show_message_ok(this, "Error exporting superstream", e
.what(), wxICON_EXCLAMATION
);
252 void wxeditor_voicesub::on_import(wxCommandEvent
& e
)
257 ts
= inst
.commentary
->parse_timebase(pick_text(this, "Enter timebase",
258 "Enter position for newly imported stream"));
259 auto filename
= choose_file_save(this, "Select file to import", UI_get_project_otherpath(inst
),
261 inst
.commentary
->import_stream(ts
, filename
.first
, filename
.second
);
262 } catch(canceled_exception
& e
) {
263 } catch(std::exception
& e
) {
264 show_message_ok(this, "Error importing", e
.what(), wxICON_EXCLAMATION
);
268 void wxeditor_voicesub::on_change_ts(wxCommandEvent
& e
)
271 uint64_t id
= get_id();
276 ts
= inst
.commentary
->parse_timebase(pick_text(this, "Enter timebase",
277 "Enter new position for stream"));
278 inst
.commentary
->alter_timebase(id
, ts
);
279 } catch(canceled_exception
& e
) {
280 } catch(std::exception
& e
) {
281 show_message_ok(this, "Error changing timebase", e
.what(), wxICON_EXCLAMATION
);
285 void wxeditor_voicesub::on_change_gain(wxCommandEvent
& e
)
288 uint64_t id
= get_id();
293 std::string old
= (stringfmt() << inst
.commentary
->get_gain(id
)).str();
294 gain
= parse_value
<float>(pick_text(this, "Enter gain", "Enter new gain (dB) for stream", old
));
295 inst
.commentary
->set_gain(id
, gain
);
296 } catch(canceled_exception
& e
) {
297 } catch(std::exception
& e
) {
298 show_message_ok(this, "Error changing gain", e
.what(), wxICON_EXCLAMATION
);
302 void wxeditor_voicesub::on_load(wxCommandEvent
& e
)
305 if(UI_in_project_context(inst
))
308 std::string filename
;
310 //Use "." here because there can't be active project.
311 filename
= choose_file_load(this, "Select collection to load", ".", filetype_commentary
);
315 inst
.commentary
->load_collection(filename
);
316 } catch(canceled_exception
& e
) {
317 } catch(std::exception
& e
) {
318 show_message_ok(this, "Error loading collection", e
.what(), wxICON_EXCLAMATION
);
322 void wxeditor_voicesub::on_unload(wxCommandEvent
& e
)
325 if(UI_in_project_context(inst
))
327 inst
.commentary
->unload_collection();
330 void wxeditor_voicesub::on_refresh(wxCommandEvent
& e
)
335 void wxeditor_voicesub::on_close(wxCommandEvent
& e
)
338 voicesub_open
.erase(&inst
);
342 void wxeditor_voicesub::refresh()
347 bool cflag
= inst
.commentary
->collection_loaded();
348 bool pflag
= UI_in_project_context(inst
);
349 unloadbutton
->Enable(cflag
&& !pflag
);
350 loadbutton
->Enable(!pflag
);
351 exportsbutton
->Enable(cflag
);
352 importpbutton
->Enable(cflag
);
353 int sel
= subtitles
->GetSelection();
357 for(auto i
: inst
.commentary
->get_stream_info()) {
359 std::ostringstream tmp
;
360 tmp
<< "#" << i
.id
<< " " << inst
.commentary
->ts_seconds(i
.length
) << "s@"
361 << inst
.commentary
->ts_seconds(i
.base
) << "s";
362 float gain
= inst
.commentary
->get_gain(i
.id
);
363 if(gain
< -1e-5 || gain
> 1e-5)
364 tmp
<< " (gain " << gain
<< "dB)";
365 std::string text
= tmp
.str();
366 subtitles
->Append(towxstring(text
));
368 if(sel
!= wxNOT_FOUND
&& sel
< (ssize_t
)subtitles
->GetCount())
369 subtitles
->SetSelection(sel
);
370 else if(subtitles
->GetCount())
371 subtitles
->SetSelection(0);
376 uint64_t wxeditor_voicesub::get_id()
379 int id
= subtitles
->GetSelection();
380 return smap
.count(id
) ? smap
[id
] : NOTHING
;
383 void wxeditor_voicesub::on_wclose(wxCloseEvent
& e
)
390 voicesub_open
.erase(&inst
);
393 bool wxeditor_voicesub::ShouldPreventAppExit() const { return false; }
395 void show_wxeditor_voicesub(wxWindow
* parent
, emulator_instance
& inst
)
398 if(voicesub_open
.count(&inst
))
400 wxeditor_voicesub
* v
= new wxeditor_voicesub(parent
, inst
);
402 voicesub_open
.insert(&inst
);