summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lib/config.cc6
-rw-r--r--src/lib/config.h19
-rw-r--r--src/wx/config_dialog.cc110
-rw-r--r--src/wx/config_dialog.h5
-rw-r--r--src/wx/dcp_panel.cc2
-rw-r--r--src/wx/film_viewer.cc2
-rw-r--r--src/wx/wx_util.cc69
-rw-r--r--src/wx/wx_util.h46
8 files changed, 218 insertions, 41 deletions
diff --git a/src/lib/config.cc b/src/lib/config.cc
index 6984c4064..c51f729b8 100644
--- a/src/lib/config.cc
+++ b/src/lib/config.cc
@@ -152,6 +152,7 @@ Config::set_defaults ()
_nagged[i] = false;
}
_sound = true;
+ _sound_api = boost::none;
_sound_output = optional<string> ();
_last_kdm_write_type = KDM_WRITE_FLAT;
_last_dkdm_write_type = DKDM_WRITE_INTERNAL;
@@ -523,6 +524,7 @@ try
_jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
/* The variable was renamed but not the XML tag */
_sound = f.optional_bool_child("PreviewSound").get_value_or (true);
+ _sound_api = f.optional_string_child("PreviewSoundAPI");
_sound_output = f.optional_string_child("PreviewSoundOutput");
if (f.optional_string_child("CoverSheet")) {
_cover_sheet = f.optional_string_child("CoverSheet").get();
@@ -938,6 +940,10 @@ Config::write_config () const
}
/* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
root->add_child("PreviewSound")->add_child_text (_sound ? "1" : "0");
+ if (_sound_api) {
+ /* [XML:opt] PreviewSoundAPI Name of the audio API to use. */
+ root->add_child("PreviewSoundAPI")->add_child_text(_sound_api.get());
+ }
if (_sound_output) {
/* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
root->add_child("PreviewSoundOutput")->add_child_text (_sound_output.get());
diff --git a/src/lib/config.h b/src/lib/config.h
index 463eec47e..a91d58e64 100644
--- a/src/lib/config.h
+++ b/src/lib/config.h
@@ -86,6 +86,7 @@ public:
CINEMAS,
DKDM_RECIPIENTS,
SOUND,
+ SOUND_API,
SOUND_OUTPUT,
PLAYER_CONTENT_DIRECTORY,
PLAYER_PLAYLIST_DIRECTORY,
@@ -440,6 +441,10 @@ public:
return _cover_sheet;
}
+ boost::optional<std::string> sound_api() const {
+ return _sound_api;
+ }
+
boost::optional<std::string> sound_output () const {
return _sound_output;
}
@@ -906,6 +911,19 @@ public:
maybe_set (_sound, s, SOUND);
}
+ void set_sound_api(std::string api) {
+ maybe_set(_sound_api, api, SOUND_API);
+ }
+
+ void unset_sound_api() {
+ if (!_sound_api) {
+ return;
+ }
+
+ _sound_api = boost::none;
+ changed(SOUND_API);
+ }
+
void set_sound_output (std::string o) {
maybe_set (_sound_output, o, SOUND_OUTPUT);
}
@@ -1356,6 +1374,7 @@ private:
bool _jump_to_selected;
bool _nagged[NAG_COUNT];
bool _sound;
+ boost::optional<std::string> _sound_api;
/** name of a specific sound output stream to use, or empty to use the default */
boost::optional<std::string> _sound_output;
std::string _cover_sheet;
diff --git a/src/wx/config_dialog.cc b/src/wx/config_dialog.cc
index b23b4bae8..9e68d8cb3 100644
--- a/src/wx/config_dialog.cc
+++ b/src/wx/config_dialog.cc
@@ -23,6 +23,7 @@
#include "check_box.h"
#include "config_dialog.h"
#include "dcpomatic_button.h"
+#include "dcpomatic_choice.h"
#include "nag_dialog.h"
#include "static_text.h"
#include <dcp/file.h>
@@ -860,9 +861,11 @@ SoundPage::setup ()
_sound = new CheckBox (_panel, _("Play sound via"));
table->Add (_sound, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
- wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+ auto s = new wxBoxSizer (wxHORIZONTAL);
+ _sound_api = new Choice(_panel);
+ s->Add(_sound_api, 0);
_sound_output = new wxChoice (_panel, wxID_ANY);
- s->Add (_sound_output, 0);
+ s->Add(_sound_output, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
_sound_output_details = new wxStaticText (_panel, wxID_ANY, wxT(""));
s->Add (_sound_output_details, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, DCPOMATIC_SIZER_X_GAP);
table->Add (s, wxGBPosition(r, 1));
@@ -883,22 +886,40 @@ SoundPage::setup ()
font.SetPointSize (font.GetPointSize() - 1);
_sound_output_details->SetFont (font);
- RtAudio audio (DCPOMATIC_RTAUDIO_API);
+ for (auto const& api: audio_apis()) {
+ _sound_api->add(api.name(), new wxStringClientData(std_to_wx(api.id())));
+ }
+
+ update_sound_outputs();
+
+ _sound->bind(&SoundPage::sound_changed, this);
+ _sound_api->Bind(wxEVT_CHOICE, bind(&SoundPage::sound_api_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::update_sound_outputs()
+{
+ _sound_output->Clear();
+
+ RtAudio audio(id_to_audio_api(Config::instance()->sound_api()).rtaudio_id());
for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
try {
- auto dev = audio.getDeviceInfo (i);
+ auto dev = audio.getDeviceInfo(i);
if (dev.probed && dev.outputChannels > 0) {
- _sound_output->Append (std_to_wx (dev.name));
+ _sound_output->Append(std_to_wx(dev.name));
}
} catch (RtAudioError&) {
/* Something went wrong so let's just ignore that device */
}
}
- _sound->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));
+ if (!_sound_output->IsEmpty()) {
+ _sound_output->SetSelection(0);
+ }
}
void
@@ -913,16 +934,28 @@ SoundPage::map_changed (AudioMapping m)
Config::instance()->set_audio_mapping (m);
}
+
void
SoundPage::sound_changed ()
{
Config::instance()->set_sound (_sound->GetValue ());
}
+
+void
+SoundPage::sound_api_changed()
+{
+ auto config = Config::instance();
+ config->set_sound_api(index_to_audio_api(_sound_api->get().get_value_or(0)).id());
+ config->unset_sound_output();
+ update_sound_outputs();
+}
+
+
void
SoundPage::sound_output_changed ()
{
- RtAudio audio (DCPOMATIC_RTAUDIO_API);
+ RtAudio audio(id_to_audio_api(Config::instance()->sound_api()).rtaudio_id());
auto const so = get_sound_output();
string default_device;
try {
@@ -944,6 +977,28 @@ SoundPage::config_changed ()
checked_set (_sound, config->sound ());
+ auto const current_api = get_sound_api();
+ optional<string> configured_api;
+
+ if (config->sound_api()) {
+ configured_api = config->sound_api().get();
+ } else {
+ configured_api = audio_apis()[0].id();
+ }
+
+ if (current_api != *configured_api) {
+ unsigned int i = 0;
+ while (i < _sound_api->GetCount()) {
+ if (string_client_data(_sound_api->GetClientObject(i)) == std_to_wx(*configured_api)) {
+ _sound_api->SetSelection(i);
+ break;
+ }
+ ++i;
+ }
+ }
+
+ RtAudio audio(id_to_audio_api(configured_api).rtaudio_id());
+
auto const current_so = get_sound_output ();
optional<string> configured_so;
@@ -951,10 +1006,9 @@ SoundPage::config_changed ()
configured_so = config->sound_output().get();
} else {
/* No configured output means we should use the default */
- RtAudio audio (DCPOMATIC_RTAUDIO_API);
try {
configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name;
- } catch (RtAudioError&) {
+ } catch (RtAudioError& e) {
/* Probably no audio devices at all */
}
}
@@ -971,19 +1025,6 @@ SoundPage::config_changed ()
}
}
- RtAudio audio (DCPOMATIC_RTAUDIO_API);
-
- map<int, wxString> apis;
- apis[RtAudio::MACOSX_CORE] = _("CoreAudio");
- apis[RtAudio::WINDOWS_ASIO] = _("ASIO");
- apis[RtAudio::WINDOWS_DS] = _("Direct Sound");
- apis[RtAudio::WINDOWS_WASAPI] = _("WASAPI");
- apis[RtAudio::UNIX_JACK] = _("JACK");
- apis[RtAudio::LINUX_ALSA] = _("ALSA");
- apis[RtAudio::LINUX_PULSE] = _("PulseAudio");
- apis[RtAudio::LINUX_OSS] = _("OSS");
- apis[RtAudio::RTAUDIO_DUMMY] = _("Dummy");
-
int channels = 0;
if (configured_so) {
for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
@@ -998,9 +1039,7 @@ SoundPage::config_changed ()
}
}
- _sound_output_details->SetLabel (
- wxString::Format(_("%d channels on %s"), channels, apis[audio.getCurrentApi()])
- );
+ _sound_output_details->SetLabel(wxString::Format(_("%d channels"), channels));
_map->set (Config::instance()->audio_mapping(channels));
@@ -1025,13 +1064,26 @@ SoundPage::setup_sensitivity ()
_sound_output->Enable (_sound->GetValue());
}
+
+optional<string>
+SoundPage::get_sound_api()
+{
+ auto const sel = _sound_api->GetSelection();
+ if (sel == wxNOT_FOUND) {
+ return {};
+ }
+
+ return wx_to_std(selection_string_client_data(_sound_api));
+}
+
+
/** @return Currently-selected preview sound output in the dialogue */
optional<string>
SoundPage::get_sound_output ()
{
int const sel = _sound_output->GetSelection ();
if (sel == wxNOT_FOUND) {
- return optional<string> ();
+ return {};
}
return wx_to_std (_sound_output->GetString (sel));
diff --git a/src/wx/config_dialog.h b/src/wx/config_dialog.h
index e0d7f15b8..e74acb76f 100644
--- a/src/wx/config_dialog.h
+++ b/src/wx/config_dialog.h
@@ -50,6 +50,7 @@ LIBDCP_ENABLE_WARNINGS
class AudioMappingView;
class CheckBox;
+class Choice;
class Page : public wxPreferencesPage
@@ -206,14 +207,18 @@ private:
void setup () override;
void config_changed () override;
+ boost::optional<std::string> get_sound_api();
boost::optional<std::string> get_sound_output ();
void sound_changed ();
+ void sound_api_changed();
void sound_output_changed ();
void setup_sensitivity ();
void map_changed (AudioMapping m);
void reset_to_default ();
+ void update_sound_outputs();
CheckBox* _sound;
+ Choice* _sound_api;
wxChoice* _sound_output;
wxStaticText* _sound_output_details;
AudioMappingView* _map;
diff --git a/src/wx/dcp_panel.cc b/src/wx/dcp_panel.cc
index d8aa3ac46..c7528d942 100644
--- a/src/wx/dcp_panel.cc
+++ b/src/wx/dcp_panel.cc
@@ -967,7 +967,7 @@ DCPPanel::audio_processor_changed ()
return;
}
- auto const s = string_client_data(_audio_processor->GetClientObject(*_audio_processor->get()));
+ auto const s = selection_string_client_data(_audio_processor);
_film->set_audio_processor (AudioProcessor::from_id (s));
}
diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc
index fb02f0a0f..770177228 100644
--- a/src/wx/film_viewer.cc
+++ b/src/wx/film_viewer.cc
@@ -86,7 +86,7 @@ rtaudio_callback (void* out, void *, unsigned int frames, double, RtAudioStreamS
FilmViewer::FilmViewer (wxWindow* p)
- : _audio (DCPOMATIC_RTAUDIO_API)
+ : _audio(id_to_audio_api(Config::instance()->sound_api()).rtaudio_id())
, _closed_captions_dialog (new ClosedCaptionsDialog(p, this))
{
#if wxCHECK_VERSION(3, 1, 0)
diff --git a/src/wx/wx_util.cc b/src/wx/wx_util.cc
index 074f47d61..dc6bbfdc1 100644
--- a/src/wx/wx_util.cc
+++ b/src/wx/wx_util.cc
@@ -42,6 +42,7 @@ LIBDCP_DISABLE_WARNINGS
#include <wx/progdlg.h>
#include <wx/filepicker.h>
#include <wx/sizer.h>
+#include <RtAudio.h>
LIBDCP_ENABLE_WARNINGS
#include <boost/thread.hpp>
@@ -220,6 +221,13 @@ string_client_data (wxClientData* o)
}
+string
+selection_string_client_data(wxChoice* choice)
+{
+ return string_client_data(choice->GetClientObject(choice->GetSelection()));
+}
+
+
void
checked_set (FilePickerCtrl* widget, boost::filesystem::path value)
{
@@ -732,3 +740,64 @@ report_config_load_failure(wxWindow* parent, Config::LoadFailure what)
}
}
+
+static AudioAPI wasapi(_("WASAPI"), "wasapi", RtAudio::WINDOWS_WASAPI);
+static AudioAPI asio(_("ASIO"), "asio", RtAudio::WINDOWS_ASIO);
+static AudioAPI pulse(_("PulseAudio"), "pulse", RtAudio::LINUX_PULSE);
+static AudioAPI alsa(_("ALSA"), "alsa", RtAudio::LINUX_ALSA);
+static AudioAPI core(_("CoreAudio"), "core", RtAudio::MACOSX_CORE);
+
+
+vector<AudioAPI>
+audio_apis()
+{
+#ifdef DCPOMATIC_WINDOWS
+ return { wasapi, asio };
+ };
+#endif
+#ifdef DCPOMATIC_LINUX
+ return { pulse, alsa };
+#endif
+#ifdef DCPOMATIC_OSX
+ return { core };
+#endif
+}
+
+
+AudioAPI
+id_to_audio_api(string id)
+{
+ const auto apis = audio_apis();
+ auto iter = std::find_if(apis.begin(), apis.end(), [&id](AudioAPI const& api) { return api.id() == id; });
+ if (iter == apis.end()) {
+ return apis[0];
+ }
+
+ return *iter;
+}
+
+
+AudioAPI
+id_to_audio_api(optional<string> id)
+{
+ const auto apis = audio_apis();
+ if (!id) {
+ return apis[0];
+ }
+
+ return id_to_audio_api(*id);
+}
+
+
+AudioAPI
+index_to_audio_api(int index)
+{
+ const auto apis = audio_apis();
+ if (index < 0 || index >= static_cast<int>(apis.size())) {
+ return apis[0];
+ }
+
+ return apis[index];
+}
+
+
diff --git a/src/wx/wx_util.h b/src/wx/wx_util.h
index 50fb7268f..caf814a7d 100644
--- a/src/wx/wx_util.h
+++ b/src/wx/wx_util.h
@@ -34,6 +34,7 @@
LIBDCP_DISABLE_WARNINGS
#include <wx/gbsizer.h>
#include <wx/wx.h>
+#include <RtAudio.h>
LIBDCP_ENABLE_WARNINGS
#include <boost/signals2.hpp>
#include <boost/thread.hpp>
@@ -81,16 +82,6 @@ class PasswordEntry;
#define DCPOMATIC_BUTTON_STACK_GAP 0
#endif
-#ifdef DCPOMATIC_LINUX
-#define DCPOMATIC_RTAUDIO_API RtAudio::LINUX_PULSE
-#endif
-#ifdef DCPOMATIC_WINDOWS
-#define DCPOMATIC_RTAUDIO_API RtAudio::UNSPECIFIED
-#endif
-#ifdef DCPOMATIC_OSX
-#define DCPOMATIC_RTAUDIO_API RtAudio::MACOSX_CORE
-#endif
-
/** i18n macro to support strings like Context|String
* so that `String' can be translated to different things
@@ -112,6 +103,7 @@ extern wxString std_to_wx (std::string);
extern void dcpomatic_setup_i18n ();
extern wxString context_translation (wxString);
extern std::string string_client_data (wxClientData* o);
+extern std::string selection_string_client_data(wxChoice* choice);
extern wxString time_to_timecode (dcpomatic::DCPTime t, double fps);
extern void setup_audio_channels_choice (wxChoice* choice, int minimum);
extern wxSplashScreen* maybe_show_splash ();
@@ -142,6 +134,40 @@ struct Offset
extern int get_offsets (std::vector<Offset>& offsets);
+class AudioAPI
+{
+public:
+ AudioAPI(wxString name, std::string id, RtAudio::Api rtaudio_id)
+ : _name(name)
+ , _id(id)
+ , _rtaudio_id(rtaudio_id)
+ {}
+
+ wxString name() const {
+ return _name;
+ }
+
+ std::string id() const {
+ return _id;
+ }
+
+ RtAudio::Api rtaudio_id() const {
+ return _rtaudio_id;
+ }
+
+private:
+ wxString _name;
+ std::string _id;
+ RtAudio::Api _rtaudio_id;
+};
+
+
+std::vector<AudioAPI> audio_apis();
+AudioAPI id_to_audio_api(std::string id);
+AudioAPI id_to_audio_api(boost::optional<std::string> id);
+AudioAPI index_to_audio_api(int index);
+
+
extern void checked_set (FilePickerCtrl* widget, boost::filesystem::path value);
extern void checked_set (wxDirPickerCtrl* widget, boost::filesystem::path value);
extern void checked_set (wxSpinCtrl* widget, int value);