using std::ifstream;
using std::string;
using std::list;
+using std::min;
using std::max;
using std::remove;
using std::exception;
_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}";
_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}");
/* [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());
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);
+}
/*
- 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.
#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>
PLAYER_DEBUG_LOG,
HISTORY,
SHOW_EXPERIMENTAL_AUDIO_PROCESSORS,
+ AUDIO_MAPPING,
#ifdef DCPOMATIC_VARIANT_SWAROOP
PLAYER_BACKGROUND_IMAGE,
#endif
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;
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);
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;
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"));
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
}
}
- if (group) {
+ if (group && !group->IsEmpty()) {
return wxString::Format ("%s/%s", group->data(), std_to_wx(_input_channels[n]).data());
}
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
);
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);
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;
/// 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);
#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;
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);
}
}
- _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
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 ();
}
#include <boost/foreach.hpp>
#include <iostream>
+class AudioMappingView;
+
class Page
{
public:
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
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 ();
}
}
#endif
+ if (p == Config::AUDIO_MAPPING) {
+ recreate_butler ();
+ return;
+ }
+
if (p != Config::SOUND && p != Config::SOUND_OUTPUT) {
return;
}