7fd73c0cf1f723896826c77fec3720c5c404d4e8 from master; tidy audio analysis dialogue...
[dcpomatic.git] / src / wx / audio_panel.cc
1 /*
2     Copyright (C) 2012-2015 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 "lib/config.h"
21 #include "lib/ffmpeg_content.h"
22 #include "lib/ffmpeg_audio_stream.h"
23 #include "lib/audio_processor.h"
24 #include "lib/cinema_sound_processor.h"
25 #include "audio_dialog.h"
26 #include "audio_panel.h"
27 #include "audio_mapping_view.h"
28 #include "wx_util.h"
29 #include "gain_calculator_dialog.h"
30 #include "content_panel.h"
31 #include <wx/spinctrl.h>
32 #include <boost/lexical_cast.hpp>
33 #include <boost/foreach.hpp>
34
35 using std::vector;
36 using std::cout;
37 using std::string;
38 using std::list;
39 using std::pair;
40 using boost::dynamic_pointer_cast;
41 using boost::lexical_cast;
42 using boost::shared_ptr;
43
44 AudioPanel::AudioPanel (ContentPanel* p)
45         : ContentSubPanel (p, _("Audio"))
46         , _audio_dialog (0)
47 {
48         wxGridBagSizer* grid = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
49         _sizer->Add (grid, 0, wxALL, 8);
50
51         int r = 0;
52
53         _show = new wxButton (this, wxID_ANY, _("Show Audio..."));
54         grid->Add (_show, wxGBPosition (r, 0));
55         ++r;
56
57         add_label_to_grid_bag_sizer (grid, this, _("Gain"), true, wxGBPosition (r, 0));
58         _gain = new ContentSpinCtrlDouble<AudioContent> (
59                 this,
60                 new wxSpinCtrlDouble (this),
61                 AudioContentProperty::AUDIO_GAIN,
62                 boost::mem_fn (&AudioContent::audio_gain),
63                 boost::mem_fn (&AudioContent::set_audio_gain)
64                 );
65         
66         _gain->add (grid, wxGBPosition (r, 1));
67         add_label_to_grid_bag_sizer (grid, this, _("dB"), false, wxGBPosition (r, 2));
68         _gain_calculate_button = new wxButton (this, wxID_ANY, _("Calculate..."));
69         grid->Add (_gain_calculate_button, wxGBPosition (r, 3));
70         ++r;
71
72         add_label_to_grid_bag_sizer (grid, this, _("Delay"), true, wxGBPosition (r, 0));
73         _delay = new ContentSpinCtrl<AudioContent> (
74                 this,
75                 new wxSpinCtrl (this),
76                 AudioContentProperty::AUDIO_DELAY,
77                 boost::mem_fn (&AudioContent::audio_delay),
78                 boost::mem_fn (&AudioContent::set_audio_delay)
79                 );
80         
81         _delay->add (grid, wxGBPosition (r, 1));
82         /// TRANSLATORS: this is an abbreviation for milliseconds, the unit of time
83         add_label_to_grid_bag_sizer (grid, this, _("ms"), false, wxGBPosition (r, 2));
84         ++r;
85
86         add_label_to_grid_bag_sizer (grid, this, _("Stream"), true, wxGBPosition (r, 0));
87         _stream = new wxChoice (this, wxID_ANY);
88         grid->Add (_stream, wxGBPosition (r, 1), wxGBSpan (1, 3), wxEXPAND);
89         ++r;
90
91         add_label_to_grid_bag_sizer (grid, this, _("Process with"), true, wxGBPosition (r, 0));
92         _processor = new wxChoice (this, wxID_ANY);
93         setup_processors ();
94         grid->Add (_processor, wxGBPosition (r, 1), wxGBSpan (1, 3), wxEXPAND);
95         ++r;
96         
97         _mapping = new AudioMappingView (this);
98         _sizer->Add (_mapping, 1, wxEXPAND | wxALL, 6);
99         ++r;
100
101         _description = new wxStaticText (this, wxID_ANY, wxT (" \n"), wxDefaultPosition, wxDefaultSize);
102         _sizer->Add (_description, 0, wxALL, 12);
103         wxFont font = _description->GetFont();
104         font.SetStyle (wxFONTSTYLE_ITALIC);
105         font.SetPointSize (font.GetPointSize() - 1);
106         _description->SetFont (font);
107         ++r;
108
109         _gain->wrapped()->SetRange (-60, 60);
110         _gain->wrapped()->SetDigits (1);
111         _gain->wrapped()->SetIncrement (0.5);
112         _delay->wrapped()->SetRange (-1000, 1000);
113
114         _stream->Bind                (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&AudioPanel::stream_changed, this));
115         _show->Bind                  (wxEVT_COMMAND_BUTTON_CLICKED,  boost::bind (&AudioPanel::show_clicked, this));
116         _gain_calculate_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED,  boost::bind (&AudioPanel::gain_calculate_button_clicked, this));
117         _processor->Bind             (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&AudioPanel::processor_changed, this));
118
119         _mapping_connection = _mapping->Changed.connect (boost::bind (&AudioPanel::mapping_changed, this, _1));
120 }
121
122
123 void
124 AudioPanel::film_changed (Film::Property property)
125 {
126         switch (property) {
127         case Film::AUDIO_CHANNELS:
128                 _mapping->set_channels (_parent->film()->audio_channels ());
129                 _sizer->Layout ();
130                 break;
131         case Film::VIDEO_FRAME_RATE:
132                 setup_description ();
133                 break;
134         default:
135                 break;
136         }
137 }
138
139 void
140 AudioPanel::film_content_changed (int property)
141 {
142         AudioContentList ac = _parent->selected_audio ();
143         shared_ptr<AudioContent> acs;
144         shared_ptr<FFmpegContent> fcs;
145         if (ac.size() == 1) {
146                 acs = ac.front ();
147                 fcs = dynamic_pointer_cast<FFmpegContent> (acs);
148         }
149         
150         if (property == AudioContentProperty::AUDIO_MAPPING) {
151                 _mapping->set (acs ? acs->audio_mapping () : AudioMapping ());
152                 _sizer->Layout ();
153         } else if (property == AudioContentProperty::AUDIO_FRAME_RATE) {
154                 setup_description ();
155         } else if (property == FFmpegContentProperty::AUDIO_STREAM) {
156                 _mapping->set (acs ? acs->audio_mapping () : AudioMapping ());
157                 _sizer->Layout ();
158         } else if (property == FFmpegContentProperty::AUDIO_STREAMS) {
159                 if (fcs) {
160                         vector<pair<string, string> > data;
161                         BOOST_FOREACH (shared_ptr<FFmpegAudioStream> i, fcs->audio_streams ()) {
162                                 data.push_back (make_pair (i->name, i->identifier ()));
163                         }
164                         checked_set (_stream, data);
165                         
166                         if (fcs->audio_stream()) {
167                                 checked_set (_stream, fcs->audio_stream()->identifier ());
168                         }
169                 } else {
170                         _stream->Clear ();
171                 }
172         } else if (property == AudioContentProperty::AUDIO_PROCESSOR) {
173                 if (acs) {
174                         checked_set (_processor, acs->audio_processor() ? acs->audio_processor()->id() : N_("none"));
175                 } else {
176                         checked_set (_processor, N_("none"));
177                 }
178         }
179 }
180
181 void
182 AudioPanel::gain_calculate_button_clicked ()
183 {
184         GainCalculatorDialog* d = new GainCalculatorDialog (this);
185         int const r = d->ShowModal ();
186
187         if (r == wxID_CANCEL || d->wanted_fader() == 0 || d->actual_fader() == 0) {
188                 d->Destroy ();
189                 return;
190         }
191         
192         _gain->wrapped()->SetValue (
193                 Config::instance()->cinema_sound_processor()->db_for_fader_change (
194                         d->wanted_fader (),
195                         d->actual_fader ()
196                         )
197                 );
198
199         /* This appears to be necessary, as the change is not signalled,
200            I think.
201         */
202         _gain->view_changed ();
203         
204         d->Destroy ();
205 }
206
207 void
208 AudioPanel::show_clicked ()
209 {
210         if (_audio_dialog) {
211                 _audio_dialog->Destroy ();
212                 _audio_dialog = 0;
213         }
214
215         AudioContentList ac = _parent->selected_audio ();
216         if (ac.size() != 1) {
217                 return;
218         }
219         
220         _audio_dialog = new AudioDialog (this, _parent->film ());
221         _audio_dialog->Show ();
222         _audio_dialog->set_content (ac.front ());
223 }
224
225 void
226 AudioPanel::stream_changed ()
227 {
228         FFmpegContentList fc = _parent->selected_ffmpeg ();
229         if (fc.size() != 1) {
230                 return;
231         }
232
233         shared_ptr<FFmpegContent> fcs = fc.front ();
234         
235         if (_stream->GetSelection() == -1) {
236                 return;
237         }
238         
239         vector<shared_ptr<FFmpegAudioStream> > a = fcs->audio_streams ();
240         vector<shared_ptr<FFmpegAudioStream> >::iterator i = a.begin ();
241         string const s = string_client_data (_stream->GetClientObject (_stream->GetSelection ()));
242         while (i != a.end() && (*i)->identifier () != s) {
243                 ++i;
244         }
245
246         if (i != a.end ()) {
247                 fcs->set_audio_stream (*i);
248         }
249 }
250
251 void
252 AudioPanel::processor_changed ()
253 {
254         string const s = string_client_data (_processor->GetClientObject (_processor->GetSelection ()));
255         AudioProcessor const * p = 0;
256         if (s != wx_to_std (N_("none"))) {
257                 p = AudioProcessor::from_id (s);
258         }
259                 
260         AudioContentList c = _parent->selected_audio ();
261         for (AudioContentList::const_iterator i = c.begin(); i != c.end(); ++i) {
262                 (*i)->set_audio_processor (p);
263         }
264 }
265
266 void
267 AudioPanel::setup_description ()
268 {
269         AudioContentList ac = _parent->selected_audio ();
270         if (ac.size () != 1) {
271                 checked_set (_description, wxT (""));
272                 return;
273         }
274
275         checked_set (_description, ac.front()->processing_description ());
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 (AudioContentProperty::AUDIO_FRAME_RATE);
310         film_content_changed (FFmpegContentProperty::AUDIO_STREAM);
311         film_content_changed (FFmpegContentProperty::AUDIO_STREAMS);
312 }
313
314 void
315 AudioPanel::setup_processors ()
316 {
317         AudioContentList sel = _parent->selected_audio ();
318
319         _processor->Clear ();
320         list<AudioProcessor const *> ap = AudioProcessor::all ();
321         _processor->Append (_("None"), new wxStringClientData (N_("none")));
322         for (list<AudioProcessor const *>::const_iterator i = ap.begin(); i != ap.end(); ++i) {
323
324                 AudioContentList::const_iterator j = sel.begin();
325                 while (j != sel.end() && (*i)->in_channels().includes ((*j)->audio_channels ())) {
326                         ++j;
327                 }
328
329                 if (j == sel.end ()) {
330                         _processor->Append (std_to_wx ((*i)->name ()), new wxStringClientData (std_to_wx ((*i)->id ())));
331                 }
332         }
333 }