Upload UI
[lsnes.git] / src / platform / wxwidgets / editor-voicesub.cpp
blob4941ee7c0577d8ea102ff3b2182abaff55693fad
1 #include "core/inthread.hpp"
2 #include "core/project.hpp"
3 #include <stdexcept>
5 #include "platform/wxwidgets/platform.hpp"
6 #include "platform/wxwidgets/loadsave.hpp"
8 #include <wx/wx.h>
9 #include <wx/event.h>
10 #include <wx/control.h>
11 #include <wx/combobox.h>
13 #include "core/dispatch.hpp"
14 #include "library/string.hpp"
15 #include <sstream>
17 #define NOTHING 0xFFFFFFFFFFFFFFFFULL
19 namespace
21 bool voicesub_open = false;
24 class wxeditor_voicesub : public wxDialog
26 public:
27 wxeditor_voicesub(wxWindow* parent);
28 ~wxeditor_voicesub() throw();
29 bool ShouldPreventAppExit() const;
30 void on_select(wxCommandEvent& e);
31 void on_play(wxCommandEvent& e);
32 void on_delete(wxCommandEvent& e);
33 void on_export(wxCommandEvent& e);
34 void on_export_s(wxCommandEvent& e);
35 void on_import(wxCommandEvent& e);
36 void on_change_ts(wxCommandEvent& e);
37 void on_change_gain(wxCommandEvent& e);
38 void on_load(wxCommandEvent& e);
39 void on_unload(wxCommandEvent& e);
40 void on_refresh(wxCommandEvent& e);
41 void on_close(wxCommandEvent& e);
42 void on_wclose(wxCloseEvent& e);
43 void refresh();
44 private:
45 bool closing;
46 uint64_t get_id();
47 std::map<int, uint64_t> smap;
48 wxListBox* subtitles;
49 wxButton* playbutton;
50 wxButton* deletebutton;
51 wxButton* exportpbutton;
52 wxButton* exportsbutton;
53 wxButton* importpbutton;
54 wxButton* changetsbutton;
55 wxButton* changegainbutton;
56 wxButton* loadbutton;
57 wxButton* unloadbutton;
58 wxButton* refreshbutton;
59 wxButton* closebutton;
60 struct dispatch_target<> corechange;
61 struct dispatch_target<> vstreamchange;
64 wxeditor_voicesub::wxeditor_voicesub(wxWindow* parent)
65 : wxDialog(parent, wxID_ANY, wxT("lsnes: Edit commentary track"), wxDefaultPosition, wxSize(-1, -1))
67 closing = false;
68 Centre();
69 wxFlexGridSizer* top_s = new wxFlexGridSizer(6, 1, 0, 0);
70 SetSizer(top_s);
72 top_s->Add(subtitles = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(300, 400), 0, NULL,
73 wxLB_SINGLE), 1, wxGROW);
75 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
76 pbutton_s->Add(new wxStaticText(this, wxID_ANY, wxT("Stream")), 0, wxGROW);
77 pbutton_s->Add(playbutton = new wxButton(this, wxID_ANY, wxT("Play")), 0, wxGROW);
78 pbutton_s->Add(deletebutton = new wxButton(this, wxID_ANY, wxT("Delete")), 0, wxGROW);
79 top_s->Add(pbutton_s, 1, wxGROW);
80 pbutton_s->SetSizeHints(this);
82 pbutton_s = new wxBoxSizer(wxHORIZONTAL);
83 pbutton_s->Add(new wxStaticText(this, wxID_ANY, wxT("I/O")), 0, wxGROW);
84 pbutton_s->Add(importpbutton = new wxButton(this, wxID_ANY, wxT("Import")), 0, wxGROW);
85 pbutton_s->Add(exportpbutton = new wxButton(this, wxID_ANY, wxT("Export")), 0, wxGROW);
86 pbutton_s->Add(exportsbutton = new wxButton(this, wxID_ANY, wxT("Export all")), 0, wxGROW);
87 top_s->Add(pbutton_s, 1, wxGROW);
88 pbutton_s->SetSizeHints(this);
90 pbutton_s = new wxBoxSizer(wxHORIZONTAL);
91 pbutton_s->Add(new wxStaticText(this, wxID_ANY, wxT("Misc.")), 0, wxGROW);
92 pbutton_s->Add(changetsbutton = new wxButton(this, wxID_ANY, wxT("Change time")), 0, wxGROW);
93 pbutton_s->Add(changegainbutton = new wxButton(this, wxID_ANY, wxT("Change gain")), 0, wxGROW);
94 top_s->Add(pbutton_s, 1, wxGROW);
95 pbutton_s->SetSizeHints(this);
97 pbutton_s = new wxBoxSizer(wxHORIZONTAL);
98 pbutton_s->Add(new wxStaticText(this, wxID_ANY, wxT("Collection")), 0, wxGROW);
99 pbutton_s->Add(loadbutton = new wxButton(this, wxID_ANY, wxT("Load")), 0, wxGROW);
100 pbutton_s->Add(unloadbutton = new wxButton(this, wxID_ANY, wxT("Unload")), 0, wxGROW);
101 top_s->Add(pbutton_s, 1, wxGROW);
102 pbutton_s->SetSizeHints(this);
104 pbutton_s = new wxBoxSizer(wxHORIZONTAL);
105 pbutton_s->AddStretchSpacer();
106 pbutton_s->Add(refreshbutton = new wxButton(this, wxID_OK, wxT("Refresh")), 0, wxGROW);
107 pbutton_s->Add(closebutton = new wxButton(this, wxID_OK, wxT("Close")), 0, wxGROW);
108 top_s->Add(pbutton_s, 1, wxGROW);
109 pbutton_s->SetSizeHints(this);
111 playbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
112 wxCommandEventHandler(wxeditor_voicesub::on_play), NULL, this);
113 deletebutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
114 wxCommandEventHandler(wxeditor_voicesub::on_delete), NULL, this);
115 exportpbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
116 wxCommandEventHandler(wxeditor_voicesub::on_export), NULL, this);
117 exportsbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
118 wxCommandEventHandler(wxeditor_voicesub::on_export_s), NULL, this);
119 importpbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
120 wxCommandEventHandler(wxeditor_voicesub::on_import), NULL, this);
121 changetsbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
122 wxCommandEventHandler(wxeditor_voicesub::on_change_ts), NULL, this);
123 changegainbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
124 wxCommandEventHandler(wxeditor_voicesub::on_change_gain), NULL, this);
125 loadbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
126 wxCommandEventHandler(wxeditor_voicesub::on_load), NULL, this);
127 unloadbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
128 wxCommandEventHandler(wxeditor_voicesub::on_unload), NULL, this);
129 refreshbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
130 wxCommandEventHandler(wxeditor_voicesub::on_refresh), NULL, this);
131 closebutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
132 wxCommandEventHandler(wxeditor_voicesub::on_close), NULL, this);
133 subtitles->Connect(wxEVT_COMMAND_LISTBOX_SELECTED,
134 wxCommandEventHandler(wxeditor_voicesub::on_select), NULL, this);
135 Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(wxeditor_voicesub::on_wclose));
137 top_s->SetSizeHints(this);
138 Fit();
139 vstreamchange.set(notify_voice_stream_change, [this]() { runuifun([this]() -> void { this->refresh(); }); });
140 corechange.set(notify_core_change, [this]() { runuifun([this]() -> void { this->refresh(); }); });
141 refresh();
144 wxeditor_voicesub::~wxeditor_voicesub() throw()
148 void wxeditor_voicesub::on_select(wxCommandEvent& e)
150 if(closing)
151 return;
152 uint64_t id = get_id();
153 bool valid = (id != NOTHING);
154 playbutton->Enable(valid);
155 deletebutton->Enable(valid);
156 exportpbutton->Enable(valid);
157 changetsbutton->Enable(valid);
158 changegainbutton->Enable(valid);
161 void wxeditor_voicesub::on_play(wxCommandEvent& e)
163 uint64_t id = get_id();
164 if(id == NOTHING)
165 return;
166 try {
167 voicesub_play_stream(id);
168 } catch(std::exception& e) {
169 show_message_ok(this, "Error playing", e.what(), wxICON_EXCLAMATION);
173 void wxeditor_voicesub::on_delete(wxCommandEvent& e)
175 uint64_t id = get_id();
176 if(id == NOTHING)
177 return;
178 try {
179 voicesub_delete_stream(id);
180 } catch(std::exception& e) {
181 show_message_ok(this, "Error deleting", e.what(), wxICON_EXCLAMATION);
185 namespace
187 class _opus_or_sox
189 public:
190 typedef std::pair<std::string, enum external_stream_format> returntype;
191 _opus_or_sox() {}
192 filedialog_input_params input(bool save) const
194 filedialog_input_params p;
195 p.types.push_back(filedialog_type_entry("Opus streams", "*.opus", "opus"));
196 p.types.push_back(filedialog_type_entry("SoX files", "*.sox", "sox"));
197 p.default_type = 0;
198 return p;
200 std::pair<std::string, enum external_stream_format> output(const filedialog_output_params& p,
201 bool save) const
203 return std::make_pair(p.path, (p.typechoice == 1) ? EXTFMT_SOX : EXTFMT_OGGOPUS);
205 } filetype_opus_sox;
209 void wxeditor_voicesub::on_export(wxCommandEvent& e)
211 uint64_t id = get_id();
212 if(id == NOTHING)
213 return;
214 try {
215 auto filename = choose_file_save(this, "Select file to epxort", project_otherpath(),
216 filetype_opus_sox);
217 voicesub_export_stream(id, filename.first, filename.second);
218 } catch(canceled_exception& e) {
219 } catch(std::exception& e) {
220 show_message_ok(this, "Error exporting", e.what(), wxICON_EXCLAMATION);
224 void wxeditor_voicesub::on_export_s(wxCommandEvent& e)
226 try {
227 std::string filename;
228 filename = choose_file_save(this, "Select file to export superstream", project_otherpath(),
229 filetype_sox);
230 voicesub_export_superstream(filename);
231 } catch(canceled_exception& e) {
232 } catch(std::exception& e) {
233 show_message_ok(this, "Error exporting superstream", e.what(), wxICON_EXCLAMATION);
237 void wxeditor_voicesub::on_import(wxCommandEvent& e)
239 try {
240 uint64_t ts;
241 ts = voicesub_parse_timebase(pick_text(this, "Enter timebase", "Enter position for newly "
242 "imported stream"));
243 auto filename = choose_file_save(this, "Select file to import", project_otherpath(),
244 filetype_opus_sox);
245 voicesub_import_stream(ts, filename.first, filename.second);
246 } catch(canceled_exception& e) {
247 } catch(std::exception& e) {
248 show_message_ok(this, "Error importing", e.what(), wxICON_EXCLAMATION);
252 void wxeditor_voicesub::on_change_ts(wxCommandEvent& e)
254 uint64_t id = get_id();
255 if(id == NOTHING)
256 return;
257 try {
258 uint64_t ts;
259 ts = voicesub_parse_timebase(pick_text(this, "Enter timebase", "Enter new position for "
260 "stream"));
261 voicesub_alter_timebase(id, ts);
262 } catch(canceled_exception& e) {
263 } catch(std::exception& e) {
264 show_message_ok(this, "Error changing timebase", e.what(), wxICON_EXCLAMATION);
268 void wxeditor_voicesub::on_change_gain(wxCommandEvent& e)
270 uint64_t id = get_id();
271 if(id == NOTHING)
272 return;
273 try {
274 float gain;
275 std::string old = (stringfmt() << voicesub_get_gain(id)).str();
276 gain = parse_value<float>(pick_text(this, "Enter gain", "Enter new gain (dB) for stream", old));
277 voicesub_set_gain(id, gain);
278 } catch(canceled_exception& e) {
279 } catch(std::exception& e) {
280 show_message_ok(this, "Error changing gain", e.what(), wxICON_EXCLAMATION);
284 void wxeditor_voicesub::on_load(wxCommandEvent& e)
286 if(project_get() != NULL)
287 return;
288 try {
289 std::string filename;
290 try {
291 //Use "." here because there can't be active project.
292 filename = choose_file_load(this, "Select collection to load", ".", filetype_commentary);
293 } catch(...) {
294 return;
296 voicesub_load_collection(filename);
297 } catch(canceled_exception& e) {
298 } catch(std::exception& e) {
299 show_message_ok(this, "Error loading collection", e.what(), wxICON_EXCLAMATION);
303 void wxeditor_voicesub::on_unload(wxCommandEvent& e)
305 if(project_get() != NULL)
306 return;
307 voicesub_unload_collection();
310 void wxeditor_voicesub::on_refresh(wxCommandEvent& e)
312 refresh();
315 void wxeditor_voicesub::on_close(wxCommandEvent& e)
317 Destroy();
318 voicesub_open = false;
321 void wxeditor_voicesub::refresh()
323 if(closing)
324 return;
325 bool cflag = voicesub_collection_loaded();
326 bool pflag = (project_get() != NULL);
327 unloadbutton->Enable(cflag && !pflag);
328 loadbutton->Enable(!pflag);
329 exportsbutton->Enable(cflag);
330 importpbutton->Enable(cflag);
331 int sel = subtitles->GetSelection();
332 subtitles->Clear();
333 smap.clear();
334 int next = 0;
335 for(auto i : voicesub_get_stream_info()) {
336 smap[next++] = i.id;
337 std::ostringstream tmp;
338 tmp << "#" << i.id << " " << voicesub_ts_seconds(i.length) << "s@" << voicesub_ts_seconds(i.base)
339 << "s";
340 float gain = voicesub_get_gain(i.id);
341 if(gain < -1e-5 || gain > 1e-5)
342 tmp << " (gain " << gain << "dB)";
343 std::string text = tmp.str();
344 subtitles->Append(towxstring(text));
346 if(sel != wxNOT_FOUND && sel < (ssize_t)subtitles->GetCount())
347 subtitles->SetSelection(sel);
348 else if(subtitles->GetCount())
349 subtitles->SetSelection(0);
350 wxCommandEvent e;
351 on_select(e);
354 uint64_t wxeditor_voicesub::get_id()
356 int id = subtitles->GetSelection();
357 return smap.count(id) ? smap[id] : NOTHING;
360 void wxeditor_voicesub::on_wclose(wxCloseEvent& e)
362 bool wasc = closing;
363 closing = true;
364 if(!wasc)
365 Destroy();
366 voicesub_open = false;
369 bool wxeditor_voicesub::ShouldPreventAppExit() const { return false; }
371 void show_wxeditor_voicesub(wxWindow* parent)
373 if(voicesub_open)
374 return;
375 wxeditor_voicesub* v = new wxeditor_voicesub(parent);
376 v->Show();
377 voicesub_open = true;