summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2019-12-18 01:09:25 +0100
committerCarl Hetherington <cth@carlh.net>2019-12-19 22:11:47 +0100
commite0b2ef3dbac6fc4900cad6fea4395c212578602b (patch)
tree9ff34490552015577080f0d9bbe4d54b87e5d327 /src
parent30c9ecad729397574754163d13253c54a2285a6a (diff)
Add an output audio matrix (#1482).
Diffstat (limited to 'src')
-rw-r--r--src/lib/config.cc60
-rw-r--r--src/lib/config.h10
-rw-r--r--src/wx/audio_mapping_view.cc32
-rw-r--r--src/wx/audio_mapping_view.h7
-rw-r--r--src/wx/audio_panel.cc2
-rw-r--r--src/wx/config_dialog.cc44
-rw-r--r--src/wx/config_dialog.h8
-rw-r--r--src/wx/film_viewer.cc48
8 files changed, 159 insertions, 52 deletions
diff --git a/src/lib/config.cc b/src/lib/config.cc
index 581620f83..7f32320df 100644
--- a/src/lib/config.cc
+++ b/src/lib/config.cc
@@ -53,6 +53,7 @@ using std::cout;
using std::ifstream;
using std::string;
using std::list;
+using std::min;
using std::max;
using std::remove;
using std::exception;
@@ -173,6 +174,7 @@ Config::set_defaults ()
_player_content_directory = boost::none;
_player_playlist_directory = boost::none;
_player_kdm_directory = boost::none;
+ _audio_mapping = boost::none;
#ifdef DCPOMATIC_VARIANT_SWAROOP
_player_background_image = boost::none;
_kdm_server_url = "http://localhost:8000/{CPL}";
@@ -590,6 +592,11 @@ try
_player_content_directory = f.optional_string_child("PlayerContentDirectory");
_player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
_player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
+
+ if (f.optional_node_child("AudioMapping")) {
+ _audio_mapping = AudioMapping (f.node_child("AudioMapping"), Film::current_state_version);
+ }
+
#ifdef DCPOMATIC_VARIANT_SWAROOP
_player_background_image = f.optional_string_child("PlayerBackgroundImage");
_kdm_server_url = f.optional_string_child("KDMServerURL").get_value_or("http://localhost:8000/{CPL}");
@@ -1037,6 +1044,9 @@ Config::write_config () const
/* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
root->add_child("PlayerKDMDirectory")->add_child_text(_player_kdm_directory->string());
}
+ if (_audio_mapping) {
+ _audio_mapping->as_xml (root->add_child("AudioMapping"));
+ }
#ifdef DCPOMATIC_VARIANT_SWAROOP
if (_player_background_image) {
root->add_child("PlayerBackgroundImage")->add_child_text(_player_background_image->string());
@@ -1395,3 +1405,53 @@ Config::have_write_permission () const
fclose (f);
return true;
}
+
+/** @param output_channels Number of output channels in use.
+ * @return Audio mapping for this output channel count (may be a default).
+ */
+AudioMapping
+Config::audio_mapping (int output_channels)
+{
+ if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
+ /* Set up a default */
+ _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
+ if (output_channels == 2) {
+ /* Special case for stereo output.
+ Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
+ Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
+ */
+ _audio_mapping->set (dcp::LEFT, 0, 1 / sqrt(2)); // L -> Lt
+ _audio_mapping->set (dcp::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
+ _audio_mapping->set (dcp::CENTRE, 0, 1 / 2.0); // C -> Lt
+ _audio_mapping->set (dcp::CENTRE, 1, 1 / 2.0); // C -> Rt
+ _audio_mapping->set (dcp::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
+ _audio_mapping->set (dcp::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
+ _audio_mapping->set (dcp::LS, 0, 1 / sqrt(2)); // Ls -> Lt
+ _audio_mapping->set (dcp::RS, 1, 1 / sqrt(2)); // Rs -> Rt
+ } else {
+ /* 1:1 mapping */
+ for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
+ _audio_mapping->set (i, i, 1);
+ }
+ }
+ }
+
+ return *_audio_mapping;
+}
+
+void
+Config::set_audio_mapping (AudioMapping m)
+{
+ _audio_mapping = m;
+ changed (AUDIO_MAPPING);
+}
+
+void
+Config::set_audio_mapping_to_default ()
+{
+ DCPOMATIC_ASSERT (_audio_mapping);
+ int const ch = _audio_mapping->output_channels ();
+ _audio_mapping = boost::none;
+ _audio_mapping = audio_mapping (ch);
+ changed (AUDIO_MAPPING);
+}
diff --git a/src/lib/config.h b/src/lib/config.h
index ff7a0fe39..061d6ee7c 100644
--- a/src/lib/config.h
+++ b/src/lib/config.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2012-2018 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
@@ -29,6 +29,7 @@
#include "types.h"
#include "state.h"
#include "edid.h"
+#include "audio_mapping.h"
#include <dcp/name_format.h>
#include <dcp/certificate_chain.h>
#include <dcp/encrypted_kdm.h>
@@ -83,6 +84,7 @@ public:
PLAYER_DEBUG_LOG,
HISTORY,
SHOW_EXPERIMENTAL_AUDIO_PROCESSORS,
+ AUDIO_MAPPING,
#ifdef DCPOMATIC_VARIANT_SWAROOP
PLAYER_BACKGROUND_IMAGE,
#endif
@@ -522,6 +524,8 @@ public:
return _player_kdm_directory;
}
+ AudioMapping audio_mapping (int output_channels);
+
#ifdef DCPOMATIC_VARIANT_SWAROOP
boost::optional<boost::filesystem::path> player_background_image () const {
return _player_background_image;
@@ -1036,6 +1040,9 @@ public:
changed ();
}
+ void set_audio_mapping (AudioMapping m);
+ void set_audio_mapping_to_default ();
+
#ifdef DCPOMATIC_VARIANT_SWAROOP
void set_player_background_image (boost::filesystem::path p) {
maybe_set (_player_background_image, p, PLAYER_BACKGROUND_IMAGE);
@@ -1293,6 +1300,7 @@ private:
boost::optional<boost::filesystem::path> _player_content_directory;
boost::optional<boost::filesystem::path> _player_playlist_directory;
boost::optional<boost::filesystem::path> _player_kdm_directory;
+ boost::optional<AudioMapping> _audio_mapping;
#ifdef DCPOMATIC_VARIANT_SWAROOP
boost::optional<boost::filesystem::path> _player_background_image;
std::string _kdm_server_url;
diff --git a/src/wx/audio_mapping_view.cc b/src/wx/audio_mapping_view.cc
index 98430da3b..140f18d60 100644
--- a/src/wx/audio_mapping_view.cc
+++ b/src/wx/audio_mapping_view.cc
@@ -60,10 +60,14 @@ enum {
ID_edit = 4
};
-AudioMappingView::AudioMappingView (wxWindow* parent)
+AudioMappingView::AudioMappingView (wxWindow* parent, wxString left_label, wxString from, wxString top_label, wxString to)
: wxPanel (parent, wxID_ANY)
, _menu_input (0)
, _menu_output (1)
+ , _left_label (left_label)
+ , _from (from)
+ , _top_label (top_label)
+ , _to (to)
{
_menu = new wxMenu;
_menu->Append (ID_off, _("Off"));
@@ -159,16 +163,12 @@ AudioMappingView::paint_static (wxDC& dc, wxGraphicsContext* gc)
wxCoord label_width;
wxCoord label_height;
- /* DCP label at the top */
+ dc.GetTextExtent (_top_label, &label_width, &label_height);
+ dc.DrawText (_top_label, LEFT_WIDTH + (_output_channels.size() * GRID_SPACING - label_width) / 2, (GRID_SPACING - label_height) / 2);
- dc.GetTextExtent (_("DCP"), &label_width, &label_height);
- dc.DrawText (_("DCP"), LEFT_WIDTH + (_output_channels.size() * GRID_SPACING - label_width) / 2, (GRID_SPACING - label_height) / 2);
-
- /* Content label on the left */
-
- dc.GetTextExtent (_("Content"), &label_width, &label_height);
+ dc.GetTextExtent (_left_label, &label_width, &label_height);
dc.DrawRotatedText (
- _("Content"),
+ _left_label,
(GRID_SPACING - label_height) / 2,
TOP_HEIGHT + (_input_channels.size() * GRID_SPACING + label_width) / 2,
90
@@ -557,7 +557,7 @@ AudioMappingView::safe_input_channel_name (int n) const
}
}
- if (group) {
+ if (group && !group->IsEmpty()) {
return wxString::Format ("%s/%s", group->data(), std_to_wx(_input_channels[n]).data());
}
@@ -584,21 +584,27 @@ AudioMappingView::motion (wxMouseEvent& ev)
float const gain = _map.get(channels->first, channels->second);
if (gain == 0) {
s = wxString::Format (
- _("No audio will be passed from content channel '%s' to DCP channel '%s'."),
+ _("No audio will be passed from %s channel '%s' to %s channel '%s'."),
+ _from,
safe_input_channel_name(channels->first),
+ _to,
safe_output_channel_name(channels->second)
);
} else if (gain == 1) {
s = wxString::Format (
- _("Audio will be passed from content channel %s to DCP channel %s unaltered."),
+ _("Audio will be passed from %s channel %s to %s channel %s unaltered."),
+ _from,
safe_input_channel_name(channels->first),
+ _to,
safe_output_channel_name(channels->second)
);
} else {
float const dB = 20 * log10 (gain);
s = wxString::Format (
- _("Audio will be passed from content channel %s to DCP channel %s with gain %.1fdB."),
+ _("Audio will be passed from %s channel %s to %s channel %s with gain %.1fdB."),
+ _from,
safe_input_channel_name(channels->first),
+ _to,
safe_output_channel_name(channels->second),
dB
);
diff --git a/src/wx/audio_mapping_view.h b/src/wx/audio_mapping_view.h
index fd8a16d5c..aab6f64a6 100644
--- a/src/wx/audio_mapping_view.h
+++ b/src/wx/audio_mapping_view.h
@@ -45,7 +45,7 @@
class AudioMappingView : public wxPanel
{
public:
- explicit AudioMappingView (wxWindow *);
+ explicit AudioMappingView (wxWindow *, wxString left_label, wxString from, wxString top_label, wxString to);
void set (AudioMapping);
void set_input_channels (std::vector<std::string> const & names);
@@ -107,6 +107,11 @@ private:
int _menu_input;
int _menu_output;
+ wxString _left_label;
+ wxString _from;
+ wxString _top_label;
+ wxString _to;
+
std::vector<std::string> _input_channels;
std::vector<std::string> _output_channels;
std::vector<Group> _input_groups;
diff --git a/src/wx/audio_panel.cc b/src/wx/audio_panel.cc
index 0cb062efd..183a3d4b9 100644
--- a/src/wx/audio_panel.cc
+++ b/src/wx/audio_panel.cc
@@ -88,7 +88,7 @@ AudioPanel::AudioPanel (ContentPanel* p)
/// TRANSLATORS: this is an abbreviation for milliseconds, the unit of time
_delay_ms_label = create_label (this, _("ms"), false);
- _mapping = new AudioMappingView (this);
+ _mapping = new AudioMappingView (this, _("Content"), _("content"), _("DCP"), _("DCP"));
_sizer->Add (_mapping, 1, wxEXPAND | wxALL, 6);
_description = new StaticText (this, wxT(" \n"), wxDefaultPosition, wxDefaultSize);
diff --git a/src/wx/config_dialog.cc b/src/wx/config_dialog.cc
index 14948afe8..7f96e2c35 100644
--- a/src/wx/config_dialog.cc
+++ b/src/wx/config_dialog.cc
@@ -23,6 +23,8 @@
#include "check_box.h"
#include "nag_dialog.h"
#include "dcpomatic_button.h"
+#include "audio_mapping_view.h"
+#include <dcp/raw_convert.h>
#include <iostream>
using std::string;
@@ -873,6 +875,16 @@ SoundPage::setup ()
table->Add (s, wxGBPosition(r, 1));
++r;
+ add_label_to_sizer (table, _panel, _("Mapping"), true, wxGBPosition(r, 0));
+ _map = new AudioMappingView (_panel, _("DCP"), _("DCP"), _("Output"), _("output"));
+ _map->SetSize (-1, 600);
+ table->Add (_map, wxGBPosition(r, 1), wxDefaultSpan, wxEXPAND);
+ ++r;
+
+ _reset_to_default = new Button (_panel, _("Reset to default"));
+ table->Add (_reset_to_default, wxGBPosition(r, 1));
+ ++r;
+
wxFont font = _sound_output_details->GetFont();
font.SetStyle (wxFONTSTYLE_ITALIC);
font.SetPointSize (font.GetPointSize() - 1);
@@ -886,8 +898,22 @@ SoundPage::setup ()
}
}
- _sound->Bind (wxEVT_CHECKBOX, bind (&SoundPage::sound_changed, this));
- _sound_output->Bind (wxEVT_CHOICE, bind (&SoundPage::sound_output_changed, this));
+ _sound->Bind (wxEVT_CHECKBOX, bind(&SoundPage::sound_changed, this));
+ _sound_output->Bind (wxEVT_CHOICE, bind(&SoundPage::sound_output_changed, this));
+ _map->Changed.connect (bind(&SoundPage::map_changed, this, _1));
+ _reset_to_default->Bind (wxEVT_BUTTON, bind(&SoundPage::reset_to_default, this));
+}
+
+void
+SoundPage::reset_to_default ()
+{
+ Config::instance()->set_audio_mapping_to_default ();
+}
+
+void
+SoundPage::map_changed (AudioMapping m)
+{
+ Config::instance()->set_audio_mapping (m);
}
void
@@ -969,6 +995,20 @@ SoundPage::config_changed ()
wxString::Format(_("%d channels on %s"), channels, apis[audio.getCurrentApi()])
);
+ _map->set (Config::instance()->audio_mapping(channels));
+
+ vector<string> input;
+ for (int i = 0; i < MAX_DCP_AUDIO_CHANNELS; ++i) {
+ input.push_back (short_audio_channel_name(i));
+ }
+ _map->set_input_channels (input);
+
+ vector<string> output;
+ for (int i = 0; i < channels; ++i) {
+ output.push_back (dcp::raw_convert<string>(i));
+ }
+ _map->set_output_channels (output);
+
setup_sensitivity ();
}
diff --git a/src/wx/config_dialog.h b/src/wx/config_dialog.h
index 1a9d97a43..565ecf1c4 100644
--- a/src/wx/config_dialog.h
+++ b/src/wx/config_dialog.h
@@ -44,6 +44,8 @@
#include <boost/foreach.hpp>
#include <iostream>
+class AudioMappingView;
+
class Page
{
public:
@@ -200,12 +202,16 @@ private:
void config_changed ();
boost::optional<std::string> get_sound_output ();
void sound_changed ();
- void sound_output_changed ();
+ void sound_output_changed ();
void setup_sensitivity ();
+ void map_changed (AudioMapping m);
+ void reset_to_default ();
wxCheckBox* _sound;
wxChoice* _sound_output;
wxStaticText* _sound_output_details;
+ AudioMappingView* _map;
+ Button* _reset_to_default;
};
#endif
diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc
index 893e1bf0f..509cc9426 100644
--- a/src/wx/film_viewer.cc
+++ b/src/wx/film_viewer.cc
@@ -209,40 +209,17 @@ FilmViewer::recreate_butler ()
return;
}
- AudioMapping map = AudioMapping (_film->audio_channels(), _audio_channels);
-
- if (_audio_channels != 2 || _film->audio_channels() < 3) {
- for (int i = 0; i < min (_film->audio_channels(), _audio_channels); ++i) {
- map.set (i, i, 1);
- }
- } else {
- /* Special case: stereo output, at least 3 channel input.
- Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
- Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
- */
- if (_film->audio_channels() > 0) {
- map.set (dcp::LEFT, 0, 1 / sqrt(2)); // L -> Lt
- }
- if (_film->audio_channels() > 1) {
- map.set (dcp::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
- }
- if (_film->audio_channels() > 2) {
- map.set (dcp::CENTRE, 0, 1 / 2.0); // C -> Lt
- map.set (dcp::CENTRE, 1, 1 / 2.0); // C -> Rt
- }
- if (_film->audio_channels() > 3) {
- map.set (dcp::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
- map.set (dcp::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
- }
- if (_film->audio_channels() > 4) {
- map.set (dcp::LS, 0, 1 / sqrt(2)); // Ls -> Lt
- }
- if (_film->audio_channels() > 5) {
- map.set (dcp::RS, 1, 1 / sqrt(2)); // Rs -> Rt
- }
- }
+ _butler.reset(
+ new Butler(
+ _player,
+ Config::instance()->audio_mapping(_audio_channels),
+ _audio_channels,
+ bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24),
+ false,
+ true
+ )
+ );
- _butler.reset (new Butler(_player, map, _audio_channels, bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true));
if (!Config::instance()->sound() && !_audio.isStreamOpen()) {
_butler->disable_audio ();
}
@@ -626,6 +603,11 @@ FilmViewer::config_changed (Config::Property p)
}
#endif
+ if (p == Config::AUDIO_MAPPING) {
+ recreate_butler ();
+ return;
+ }
+
if (p != Config::SOUND && p != Config::SOUND_OUTPUT) {
return;
}