{
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<dcp::Channel> (k), mapping.get (c, static_cast<dcp::Channel> (k)));
+ stream_mapping.set (j, k, mapping.get (c, k));
}
++c;
}
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<dcp::Channel> (k), mapping.get (j, static_cast<dcp::Channel> (k)));
+ merged.set (c, k, mapping.get (j, k));
}
++c;
}
return "";
}
+/** @return true if any stream in this content has a sampling rate of more than 48kHz */
bool
AudioContent::has_rate_above_48k () const
{
return false;
}
+
+/** @return User-visible names of each of our audio channels */
+vector<string>
+AudioContent::audio_channel_names () const
+{
+ vector<string> 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;
+}
boost::filesystem::path audio_analysis_path () const;
int resampled_audio_frame_rate () const;
bool has_rate_above_48k () const;
+ std::vector<std::string> audio_channel_names () const;
boost::signals2::connection analyse_audio (boost::function<void()>);
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<int> ("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<dcp::Channel> (i), 1);
- }
+ setup (node->number_child<int> ("InputChannels"), node->number_child<int> ("OutputChannels"));
}
-}
-
-AudioMapping::AudioMapping (cxml::ConstNodePtr node, int state_version)
-{
- setup (node->number_child<int> ("ContentChannels"));
if (state_version <= 5) {
/* Old-style: on/off mapping */
} else {
list<cxml::NodePtr> const c = node->node_children ("Gain");
for (list<cxml::NodePtr>::const_iterator i = c.begin(); i != c.end(); ++i) {
- set (
- (*i)->number_attribute<int> ("Content"),
- static_cast<dcp::Channel> ((*i)->number_attribute<int> ("DCP")),
- raw_convert<float> ((*i)->content ())
- );
+ if (state_version < 32) {
+ set (
+ (*i)->number_attribute<int> ("Content"),
+ static_cast<dcp::Channel> ((*i)->number_attribute<int> ("DCP")),
+ raw_convert<float> ((*i)->content ())
+ );
+ } else {
+ set (
+ (*i)->number_attribute<int> ("Input"),
+ (*i)->number_attribute<int> ("Output"),
+ raw_convert<float> ((*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<string> (_content_channels));
+ node->add_child ("InputChannels")->add_child_text (raw_convert<string> (_input_channels));
+ node->add_child ("OutputChannels")->add_child_text (raw_convert<string> (_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<string> (c));
- t->set_attribute ("DCP", raw_convert<string> (d));
- t->add_child_text (raw_convert<string> (get (c, static_cast<dcp::Channel> (d))));
+ t->set_attribute ("Input", raw_convert<string> (c));
+ t->set_attribute ("Output", raw_convert<string> (d));
+ t->add_child_text (raw_convert<string> (get (c, d)));
}
}
}
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]);
}
}
return digester.get ();
}
-list<dcp::Channel>
-AudioMapping::mapped_dcp_channels () const
+list<int>
+AudioMapping::mapped_output_channels () const
{
static float const minus_96_db = 0.000015849;
- list<dcp::Channel> mapped;
+ list<int> mapped;
for (vector<vector<float> >::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);
}
}
}
}
}
}
-
-void
-AudioMapping::set_name (int channel, string name)
-{
- _name[channel] = name;
-}
}
/** @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<dcp::Channel> mapped_dcp_channels () const;
+ std::list<int> 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<std::vector<float> > _gain;
- std::vector<std::string> _name;
};
#endif
/*
- Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2014-2015 Carl Hetherington <cth@carlh.net>
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
*/
+/** @file src/lib/audio_processor.h
+ * @brief AudioProcessor class.
+ */
+
#ifndef DCPOMATIC_AUDIO_PROCESSOR_H
#define DCPOMATIC_AUDIO_PROCESSOR_H
#include <boost/shared_ptr.hpp>
#include <list>
#include <string>
+#include <vector>
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<AudioProcessor> clone (int sampling_rate) const = 0;
+ /** Process some data, returning the processed result */
virtual boost::shared_ptr<AudioBuffers> run (boost::shared_ptr<const AudioBuffers>) = 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<std::string> input_names () const = 0;
static std::list<AudioProcessor const *> all ();
static void setup_audio_processors ();
#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)
AudioStream::channels () const
{
boost::mutex::scoped_lock lm (_mutex);
- return _mapping.content_channels ();
+ return _mapping.input_channels ();
}
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);
}
/* Find all mapped channels */
- list<dcp::Channel> mapped;
+ list<int> mapped;
for (ContentList::const_iterator i = cl.begin(); i != cl.end(); ++i) {
shared_ptr<const AudioContent> ac = dynamic_pointer_cast<const AudioContent> (*i);
if (ac) {
- list<dcp::Channel> c = ac->audio_mapping().mapped_dcp_channels ();
+ list<int> c = ac->audio_mapping().mapped_output_channels ();
copy (c.begin(), c.end(), back_inserter (mapped));
}
}
int non_lfe = 0;
int lfe = 0;
- for (list<dcp::Channel>::const_iterator i = mapped.begin(); i != mapped.end(); ++i) {
- if (static_cast<int> (*i) >= audio_channels()) {
+ for (list<int>::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<dcp::Channel> (*i) == dcp::LFE) {
++lfe;
} else {
++non_lfe;
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<int> (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<string>
+Film::audio_output_names () const
+{
+ if (audio_processor ()) {
+ return audio_processor()->input_names ();
+ }
+
+ vector<string> 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<string> (n.begin(), n.begin() + audio_channels ());
+}
class AudioContent;
class Screen;
class AudioProcessor;
+class AudioMapping;
struct isdcf_name_test;
/** @class Film
std::string subtitle_language () const;
+ void make_audio_mapping_default (AudioMapping & mapping) const;
+ std::vector<std::string> audio_output_names () const;
+
/** Identifiers for the parts of our state;
used for signalling changes.
*/
/*
- Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2014-2015 Carl Hetherington <cth@carlh.net>
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
#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
}
int
-MidSideDecoder::out_channels (int) const
+MidSideDecoder::out_channels () const
{
return 3;
}
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<string>
+MidSideDecoder::input_names () const
+{
+ vector<string> 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;
+}
/*
- Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2014-2015 Carl Hetherington <cth@carlh.net>
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
std::string name () const;
std::string id () const;
ChannelCount in_channels () const;
- int out_channels (int) const;
+ int out_channels () const;
boost::shared_ptr<AudioProcessor> clone (int) const;
boost::shared_ptr<AudioBuffers> run (boost::shared_ptr<const AudioBuffers>);
+ void make_audio_mapping_default (AudioMapping& mapping) const;
+ std::vector<std::string> input_names () const;
};
-
-
shared_ptr<AudioBuffers> 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<dcp::Channel> (j)) > 0) {
+ if (map.get (i, j) > 0) {
dcp_mapped->accumulate_channel (
all.audio.get(),
i,
j,
- map.get (i, static_cast<dcp::Channel> (j))
+ map.get (i, j)
);
}
}
void
SingleStreamAudioContent::take_from_audio_examiner (shared_ptr<AudioExaminer> examiner)
{
+ shared_ptr<const Film> 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);
}
/*
- Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2014-2015 Carl Hetherington <cth@carlh.net>
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
#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)
}
int
-UpmixerA::out_channels (int) const
+UpmixerA::out_channels () const
{
return 6;
}
_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<string>
+UpmixerA::input_names () const
+{
+ vector<string> n;
+ n.push_back (_("Upmix L"));
+ n.push_back (_("Upmix R"));
+ return n;
+}
std::string name () const;
std::string id () const;
ChannelCount in_channels () const;
- int out_channels (int) const;
+ int out_channels () const;
boost::shared_ptr<AudioProcessor> clone (int) const;
boost::shared_ptr<AudioBuffers> run (boost::shared_ptr<const AudioBuffers>);
void flush ();
+ void make_audio_mapping_default (AudioMapping& mapping) const;
+ std::vector<std::string> input_names () const;
private:
BandPassAudioFilter _left;
/*
- Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2015 Carl Hetherington <cth@carlh.net>
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
using std::list;
using std::string;
using std::max;
+using std::vector;
using boost::shared_ptr;
using boost::lexical_cast;
_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);
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);
return;
}
- dcp::Channel d = static_cast<dcp::Channel> (ev.GetCol() - 1);
+ int const d = ev.GetCol() - 1;
if (_map.get (ev.GetRow(), d) > 0) {
_map.set (ev.GetRow(), d, 0);
_map.set (ev.GetRow(), d, 1);
}
- map_changed ();
+ map_values_changed ();
}
void
void
AudioMappingView::off ()
{
- _map.set (_menu_row, static_cast<dcp::Channel> (_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<dcp::Channel> (_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<dcp::Channel> (_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<dcp::Channel> (_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 ();
}
void
-AudioMappingView::update_cells ()
+AudioMappingView::set_input_channels (vector<string> 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<string> (_map.get (i, static_cast<dcp::Channel> (j - 1)))));
- }
- }
-
- _grid->AutoSize ();
}
-/** @param c Number of DCP channels */
void
-AudioMappingView::set_channels (int c)
+AudioMappingView::set_output_channels (vector<string> 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<string> (_map.get (i, j - 1))));
+ }
}
_grid->AutoSize ();
if (row != _last_tooltip_row || column != _last_tooltip_column) {
wxString s;
- float const gain = _map.get (row, static_cast<dcp::Channel> (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) {
/*
- Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2015 Carl Hetherington <cth@carlh.net>
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
*/
/** @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 <boost/signals2.hpp>
#include <wx/grid.h>
#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<std::string> const & names);
+ void set_output_channels (std::vector<std::string> const & names);
boost::signals2::signal<void (AudioMapping)> Changed;
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 ();
{
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 ();
void
AudioPanel::film_content_changed (int property)
{
- AudioContentList ac = _parent->selected_audio ();
- shared_ptr<AudioContent> acs;
- shared_ptr<FFmpegContent> fcs;
- if (ac.size() == 1) {
- acs = ac.front ();
- fcs = dynamic_pointer_cast<FFmpegContent> (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 ();
}
}
}
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));
}
}
shared_ptr<AudioContent> ac = dynamic_pointer_cast<AudioContent> (*i);
- if (ac && !ac->audio_mapping().mapped_dcp_channels().empty ()) {
+ if (ac && !ac->audio_mapping().mapped_output_channels().empty ()) {
_views.push_back (shared_ptr<TimelineView> (new TimelineAudioContentView (*this, *i)));
}
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<dcp::Channel> (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);
}
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<int> (dcp::LEFT)), 1);
+ BOOST_CHECK_EQUAL (a.mapping().get (0, static_cast<int> (dcp::RIGHT)), 0);
+ BOOST_CHECK_EQUAL (a.mapping().get (0, static_cast<int> (dcp::CENTRE)), 1);
+ BOOST_CHECK_EQUAL (a.mapping().get (1, static_cast<int> (dcp::LEFT)), 0);
+ BOOST_CHECK_EQUAL (a.mapping().get (1, static_cast<int> (dcp::RIGHT)), 1);
+ BOOST_CHECK_EQUAL (a.mapping().get (1, static_cast<int> (dcp::CENTRE)), 1);
}