Merge master.
[dcpomatic.git] / src / wx / audio_panel.cc
1 /*
2     Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <boost/lexical_cast.hpp>
21 #include <wx/spinctrl.h>
22 #include "lib/config.h"
23 #include "lib/ffmpeg_content.h"
24 #include "lib/ffmpeg_audio_stream.h"
25 #include "lib/audio_processor.h"
26 #include "lib/cinema_sound_processor.h"
27 #include "audio_dialog.h"
28 #include "audio_panel.h"
29 #include "audio_mapping_view.h"
30 #include "wx_util.h"
31 #include "gain_calculator_dialog.h"
32 #include "film_editor.h"
33
34 using std::vector;
35 using std::cout;
36 using std::string;
37 using std::list;
38 using boost::dynamic_pointer_cast;
39 using boost::lexical_cast;
40 using boost::shared_ptr;
41
42 AudioPanel::AudioPanel (ContentPanel* p)
43         : ContentSubPanel (p, _("Audio"))
44         , _audio_dialog (0)
45 {
46         wxGridBagSizer* grid = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
47         _sizer->Add (grid, 0, wxALL, 8);
48
49         int r = 0;
50
51         _show = new wxButton (this, wxID_ANY, _("Show Audio..."));
52         grid->Add (_show, wxGBPosition (r, 0));
53         ++r;
54
55         add_label_to_grid_bag_sizer (grid, this, _("Gain"), true, wxGBPosition (r, 0));
56         _gain = new ContentSpinCtrlDouble<AudioContent> (
57                 this,
58                 new wxSpinCtrlDouble (this),
59                 AudioContentProperty::AUDIO_GAIN,
60                 boost::mem_fn (&AudioContent::audio_gain),
61                 boost::mem_fn (&AudioContent::set_audio_gain)
62                 );
63         
64         _gain->add (grid, wxGBPosition (r, 1));
65         add_label_to_grid_bag_sizer (grid, this, _("dB"), false, wxGBPosition (r, 2));
66         _gain_calculate_button = new wxButton (this, wxID_ANY, _("Calculate..."));
67         grid->Add (_gain_calculate_button, wxGBPosition (r, 3));
68         ++r;
69
70         add_label_to_grid_bag_sizer (grid, this, _("Delay"), true, wxGBPosition (r, 0));
71         _delay = new ContentSpinCtrl<AudioContent> (
72                 this,
73                 new wxSpinCtrl (this),
74                 AudioContentProperty::AUDIO_DELAY,
75                 boost::mem_fn (&AudioContent::audio_delay),
76                 boost::mem_fn (&AudioContent::set_audio_delay)
77                 );
78         
79         _delay->add (grid, wxGBPosition (r, 1));
80         /// TRANSLATORS: this is an abbreviation for milliseconds, the unit of time
81         add_label_to_grid_bag_sizer (grid, this, _("ms"), false, wxGBPosition (r, 2));
82         ++r;
83
84         add_label_to_grid_bag_sizer (grid, this, _("Stream"), true, wxGBPosition (r, 0));
85         _stream = new wxChoice (this, wxID_ANY);
86         grid->Add (_stream, wxGBPosition (r, 1));
87         _description = add_label_to_grid_bag_sizer (grid, this, "", false, wxGBPosition (r, 2), wxGBSpan (1, 2));
88         ++r;
89
90         add_label_to_grid_bag_sizer (grid, this, _("Process with"), true, wxGBPosition (r, 0));
91         _processor = new wxChoice (this, wxID_ANY);
92         setup_processors ();
93         grid->Add (_processor, wxGBPosition (r, 1));
94         ++r;
95         
96         _mapping = new AudioMappingView (this);
97         _sizer->Add (_mapping, 1, wxEXPAND | wxALL, 6);
98
99         _gain->wrapped()->SetRange (-60, 60);
100         _gain->wrapped()->SetDigits (1);
101         _gain->wrapped()->SetIncrement (0.5);
102         _delay->wrapped()->SetRange (-1000, 1000);
103
104         _stream->Bind                (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&AudioPanel::stream_changed, this));
105         _show->Bind                  (wxEVT_COMMAND_BUTTON_CLICKED,  boost::bind (&AudioPanel::show_clicked, this));
106         _gain_calculate_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED,  boost::bind (&AudioPanel::gain_calculate_button_clicked, this));
107         _processor->Bind             (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&AudioPanel::processor_changed, this));
108
109         _mapping->Changed.connect (boost::bind (&AudioPanel::mapping_changed, this, _1));
110 }
111
112
113 void
114 AudioPanel::film_changed (Film::Property property)
115 {
116         switch (property) {
117         case Film::AUDIO_CHANNELS:
118                 _mapping->set_channels (_parent->film()->audio_channels ());
119                 _sizer->Layout ();
120                 break;
121         default:
122                 break;
123         }
124 }
125
126 void
127 AudioPanel::film_content_changed (int property)
128 {
129         AudioContentList ac = _parent->selected_audio ();
130         shared_ptr<AudioContent> acs;
131         shared_ptr<FFmpegContent> fcs;
132         if (ac.size() == 1) {
133                 acs = ac.front ();
134                 fcs = dynamic_pointer_cast<FFmpegContent> (acs);
135         }
136         
137         if (property == AudioContentProperty::AUDIO_MAPPING) {
138                 _mapping->set (acs ? acs->audio_mapping () : AudioMapping ());
139                 _sizer->Layout ();
140         } else if (property == FFmpegContentProperty::AUDIO_STREAM) {
141                 setup_stream_description ();
142                 _mapping->set (acs ? acs->audio_mapping () : AudioMapping ());
143                 _sizer->Layout ();
144         } else if (property == FFmpegContentProperty::AUDIO_STREAMS) {
145                 _stream->Clear ();
146                 if (fcs) {
147                         vector<shared_ptr<FFmpegAudioStream> > a = fcs->audio_streams ();
148                         for (vector<shared_ptr<FFmpegAudioStream> >::iterator i = a.begin(); i != a.end(); ++i) {
149                                 _stream->Append (std_to_wx ((*i)->name), new wxStringClientData (std_to_wx ((*i)->identifier ())));
150                         }
151                         
152                         if (fcs->audio_stream()) {
153                                 checked_set (_stream, fcs->audio_stream()->identifier ());
154                                 setup_stream_description ();
155                         }
156                 }
157         } else if (property == AudioContentProperty::AUDIO_PROCESSOR) {
158                 if (acs) {
159                         checked_set (_processor, acs->audio_processor() ? acs->audio_processor()->id() : N_("none"));
160                 } else {
161                         checked_set (_processor, N_("none"));
162                 }
163         }
164 }
165
166 void
167 AudioPanel::gain_calculate_button_clicked ()
168 {
169         GainCalculatorDialog* d = new GainCalculatorDialog (this);
170         d->ShowModal ();
171
172         if (d->wanted_fader() == 0 || d->actual_fader() == 0) {
173                 d->Destroy ();
174                 return;
175         }
176         
177         _gain->wrapped()->SetValue (
178                 Config::instance()->cinema_sound_processor()->db_for_fader_change (
179                         d->wanted_fader (),
180                         d->actual_fader ()
181                         )
182                 );
183
184         /* This appears to be necessary, as the change is not signalled,
185            I think.
186         */
187         _gain->view_changed ();
188         
189         d->Destroy ();
190 }
191
192 void
193 AudioPanel::show_clicked ()
194 {
195         if (_audio_dialog) {
196                 _audio_dialog->Destroy ();
197                 _audio_dialog = 0;
198         }
199
200         AudioContentList ac = _parent->selected_audio ();
201         if (ac.size() != 1) {
202                 return;
203         }
204         
205         _audio_dialog = new AudioDialog (this);
206         _audio_dialog->Show ();
207         _audio_dialog->set_content (ac.front ());
208 }
209
210 void
211 AudioPanel::stream_changed ()
212 {
213         FFmpegContentList fc = _parent->selected_ffmpeg ();
214         if (fc.size() != 1) {
215                 return;
216         }
217
218         shared_ptr<FFmpegContent> fcs = fc.front ();
219         
220         if (_stream->GetSelection() == -1) {
221                 return;
222         }
223         
224         vector<shared_ptr<FFmpegAudioStream> > a = fcs->audio_streams ();
225         vector<shared_ptr<FFmpegAudioStream> >::iterator i = a.begin ();
226         string const s = string_client_data (_stream->GetClientObject (_stream->GetSelection ()));
227         while (i != a.end() && (*i)->identifier () != s) {
228                 ++i;
229         }
230
231         if (i != a.end ()) {
232                 fcs->set_audio_stream (*i);
233         }
234
235         setup_stream_description ();
236 }
237
238 void
239 AudioPanel::setup_stream_description ()
240 {
241         FFmpegContentList fc = _parent->selected_ffmpeg ();
242         if (fc.size() != 1) {
243                 _description->SetLabel ("");
244                 return;
245         }
246
247         shared_ptr<FFmpegContent> fcs = fc.front ();
248
249         if (!fcs->audio_stream ()) {
250                 _description->SetLabel (wxT (""));
251         } else {
252                 wxString s;
253                 if (fcs->audio_channels() == 1) {
254                         s << _("1 channel");
255                 } else {
256                         s << fcs->audio_channels() << wxT (" ") << _("channels");
257                 }
258                 s << wxT (", ") << fcs->audio_frame_rate() << _("Hz");
259                 _description->SetLabel (s);
260         }
261 }
262
263 void
264 AudioPanel::processor_changed ()
265 {
266         string const s = string_client_data (_processor->GetClientObject (_processor->GetSelection ()));
267         AudioProcessor const * p = 0;
268         if (s != wx_to_std (N_("none"))) {
269                 p = AudioProcessor::from_id (s);
270         }
271                 
272         AudioContentList c = _parent->selected_audio ();
273         for (AudioContentList::const_iterator i = c.begin(); i != c.end(); ++i) {
274                 (*i)->set_audio_processor (p);
275         }
276 }
277
278 void
279 AudioPanel::mapping_changed (AudioMapping m)
280 {
281         AudioContentList c = _parent->selected_audio ();
282         if (c.size() == 1) {
283                 c.front()->set_audio_mapping (m);
284         }
285 }
286
287 void
288 AudioPanel::content_selection_changed ()
289 {
290         AudioContentList sel = _parent->selected_audio ();
291
292         if (_audio_dialog && sel.size() == 1) {
293                 _audio_dialog->set_content (sel.front ());
294         }
295         
296         _gain->set_content (sel);
297         _delay->set_content (sel);
298
299         _gain_calculate_button->Enable (sel.size() == 1);
300         _show->Enable (sel.size() == 1);
301         _stream->Enable (sel.size() == 1);
302         _processor->Enable (!sel.empty());
303         _mapping->Enable (sel.size() == 1);
304
305         setup_processors ();
306
307         film_content_changed (AudioContentProperty::AUDIO_MAPPING);
308         film_content_changed (AudioContentProperty::AUDIO_PROCESSOR);
309         film_content_changed (FFmpegContentProperty::AUDIO_STREAM);
310         film_content_changed (FFmpegContentProperty::AUDIO_STREAMS);
311 }
312
313 void
314 AudioPanel::setup_processors ()
315 {
316         AudioContentList sel = _parent->selected_audio ();
317
318         _processor->Clear ();
319         list<AudioProcessor const *> ap = AudioProcessor::all ();
320         _processor->Append (_("None"), new wxStringClientData (N_("none")));
321         for (list<AudioProcessor const *>::const_iterator i = ap.begin(); i != ap.end(); ++i) {
322
323                 AudioContentList::const_iterator j = sel.begin();
324                 while (j != sel.end() && (*i)->in_channels().includes ((*j)->audio_channels ())) {
325                         ++j;
326                 }
327
328                 if (j == sel.end ()) {
329                         _processor->Append (std_to_wx ((*i)->name ()), new wxStringClientData (std_to_wx ((*i)->id ())));
330                 }
331         }
332 }