From fa61fc99549264810e17fcd35abffe9e8ddab5b2 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 3 Jun 2015 13:17:37 +0100 Subject: [PATCH] Various work on audio mapping. Fix everything up so that the audio mapping view in the audio panel reflects the processor (or lack of). --- src/lib/audio_content.cc | 29 ++++-- src/lib/audio_content.h | 1 + src/lib/audio_mapping.cc | 109 +++++++++---------- src/lib/audio_mapping.h | 36 +++---- src/lib/audio_processor.h | 26 ++++- src/lib/audio_stream.cc | 5 +- src/lib/ffmpeg_content.cc | 2 +- src/lib/film.cc | 60 ++++++++++- src/lib/film.h | 4 + src/lib/mid_side_decoder.cc | 30 +++++- src/lib/mid_side_decoder.h | 8 +- src/lib/player.cc | 6 +- src/lib/single_stream_audio_content.cc | 5 +- src/lib/upmixer_a.cc | 26 ++++- src/lib/upmixer_a.h | 4 +- src/wx/audio_mapping_view.cc | 138 ++++++++----------------- src/wx/audio_mapping_view.h | 27 +++-- src/wx/audio_panel.cc | 21 ++-- src/wx/dcp_panel.cc | 4 +- src/wx/timeline.cc | 2 +- test/audio_mapping_test.cc | 17 +-- test/stream_test.cc | 14 +-- 22 files changed, 329 insertions(+), 245 deletions(-) diff --git a/src/lib/audio_content.cc b/src/lib/audio_content.cc index 3ea31f673..b6749d20b 100644 --- a/src/lib/audio_content.cc +++ b/src/lib/audio_content.cc @@ -171,10 +171,10 @@ AudioContent::set_audio_mapping (AudioMapping mapping) { int c = 0; BOOST_FOREACH (AudioStreamPtr i, audio_streams ()) { - AudioMapping stream_mapping (i->channels ()); + AudioMapping stream_mapping (i->channels (), MAX_DCP_AUDIO_CHANNELS); for (int j = 0; j < i->channels(); ++j) { for (int k = 0; k < MAX_DCP_AUDIO_CHANNELS; ++k) { - stream_mapping.set (j, static_cast (k), mapping.get (c, static_cast (k))); + stream_mapping.set (j, k, mapping.get (c, k)); } ++c; } @@ -192,16 +192,15 @@ AudioContent::audio_mapping () const channels += i->channels (); } - AudioMapping merged (channels); + AudioMapping merged (channels, MAX_DCP_AUDIO_CHANNELS); int c = 0; int s = 0; BOOST_FOREACH (AudioStreamPtr i, audio_streams ()) { AudioMapping mapping = i->mapping (); - for (int j = 0; j < mapping.content_channels(); ++j) { - merged.set_name (c, String::compose ("%1:%2", s + 1, j + 1)); + for (int j = 0; j < mapping.input_channels(); ++j) { for (int k = 0; k < MAX_DCP_AUDIO_CHANNELS; ++k) { - merged.set (c, static_cast (k), mapping.get (j, static_cast (k))); + merged.set (c, k, mapping.get (j, k)); } ++c; } @@ -289,6 +288,7 @@ AudioContent::processing_description () const return ""; } +/** @return true if any stream in this content has a sampling rate of more than 48kHz */ bool AudioContent::has_rate_above_48k () const { @@ -300,3 +300,20 @@ AudioContent::has_rate_above_48k () const return false; } + +/** @return User-visible names of each of our audio channels */ +vector +AudioContent::audio_channel_names () const +{ + vector n; + + int t = 1; + BOOST_FOREACH (AudioStreamPtr i, audio_streams ()) { + for (int j = 0; j < i->channels(); ++j) { + n.push_back (String::compose ("%1:%2", t, j + 1)); + } + ++t; + } + + return n; +} diff --git a/src/lib/audio_content.h b/src/lib/audio_content.h index 79dba9fda..63ce3d0fa 100644 --- a/src/lib/audio_content.h +++ b/src/lib/audio_content.h @@ -65,6 +65,7 @@ public: boost::filesystem::path audio_analysis_path () const; int resampled_audio_frame_rate () const; bool has_rate_above_48k () const; + std::vector audio_channel_names () const; boost::signals2::connection analyse_audio (boost::function); diff --git a/src/lib/audio_mapping.cc b/src/lib/audio_mapping.cc index 65eb5fc96..fc3909a8a 100644 --- a/src/lib/audio_mapping.cc +++ b/src/lib/audio_mapping.cc @@ -35,63 +35,52 @@ using boost::shared_ptr; using boost::dynamic_pointer_cast; AudioMapping::AudioMapping () - : _content_channels (0) + : _input_channels (0) + , _output_channels (0) { } -/** Create an empty AudioMapping for a given channel count. - * @param channels Number of channels. +/** Create an empty AudioMapping. + * @param input_channels Number of input channels. + * @param output_channels Number of output channels. */ -AudioMapping::AudioMapping (int channels) +AudioMapping::AudioMapping (int input_channels, int output_channels) { - setup (channels); + setup (input_channels, output_channels); } void -AudioMapping::setup (int c) +AudioMapping::setup (int input_channels, int output_channels) { - _content_channels = c; + _input_channels = input_channels; + _output_channels = output_channels; - _gain.resize (_content_channels); - for (int i = 0; i < _content_channels; ++i) { - _gain[i].resize (MAX_DCP_AUDIO_CHANNELS); + _gain.resize (_input_channels); + for (int i = 0; i < _input_channels; ++i) { + _gain[i].resize (_output_channels); } - _name.resize (_content_channels); - make_zero (); } void AudioMapping::make_zero () { - for (int i = 0; i < _content_channels; ++i) { - for (int j = 0; j < MAX_DCP_AUDIO_CHANNELS; ++j) { + for (int i = 0; i < _input_channels; ++i) { + for (int j = 0; j < _output_channels; ++j) { _gain[i][j] = 0; } } } -void -AudioMapping::make_default () +AudioMapping::AudioMapping (cxml::ConstNodePtr node, int state_version) { - make_zero (); - - if (_content_channels == 1) { - /* Mono -> Centre */ - set (0, dcp::CENTRE, 1); + if (state_version < 32) { + setup (node->number_child ("ContentChannels"), MAX_DCP_AUDIO_CHANNELS); } else { - /* 1:1 mapping */ - for (int i = 0; i < min (_content_channels, MAX_DCP_AUDIO_CHANNELS); ++i) { - set (i, static_cast (i), 1); - } + setup (node->number_child ("InputChannels"), node->number_child ("OutputChannels")); } -} - -AudioMapping::AudioMapping (cxml::ConstNodePtr node, int state_version) -{ - setup (node->number_child ("ContentChannels")); if (state_version <= 5) { /* Old-style: on/off mapping */ @@ -102,38 +91,47 @@ AudioMapping::AudioMapping (cxml::ConstNodePtr node, int state_version) } else { list const c = node->node_children ("Gain"); for (list::const_iterator i = c.begin(); i != c.end(); ++i) { - set ( - (*i)->number_attribute ("Content"), - static_cast ((*i)->number_attribute ("DCP")), - raw_convert ((*i)->content ()) - ); + if (state_version < 32) { + set ( + (*i)->number_attribute ("Content"), + static_cast ((*i)->number_attribute ("DCP")), + raw_convert ((*i)->content ()) + ); + } else { + set ( + (*i)->number_attribute ("Input"), + (*i)->number_attribute ("Output"), + raw_convert ((*i)->content ()) + ); + } } } } void -AudioMapping::set (int c, dcp::Channel d, float g) +AudioMapping::set (int input_channel, int output_channel, float g) { - _gain[c][d] = g; + _gain[input_channel][output_channel] = g; } float -AudioMapping::get (int c, dcp::Channel d) const +AudioMapping::get (int input_channel, int output_channel) const { - return _gain[c][d]; + return _gain[input_channel][output_channel]; } void AudioMapping::as_xml (xmlpp::Node* node) const { - node->add_child ("ContentChannels")->add_child_text (raw_convert (_content_channels)); + node->add_child ("InputChannels")->add_child_text (raw_convert (_input_channels)); + node->add_child ("OutputChannels")->add_child_text (raw_convert (_output_channels)); - for (int c = 0; c < _content_channels; ++c) { - for (int d = 0; d < MAX_DCP_AUDIO_CHANNELS; ++d) { + for (int c = 0; c < _input_channels; ++c) { + for (int d = 0; d < _output_channels; ++d) { xmlpp::Element* t = node->add_child ("Gain"); - t->set_attribute ("Content", raw_convert (c)); - t->set_attribute ("DCP", raw_convert (d)); - t->add_child_text (raw_convert (get (c, static_cast (d)))); + t->set_attribute ("Input", raw_convert (c)); + t->set_attribute ("Output", raw_convert (d)); + t->add_child_text (raw_convert (get (c, d))); } } } @@ -145,9 +143,10 @@ string AudioMapping::digest () const { MD5Digester digester; - digester.add (_content_channels); - for (int i = 0; i < _content_channels; ++i) { - for (int j = 0; j < MAX_DCP_AUDIO_CHANNELS; ++j) { + digester.add (_input_channels); + digester.add (_output_channels); + for (int i = 0; i < _input_channels; ++i) { + for (int j = 0; j < _output_channels; ++j) { digester.add (_gain[i][j]); } } @@ -155,17 +154,17 @@ AudioMapping::digest () const return digester.get (); } -list -AudioMapping::mapped_dcp_channels () const +list +AudioMapping::mapped_output_channels () const { static float const minus_96_db = 0.000015849; - list mapped; + list mapped; for (vector >::const_iterator i = _gain.begin(); i != _gain.end(); ++i) { for (size_t j = 0; j < i->size(); ++j) { if (abs ((*i)[j]) > minus_96_db) { - mapped.push_back ((dcp::Channel) j); + mapped.push_back (j); } } } @@ -185,9 +184,3 @@ AudioMapping::unmap_all () } } } - -void -AudioMapping::set_name (int channel, string name) -{ - _name[channel] = name; -} diff --git a/src/lib/audio_mapping.h b/src/lib/audio_mapping.h index e37beaeb2..57169cc1e 100644 --- a/src/lib/audio_mapping.h +++ b/src/lib/audio_mapping.h @@ -38,49 +38,43 @@ namespace cxml { } /** @class AudioMapping. - * @brief A many-to-many mapping from some content channels to DCP channels. - * - * The number of content channels is set on construction and fixed, - * and then each of those content channels are mapped to each DCP channel - * by a linear gain. + * @brief A many-to-many mapping of audio channels. */ class AudioMapping { public: AudioMapping (); - AudioMapping (int channels); + AudioMapping (int input_channels, int output_channels); AudioMapping (cxml::ConstNodePtr, int); /* Default copy constructor is fine */ void as_xml (xmlpp::Node *) const; - void make_default (); + void make_zero (); - void set (int, dcp::Channel, float); - float get (int, dcp::Channel) const; + void set (int input_channel, int output_channel, float); + float get (int input_channel, int output_channel) const; - int content_channels () const { - return _content_channels; + int input_channels () const { + return _input_channels; } - void set_name (int channel, std::string name); - std::string name (int channel) const { - return _name[channel]; + int output_channels () const { + return _output_channels; } - + std::string digest () const; - std::list mapped_dcp_channels () const; + std::list mapped_output_channels () const; void unmap_all (); private: - void setup (int); - void make_zero (); - - int _content_channels; + void setup (int input_channels, int output_channels); + + int _input_channels; + int _output_channels; std::vector > _gain; - std::vector _name; }; #endif diff --git a/src/lib/audio_processor.h b/src/lib/audio_processor.h index 610e973a0..114756f91 100644 --- a/src/lib/audio_processor.h +++ b/src/lib/audio_processor.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2014 Carl Hetherington + Copyright (C) 2014-2015 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,6 +17,10 @@ */ +/** @file src/lib/audio_processor.h + * @brief AudioProcessor class. + */ + #ifndef DCPOMATIC_AUDIO_PROCESSOR_H #define DCPOMATIC_AUDIO_PROCESSOR_H @@ -24,21 +28,39 @@ #include #include #include +#include class AudioBuffers; +class AudioMapping; +/** @class AudioProcessor + * @brief A parent class for processors of audio data. + * + * These are used to process data before it goes into the DCP, for things like + * stereo -> 5.1 upmixing. + */ class AudioProcessor { public: virtual ~AudioProcessor () {} + /** @return User-visible (translated) name */ virtual std::string name () const = 0; + /** @return An internal identifier */ virtual std::string id () const = 0; + /** @return Number of input channels */ virtual ChannelCount in_channels () const = 0; - virtual int out_channels (int) const = 0; + /** @return Number of output channels */ + virtual int out_channels () const = 0; + /** @return A clone of this AudioProcessor for operation at the specified sampling rate */ virtual boost::shared_ptr clone (int sampling_rate) const = 0; + /** Process some data, returning the processed result */ virtual boost::shared_ptr run (boost::shared_ptr) = 0; virtual void flush () {} + /** Make the supplied audio mapping into a sensible default for this processor */ + virtual void make_audio_mapping_default (AudioMapping& mapping) const = 0; + /** @return the user-visible (translated) names of each of our inputs, in order */ + virtual std::vector input_names () const = 0; static std::list all (); static void setup_audio_processors (); diff --git a/src/lib/audio_stream.cc b/src/lib/audio_stream.cc index a4fa8bd9b..bf55e0255 100644 --- a/src/lib/audio_stream.cc +++ b/src/lib/audio_stream.cc @@ -19,11 +19,12 @@ #include "audio_stream.h" #include "audio_mapping.h" +#include "util.h" AudioStream::AudioStream (int frame_rate, int channels) : _frame_rate (frame_rate) { - _mapping = AudioMapping (channels); + _mapping = AudioMapping (channels, MAX_DCP_AUDIO_CHANNELS); } AudioStream::AudioStream (int frame_rate, AudioMapping mapping) @@ -51,5 +52,5 @@ int AudioStream::channels () const { boost::mutex::scoped_lock lm (_mutex); - return _mapping.content_channels (); + return _mapping.input_channels (); } diff --git a/src/lib/ffmpeg_content.cc b/src/lib/ffmpeg_content.cc index 1278c4c10..9dc4afab9 100644 --- a/src/lib/ffmpeg_content.cc +++ b/src/lib/ffmpeg_content.cc @@ -173,7 +173,7 @@ FFmpegContent::examine (shared_ptr job) if (!_audio_streams.empty ()) { AudioMapping m = _audio_streams.front()->mapping (); - m.make_default (); + film->make_audio_mapping_default (m); _audio_streams.front()->set_mapping (m); } diff --git a/src/lib/film.cc b/src/lib/film.cc index 0e55ec1d3..90bfad6a2 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -595,11 +595,11 @@ Film::isdcf_name (bool if_created_now) const /* Find all mapped channels */ - list mapped; + list mapped; for (ContentList::const_iterator i = cl.begin(); i != cl.end(); ++i) { shared_ptr ac = dynamic_pointer_cast (*i); if (ac) { - list c = ac->audio_mapping().mapped_dcp_channels (); + list c = ac->audio_mapping().mapped_output_channels (); copy (c.begin(), c.end(), back_inserter (mapped)); } } @@ -611,13 +611,13 @@ Film::isdcf_name (bool if_created_now) const int non_lfe = 0; int lfe = 0; - for (list::const_iterator i = mapped.begin(); i != mapped.end(); ++i) { - if (static_cast (*i) >= audio_channels()) { + for (list::const_iterator i = mapped.begin(); i != mapped.end(); ++i) { + if (*i >= audio_channels()) { /* This channel is mapped but is not included in the DCP */ continue; } - if ((*i) == dcp::LFE) { + if (static_cast (*i) == dcp::LFE) { ++lfe; } else { ++non_lfe; @@ -1160,3 +1160,53 @@ Film::subtitle_language () const return all; } + +/** Change the gains of the supplied AudioMapping to make it a default + * for this film. The defaults are guessed based on what processor (if any) + * is in use and the number of input channels. + */ +void +Film::make_audio_mapping_default (AudioMapping& mapping) const +{ + if (audio_processor ()) { + audio_processor()->make_audio_mapping_default (mapping); + } else { + mapping.make_zero (); + if (mapping.input_channels() == 1) { + /* Mono -> Centre */ + mapping.set (0, static_cast (dcp::CENTRE), 1); + } else { + /* 1:1 mapping */ + for (int i = 0; i < min (mapping.input_channels(), mapping.output_channels()); ++i) { + mapping.set (i, i, 1); + } + } + } +} + +/** @return The names of the channels that audio contents' outputs are passed into; + * this is either the DCP or a AudioProcessor. + */ +vector +Film::audio_output_names () const +{ + if (audio_processor ()) { + return audio_processor()->input_names (); + } + + vector n; + n.push_back (_("L")); + n.push_back (_("R")); + n.push_back (_("C")); + n.push_back (_("Lfe")); + n.push_back (_("Ls")); + n.push_back (_("Rs")); + n.push_back (_("HI")); + n.push_back (_("VI")); + n.push_back (_("Lc")); + n.push_back (_("Rc")); + n.push_back (_("BsL")); + n.push_back (_("BsR")); + + return vector (n.begin(), n.begin() + audio_channels ()); +} diff --git a/src/lib/film.h b/src/lib/film.h index 8d7d2e0fb..6008160cd 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -48,6 +48,7 @@ class Playlist; class AudioContent; class Screen; class AudioProcessor; +class AudioMapping; struct isdcf_name_test; /** @class Film @@ -137,6 +138,9 @@ public: std::string subtitle_language () const; + void make_audio_mapping_default (AudioMapping & mapping) const; + std::vector audio_output_names () const; + /** Identifiers for the parts of our state; used for signalling changes. */ diff --git a/src/lib/mid_side_decoder.cc b/src/lib/mid_side_decoder.cc index be82f6754..fe6282261 100644 --- a/src/lib/mid_side_decoder.cc +++ b/src/lib/mid_side_decoder.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2014 Carl Hetherington + Copyright (C) 2014-2015 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,10 +19,13 @@ #include "mid_side_decoder.h" #include "audio_buffers.h" +#include "audio_mapping.h" #include "i18n.h" using std::string; +using std::min; +using std::vector; using boost::shared_ptr; string @@ -44,7 +47,7 @@ MidSideDecoder::in_channels () const } int -MidSideDecoder::out_channels (int) const +MidSideDecoder::out_channels () const { return 3; } @@ -70,3 +73,26 @@ MidSideDecoder::run (shared_ptr in) return out; } + +void +MidSideDecoder::make_audio_mapping_default (AudioMapping& mapping) const +{ + /* Just map the first two input channels to our M/S */ + mapping.make_zero (); + for (int i = 0; i < min (2, mapping.input_channels()); ++i) { + mapping.set (i, i, 1); + } +} + +vector +MidSideDecoder::input_names () const +{ + vector n; + + /// TRANSLATORS: this is the name of the `mid' channel for mid-side decoding + n.push_back (_("Mid")); + /// TRANSLATORS: this is the name of the `side' channel for mid-side decoding + n.push_back (_("Side")); + + return n; +} diff --git a/src/lib/mid_side_decoder.h b/src/lib/mid_side_decoder.h index dac6cb7d9..197c7b33b 100644 --- a/src/lib/mid_side_decoder.h +++ b/src/lib/mid_side_decoder.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2014 Carl Hetherington + Copyright (C) 2014-2015 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,9 +25,9 @@ public: std::string name () const; std::string id () const; ChannelCount in_channels () const; - int out_channels (int) const; + int out_channels () const; boost::shared_ptr clone (int) const; boost::shared_ptr run (boost::shared_ptr); + void make_audio_mapping_default (AudioMapping& mapping) const; + std::vector input_names () const; }; - - diff --git a/src/lib/player.cc b/src/lib/player.cc index 1a55a8472..ac5a70570 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -455,14 +455,14 @@ Player::get_audio (DCPTime time, DCPTime length, bool accurate) shared_ptr dcp_mapped (new AudioBuffers (_film->audio_channels(), all.audio->frames())); dcp_mapped->make_silent (); AudioMapping map = j->mapping (); - for (int i = 0; i < map.content_channels(); ++i) { + for (int i = 0; i < map.input_channels(); ++i) { for (int j = 0; j < _film->audio_channels(); ++j) { - if (map.get (i, static_cast (j)) > 0) { + if (map.get (i, j) > 0) { dcp_mapped->accumulate_channel ( all.audio.get(), i, j, - map.get (i, static_cast (j)) + map.get (i, j) ); } } diff --git a/src/lib/single_stream_audio_content.cc b/src/lib/single_stream_audio_content.cc index a38ef0e1e..f978fa423 100644 --- a/src/lib/single_stream_audio_content.cc +++ b/src/lib/single_stream_audio_content.cc @@ -60,11 +60,14 @@ SingleStreamAudioContent::as_xml (xmlpp::Node* node) const void SingleStreamAudioContent::take_from_audio_examiner (shared_ptr examiner) { + shared_ptr film = _film.lock (); + DCPOMATIC_ASSERT (film); + { boost::mutex::scoped_lock lm (_mutex); _audio_stream.reset (new AudioStream (examiner->audio_frame_rate(), examiner->audio_channels ())); AudioMapping m = _audio_stream->mapping (); - m.make_default (); + film->make_audio_mapping_default (m); _audio_stream->set_mapping (m); } diff --git a/src/lib/upmixer_a.cc b/src/lib/upmixer_a.cc index dce08fe37..1edc0104d 100644 --- a/src/lib/upmixer_a.cc +++ b/src/lib/upmixer_a.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2014 Carl Hetherington + Copyright (C) 2014-2015 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,10 +19,13 @@ #include "upmixer_a.h" #include "audio_buffers.h" +#include "audio_mapping.h" #include "i18n.h" using std::string; +using std::min; +using std::vector; using boost::shared_ptr; UpmixerA::UpmixerA (int sampling_rate) @@ -56,7 +59,7 @@ UpmixerA::in_channels () const } int -UpmixerA::out_channels (int) const +UpmixerA::out_channels () const { return 6; } @@ -107,3 +110,22 @@ UpmixerA::flush () _ls.flush (); _rs.flush (); } + +void +UpmixerA::make_audio_mapping_default (AudioMapping& mapping) const +{ + /* Just map the first two input channels to our L/R */ + mapping.make_zero (); + for (int i = 0; i < min (2, mapping.input_channels()); ++i) { + mapping.set (i, i, 1); + } +} + +vector +UpmixerA::input_names () const +{ + vector n; + n.push_back (_("Upmix L")); + n.push_back (_("Upmix R")); + return n; +} diff --git a/src/lib/upmixer_a.h b/src/lib/upmixer_a.h index 32e3f5fb6..9a927b0cf 100644 --- a/src/lib/upmixer_a.h +++ b/src/lib/upmixer_a.h @@ -28,10 +28,12 @@ public: std::string name () const; std::string id () const; ChannelCount in_channels () const; - int out_channels (int) const; + int out_channels () const; boost::shared_ptr clone (int) const; boost::shared_ptr run (boost::shared_ptr); void flush (); + void make_audio_mapping_default (AudioMapping& mapping) const; + std::vector input_names () const; private: BandPassAudioFilter _left; diff --git a/src/wx/audio_mapping_view.cc b/src/wx/audio_mapping_view.cc index c61ce7f06..8f55043be 100644 --- a/src/wx/audio_mapping_view.cc +++ b/src/wx/audio_mapping_view.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2014 Carl Hetherington + Copyright (C) 2013-2015 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -37,6 +37,7 @@ using std::cout; using std::list; using std::string; using std::max; +using std::vector; using boost::shared_ptr; using boost::lexical_cast; @@ -125,8 +126,7 @@ AudioMappingView::AudioMappingView (wxWindow* parent) _grid->EnableEditing (false); _grid->SetCellHighlightPenWidth (0); _grid->SetDefaultRenderer (new NoSelectionStringRenderer); - - set_column_labels (); + _grid->AutoSize (); _sizer = new wxBoxSizer (wxVERTICAL); _sizer->Add (_grid, 1, wxEXPAND | wxALL); @@ -148,8 +148,9 @@ AudioMappingView::AudioMappingView (wxWindow* parent) Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&AudioMappingView::edit, this), ID_edit); } +/** Called when any gain value has changed */ void -AudioMappingView::map_changed () +AudioMappingView::map_values_changed () { update_cells (); Changed (_map); @@ -163,7 +164,7 @@ AudioMappingView::left_click (wxGridEvent& ev) return; } - dcp::Channel d = static_cast (ev.GetCol() - 1); + int const d = ev.GetCol() - 1; if (_map.get (ev.GetRow(), d) > 0) { _map.set (ev.GetRow(), d, 0); @@ -171,7 +172,7 @@ AudioMappingView::left_click (wxGridEvent& ev) _map.set (ev.GetRow(), d, 1); } - map_changed (); + map_values_changed (); } void @@ -189,33 +190,33 @@ AudioMappingView::right_click (wxGridEvent& ev) void AudioMappingView::off () { - _map.set (_menu_row, static_cast (_menu_column - 1), 0); - map_changed (); + _map.set (_menu_row, _menu_column - 1, 0); + map_values_changed (); } void AudioMappingView::full () { - _map.set (_menu_row, static_cast (_menu_column - 1), 1); - map_changed (); + _map.set (_menu_row, _menu_column - 1, 1); + map_values_changed (); } void AudioMappingView::minus6dB () { - _map.set (_menu_row, static_cast (_menu_column - 1), pow (10, -6.0 / 20)); - map_changed (); + _map.set (_menu_row, _menu_column - 1, pow (10, -6.0 / 20)); + map_values_changed (); } void AudioMappingView::edit () { - dcp::Channel d = static_cast (_menu_column - 1); + int const d = _menu_column - 1; AudioGainDialog* dialog = new AudioGainDialog (this, _menu_row, _menu_column - 1, _map.get (_menu_row, d)); if (dialog->ShowModal () == wxID_OK) { _map.set (_menu_row, d, dialog->value ()); - map_changed (); + map_values_changed (); } dialog->Destroy (); @@ -229,104 +230,51 @@ AudioMappingView::set (AudioMapping map) } void -AudioMappingView::update_cells () +AudioMappingView::set_input_channels (vector const & names) { - if (_grid->GetNumberRows ()) { - _grid->DeleteRows (0, _grid->GetNumberRows ()); + for (int i = 0; i < _grid->GetNumberRows(); ++i) { + _grid->SetCellValue (i, 0, std_to_wx (names[i])); } - - _grid->InsertRows (0, _map.content_channels ()); - - for (int i = 0; i < _map.content_channels(); ++i) { - for (int j = 0; j < MAX_DCP_AUDIO_CHANNELS; ++j) { - _grid->SetCellRenderer (i, j + 1, new ValueRenderer); - } - } - - for (int i = 0; i < _map.content_channels(); ++i) { - _grid->SetCellValue (i, 0, wxString::Format (wxT("%d"), i + 1)); - - for (int j = 1; j < _grid->GetNumberCols(); ++j) { - _grid->SetCellValue (i, j, std_to_wx (raw_convert (_map.get (i, static_cast (j - 1))))); - } - } - - _grid->AutoSize (); } -/** @param c Number of DCP channels */ void -AudioMappingView::set_channels (int c) +AudioMappingView::set_output_channels (vector const & names) { - c++; + int const o = names.size() + 1; + if (o < _grid->GetNumberCols ()) { + _grid->DeleteCols (o, _grid->GetNumberCols() - o); + } else if (o > _grid->GetNumberCols ()) { + _grid->InsertCols (_grid->GetNumberCols(), o - _grid->GetNumberCols()); + } + + _grid->SetColLabelValue (0, _("Content")); - if (c < _grid->GetNumberCols ()) { - _grid->DeleteCols (c, _grid->GetNumberCols() - c); - } else if (c > _grid->GetNumberCols ()) { - _grid->InsertCols (_grid->GetNumberCols(), c - _grid->GetNumberCols()); - set_column_labels (); + for (size_t i = 0; i < names.size(); ++i) { + _grid->SetColLabelValue (i + 1, std_to_wx (names[i])); } update_cells (); } void -AudioMappingView::set_column_labels () +AudioMappingView::update_cells () { - int const c = _grid->GetNumberCols (); - - _grid->SetColLabelValue (0, _("Content")); - -#if MAX_DCP_AUDIO_CHANNELS != 12 -#warning AudioMappingView::set_column_labels() is expecting the wrong MAX_DCP_AUDIO_CHANNELS -#endif - - if (c > 0) { - _grid->SetColLabelValue (1, _("L")); - } - - if (c > 1) { - _grid->SetColLabelValue (2, _("R")); - } - - if (c > 2) { - _grid->SetColLabelValue (3, _("C")); - } - - if (c > 3) { - _grid->SetColLabelValue (4, _("Lfe")); - } - - if (c > 4) { - _grid->SetColLabelValue (5, _("Ls")); - } - - if (c > 5) { - _grid->SetColLabelValue (6, _("Rs")); - } - - if (c > 6) { - _grid->SetColLabelValue (7, _("HI")); - } - - if (c > 7) { - _grid->SetColLabelValue (8, _("VI")); - } - - if (c > 8) { - _grid->SetColLabelValue (9, _("Lc")); + if (_grid->GetNumberRows ()) { + _grid->DeleteRows (0, _grid->GetNumberRows ()); } - if (c > 9) { - _grid->SetColLabelValue (10, _("Rc")); - } + _grid->InsertRows (0, _map.input_channels ()); - if (c > 10) { - _grid->SetColLabelValue (11, _("BsL")); + for (int i = 0; i < _map.input_channels(); ++i) { + for (int j = 0; j < _map.output_channels(); ++j) { + _grid->SetCellRenderer (i, j + 1, new ValueRenderer); + } } - - if (c > 11) { - _grid->SetColLabelValue (12, _("BsR")); + + for (int i = 0; i < _map.input_channels(); ++i) { + for (int j = 1; j < _grid->GetNumberCols(); ++j) { + _grid->SetCellValue (i, j, std_to_wx (raw_convert (_map.get (i, j - 1)))); + } } _grid->AutoSize (); @@ -351,7 +299,7 @@ AudioMappingView::mouse_moved (wxMouseEvent& ev) if (row != _last_tooltip_row || column != _last_tooltip_column) { wxString s; - float const gain = _map.get (row, static_cast (column - 1)); + float const gain = _map.get (row, column - 1); if (gain == 0) { s = wxString::Format (_("No audio will be passed from content channel %d to DCP channel %d."), row + 1, column); } else if (gain == 1) { diff --git a/src/wx/audio_mapping_view.h b/src/wx/audio_mapping_view.h index 7ed699463..4210d7554 100644 --- a/src/wx/audio_mapping_view.h +++ b/src/wx/audio_mapping_view.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2014 Carl Hetherington + Copyright (C) 2013-2015 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,10 +18,8 @@ */ /** @file src/wx/audio_mapping_view.h - * @brief AudioMappingView class + * @brief AudioMappingView class. * - * This class displays the mapping of one set of audio channels to another, - * with gain values on each node of the map. */ #include @@ -29,13 +27,29 @@ #include #include "lib/audio_mapping.h" +/** @class AudioMappingView + * @brief This class displays the mapping of one set of audio channels to another, + * with gain values on each node of the map. + * + * The AudioMapping passed to set() describes the actual channel mapping, + * and the names passed to set_input_channels() and set_output_channels() are + * used to label the rows and columns. + * + * The display's row count is equal to the AudioMapping's input channel count, + * and the column count is equal to the number of name passed to + * set_output_channels(). Any other output channels in the AudioMapping are + * hidden from view. Thus input channels are never hidden but output channels + * might be. + */ + class AudioMappingView : public wxPanel { public: AudioMappingView (wxWindow *); void set (AudioMapping); - void set_channels (int); + void set_input_channels (std::vector const & names); + void set_output_channels (std::vector const & names); boost::signals2::signal Changed; @@ -43,9 +57,8 @@ private: void left_click (wxGridEvent &); void right_click (wxGridEvent &); void mouse_moved (wxMouseEvent &); - void set_column_labels (); void update_cells (); - void map_changed (); + void map_values_changed (); void off (); void full (); diff --git a/src/wx/audio_panel.cc b/src/wx/audio_panel.cc index c34a67032..436e6db4c 100644 --- a/src/wx/audio_panel.cc +++ b/src/wx/audio_panel.cc @@ -110,8 +110,8 @@ AudioPanel::film_changed (Film::Property property) { switch (property) { case Film::AUDIO_CHANNELS: - _mapping->set_channels (_parent->film()->audio_channels ()); - _sizer->Layout (); + case Film::AUDIO_PROCESSOR: + _mapping->set_output_channels (_parent->film()->audio_output_names ()); break; case Film::VIDEO_FRAME_RATE: setup_description (); @@ -124,17 +124,14 @@ AudioPanel::film_changed (Film::Property property) void AudioPanel::film_content_changed (int property) { - AudioContentList ac = _parent->selected_audio (); - shared_ptr acs; - shared_ptr fcs; - if (ac.size() == 1) { - acs = ac.front (); - fcs = dynamic_pointer_cast (acs); - } - if (property == AudioContentProperty::AUDIO_STREAMS) { - _mapping->set (acs ? acs->audio_mapping () : AudioMapping ()); - _sizer->Layout (); + AudioContentList ac = _parent->selected_audio (); + if (ac.size() == 1) { + _mapping->set (ac.front()->audio_mapping()); + _mapping->set_input_channels (ac.front()->audio_channel_names ()); + } else { + _mapping->set (AudioMapping ()); + } setup_description (); } } diff --git a/src/wx/dcp_panel.cc b/src/wx/dcp_panel.cc index 7a5f0554f..022fe9a06 100644 --- a/src/wx/dcp_panel.cc +++ b/src/wx/dcp_panel.cc @@ -696,7 +696,5 @@ DCPPanel::audio_processor_changed () } string const s = string_client_data (_audio_processor->GetClientObject (_audio_processor->GetSelection ())); - if (s != "none") { - _film->set_audio_processor (AudioProcessor::from_id (s)); - } + _film->set_audio_processor (AudioProcessor::from_id (s)); } diff --git a/src/wx/timeline.cc b/src/wx/timeline.cc index c1713bb61..b0197ad3d 100644 --- a/src/wx/timeline.cc +++ b/src/wx/timeline.cc @@ -115,7 +115,7 @@ Timeline::recreate_views () } shared_ptr ac = dynamic_pointer_cast (*i); - if (ac && !ac->audio_mapping().mapped_dcp_channels().empty ()) { + if (ac && !ac->audio_mapping().mapped_output_channels().empty ()) { _views.push_back (shared_ptr (new TimelineAudioContentView (*this, *i))); } diff --git a/test/audio_mapping_test.cc b/test/audio_mapping_test.cc index fc597b91d..14c6d031d 100644 --- a/test/audio_mapping_test.cc +++ b/test/audio_mapping_test.cc @@ -28,18 +28,11 @@ BOOST_AUTO_TEST_CASE (audio_mapping_test) { AudioMapping none; - BOOST_CHECK_EQUAL (none.content_channels(), 0); + BOOST_CHECK_EQUAL (none.input_channels(), 0); - AudioMapping four (4); - BOOST_CHECK_EQUAL (four.content_channels(), 4); - four.make_default (); + AudioMapping four (4, MAX_DCP_AUDIO_CHANNELS); + BOOST_CHECK_EQUAL (four.input_channels(), 4); - for (int i = 0; i < 4; ++i) { - for (int j = 0; j < MAX_DCP_AUDIO_CHANNELS; ++j) { - BOOST_CHECK_EQUAL (four.get (i, static_cast (j)), i == j ? 1 : 0); - } - } - - four.set (0, dcp::RIGHT, 1); - BOOST_CHECK_EQUAL (four.get (0, dcp::RIGHT), 1); + four.set (0, 1, 1); + BOOST_CHECK_EQUAL (four.get (0, 1), 1); } diff --git a/test/stream_test.cc b/test/stream_test.cc index de2108066..800bbd049 100644 --- a/test/stream_test.cc +++ b/test/stream_test.cc @@ -76,13 +76,13 @@ BOOST_AUTO_TEST_CASE (stream_test) BOOST_CHECK_EQUAL (a.frame_rate(), 44100); BOOST_CHECK_EQUAL (a.channels(), 2); BOOST_CHECK_EQUAL (a.name, "hello there world"); - BOOST_CHECK_EQUAL (a.mapping().content_channels(), 2); + BOOST_CHECK_EQUAL (a.mapping().input_channels(), 2); - BOOST_CHECK_EQUAL (a.mapping().get (0, dcp::LEFT), 1); - BOOST_CHECK_EQUAL (a.mapping().get (0, dcp::RIGHT), 0); - BOOST_CHECK_EQUAL (a.mapping().get (0, dcp::CENTRE), 1); - BOOST_CHECK_EQUAL (a.mapping().get (1, dcp::LEFT), 0); - BOOST_CHECK_EQUAL (a.mapping().get (1, dcp::RIGHT), 1); - BOOST_CHECK_EQUAL (a.mapping().get (1, dcp::CENTRE), 1); + BOOST_CHECK_EQUAL (a.mapping().get (0, static_cast (dcp::LEFT)), 1); + BOOST_CHECK_EQUAL (a.mapping().get (0, static_cast (dcp::RIGHT)), 0); + BOOST_CHECK_EQUAL (a.mapping().get (0, static_cast (dcp::CENTRE)), 1); + BOOST_CHECK_EQUAL (a.mapping().get (1, static_cast (dcp::LEFT)), 0); + BOOST_CHECK_EQUAL (a.mapping().get (1, static_cast (dcp::RIGHT)), 1); + BOOST_CHECK_EQUAL (a.mapping().get (1, static_cast (dcp::CENTRE)), 1); } -- 2.30.2