diff options
| author | Carl Hetherington <cth@carlh.net> | 2024-07-25 14:46:44 +0200 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2024-07-26 11:39:59 +0200 |
| commit | 051bac905d3ab8776c804e0e21fecd4c49927549 (patch) | |
| tree | 07fef88f1fa0c53fca71098b63caec8589b0c9cb | |
| parent | c7d5e56219f39a6304be9644cc577d89be16b7de (diff) | |
Stop instantiating RtAudio all over the place
and instead just have a singleton. On Windows I saw a situation where
the first instantiation would use ASIO and the second WASAPI, causing
all kinds of confusion.
| -rw-r--r-- | src/wx/audio_backend.cc | 77 | ||||
| -rw-r--r-- | src/wx/audio_backend.h | 58 | ||||
| -rw-r--r-- | src/wx/config_dialog.cc | 12 | ||||
| -rw-r--r-- | src/wx/film_viewer.cc | 116 | ||||
| -rw-r--r-- | src/wx/film_viewer.h | 8 | ||||
| -rw-r--r-- | src/wx/wscript | 1 | ||||
| -rw-r--r-- | src/wx/wx_util.h | 10 |
7 files changed, 198 insertions, 84 deletions
diff --git a/src/wx/audio_backend.cc b/src/wx/audio_backend.cc new file mode 100644 index 000000000..b77d2cdcd --- /dev/null +++ b/src/wx/audio_backend.cc @@ -0,0 +1,77 @@ +/* + Copyright (C) 2024 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include "audio_backend.h" + + +AudioBackend* AudioBackend::_instance = nullptr; + + +#ifdef DCPOMATIC_LINUX +auto constexpr api = RtAudio::LINUX_PULSE; +#endif +#ifdef DCPOMATIC_WINDOWS +auto constexpr api = RtAudio::UNSPECIFIED; +#endif +#ifdef DCPOMATIC_OSX +auto constexpr api = RtAudio::MACOSX_CORE; +#endif + + +AudioBackend::AudioBackend() +#if (RTAUDIO_VERSION_MAJOR >= 6) + : _rtaudio(api, boost::bind(&AudioBackend::rtaudio_error_callback, this, _2)) +#else + : _rtaudio(api) +#endif +{ + +} + + +#if (RTAUDIO_VERSION_MAJOR >= 6) +void +AudioBackend::rtaudio_error_callback(string const& error) +{ + boost::mutex::scoped_lock lm(_last_rtaudio_error_mutex); + _last_rtaudio_error = error; +} + +string +AudioBackend::last_rtaudio_error() const +{ + boost::mutex::scoped_lock lm(_last_rtaudio_error_mutex); + return _last_rtaudio_error; +} +#endif + + +AudioBackend* +AudioBackend::instance() +{ + if (!_instance) { + _instance = new AudioBackend(); + } + + return _instance; +} + + diff --git a/src/wx/audio_backend.h b/src/wx/audio_backend.h new file mode 100644 index 000000000..cf1bbad05 --- /dev/null +++ b/src/wx/audio_backend.h @@ -0,0 +1,58 @@ +/* + Copyright (C) 2024 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include <dcp/warnings.h> +LIBDCP_DISABLE_WARNINGS +#include <RtAudio.h> +LIBDCP_ENABLE_WARNINGS + + + +class AudioBackend +{ +public: + AudioBackend(); + + AudioBackend(AudioBackend const&) = delete; + AudioBackend& operator=(AudioBackend const&) = delete; + + RtAudio& rtaudio() { + return _rtaudio; + } + +#if (RTAUDIO_VERSION_MAJOR >= 6) + std::string last_rtaudio_error() const; +#endif + + static AudioBackend* instance(); + +private: + static AudioBackend* _instance; + + RtAudio _rtaudio; + +#if (RTAUDIO_VERSION_MAJOR >= 6) + void rtaudio_error_callback(std::string const& error); + mutable boost::mutex _last_rtaudio_error_mutex; + std::string _last_rtaudio_error; +#endif +}; + diff --git a/src/wx/config_dialog.cc b/src/wx/config_dialog.cc index b4adc855e..bbe2278ac 100644 --- a/src/wx/config_dialog.cc +++ b/src/wx/config_dialog.cc @@ -19,6 +19,7 @@ */ +#include "audio_backend.h" #include "audio_mapping_view.h" #include "check_box.h" #include "config_dialog.h" @@ -880,7 +881,8 @@ SoundPage::setup () font.SetPointSize (font.GetPointSize() - 1); _sound_output_details->SetFont (font); - RtAudio audio (DCPOMATIC_RTAUDIO_API); + auto& audio = AudioBackend::instance()->rtaudio(); + #if (RTAUDIO_VERSION_MAJOR >= 6) for (auto device_id: audio.getDeviceIds()) { auto dev = audio.getDeviceInfo(device_id); @@ -928,7 +930,8 @@ SoundPage::sound_changed () void SoundPage::sound_output_changed () { - RtAudio audio (DCPOMATIC_RTAUDIO_API); + auto& audio = AudioBackend::instance()->rtaudio(); + auto const so = get_sound_output(); string default_device; #if (RTAUDIO_VERSION_MAJOR >= 6) @@ -957,11 +960,12 @@ SoundPage::config_changed () auto const current_so = get_sound_output (); optional<string> configured_so; + auto& audio = AudioBackend::instance()->rtaudio(); + if (config->sound_output()) { configured_so = config->sound_output().get(); } else { /* No configured output means we should use the default */ - RtAudio audio (DCPOMATIC_RTAUDIO_API); #if (RTAUDIO_VERSION_MAJOR >= 6) configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name; #else @@ -985,8 +989,6 @@ SoundPage::config_changed () } } - RtAudio audio (DCPOMATIC_RTAUDIO_API); - map<int, wxString> apis; apis[RtAudio::MACOSX_CORE] = _("CoreAudio"); apis[RtAudio::WINDOWS_ASIO] = _("ASIO"); diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index b6f8096e8..f83e83c68 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -24,6 +24,7 @@ */ +#include "audio_backend.h" #include "closed_captions_dialog.h" #include "film_viewer.h" #include "gl_video_view.h" @@ -86,12 +87,7 @@ rtaudio_callback (void* out, void *, unsigned int frames, double, RtAudioStreamS FilmViewer::FilmViewer (wxWindow* p) -#if (RTAUDIO_VERSION_MAJOR >= 6) - : _audio(DCPOMATIC_RTAUDIO_API, boost::bind(&FilmViewer::rtaudio_error_callback, this, _2)) -#else - : _audio (DCPOMATIC_RTAUDIO_API) -#endif - , _closed_captions_dialog (new ClosedCaptionsDialog(p, this)) + : _closed_captions_dialog (new ClosedCaptionsDialog(p, this)) { #if wxCHECK_VERSION(3, 1, 0) switch (Config::instance()->video_view_type()) { @@ -242,6 +238,8 @@ FilmViewer::create_butler() DCPOMATIC_ASSERT(_player); + auto& audio = AudioBackend::instance()->rtaudio(); + _butler = std::make_shared<Butler>( _film, *_player, @@ -252,7 +250,7 @@ FilmViewer::create_butler() j2k_gl_optimised ? Image::Alignment::COMPACT : Image::Alignment::PADDED, true, j2k_gl_optimised, - (Config::instance()->sound() && _audio.isStreamOpen()) ? Butler::Audio::ENABLED : Butler::Audio::DISABLED + (Config::instance()->sound() && audio.isStreamOpen()) ? Butler::Audio::ENABLED : Butler::Audio::DISABLED ); _closed_captions_dialog->set_butler (_butler); @@ -344,8 +342,10 @@ void FilmViewer::suspend () { ++_suspended; - if (_audio.isStreamRunning()) { - _audio.abortStream(); + + auto& audio = AudioBackend::instance()->rtaudio(); + if (audio.isStreamRunning()) { + audio.abortStream(); } } @@ -353,19 +353,21 @@ FilmViewer::suspend () void FilmViewer::start_audio_stream_if_open () { - if (_audio.isStreamOpen()) { - _audio.setStreamTime (_video_view->position().seconds()); + auto& audio = AudioBackend::instance()->rtaudio(); + + if (audio.isStreamOpen()) { + audio.setStreamTime(_video_view->position().seconds()); #if (RTAUDIO_VERSION_MAJOR >= 6) - if (_audio.startStream() != RTAUDIO_NO_ERROR) { + if (audio.startStream() != RTAUDIO_NO_ERROR) { _audio_channels = 0; error_dialog( _video_view->get(), - _("There was a problem starting audio playback. Please try another audio output device in Preferences."), std_to_wx(last_rtaudio_error()) + _("There was a problem starting audio playback. Please try another audio output device in Preferences."), std_to_wx(audio.last_rtaudio_error()) ); } #else try { - _audio.startStream (); + audio.startStream (); } catch (RtAudioError& e) { _audio_channels = 0; error_dialog ( @@ -428,9 +430,11 @@ FilmViewer::start () bool FilmViewer::stop () { - if (_audio.isStreamRunning()) { + auto& audio = AudioBackend::instance()->rtaudio(); + + if (audio.isStreamRunning()) { /* stop stream and discard any remaining queued samples */ - _audio.abortStream (); + audio.abortStream(); } if (!_playing) { @@ -618,16 +622,18 @@ FilmViewer::config_changed (Config::Property p) return; } - if (_audio.isStreamOpen ()) { - _audio.closeStream (); + auto& audio = AudioBackend::instance()->rtaudio(); + + if (audio.isStreamOpen()) { + audio.closeStream(); } - if (Config::instance()->sound() && _audio.getDeviceCount() > 0) { + if (Config::instance()->sound() && audio.getDeviceCount() > 0) { optional<unsigned int> chosen_device_id; #if (RTAUDIO_VERSION_MAJOR >= 6) if (Config::instance()->sound_output()) { - for (auto device_id: _audio.getDeviceIds()) { - if (_audio.getDeviceInfo(device_id).name == Config::instance()->sound_output().get()) { + for (auto device_id: audio.getDeviceIds()) { + if (audio.getDeviceInfo(device_id).name == Config::instance()->sound_output().get()) { chosen_device_id = device_id; break; } @@ -635,26 +641,26 @@ FilmViewer::config_changed (Config::Property p) } if (!chosen_device_id) { - chosen_device_id = _audio.getDefaultOutputDevice(); + chosen_device_id = audio.getDefaultOutputDevice(); } - _audio_channels = _audio.getDeviceInfo(*chosen_device_id).outputChannels; + _audio_channels = audio.getDeviceInfo(*chosen_device_id).outputChannels; RtAudio::StreamParameters sp; sp.deviceId = *chosen_device_id; sp.nChannels = _audio_channels; sp.firstChannel = 0; - if (_audio.openStream(&sp, 0, RTAUDIO_FLOAT32, 48000, &_audio_block_size, &rtaudio_callback, this) != RTAUDIO_NO_ERROR) { + if (audio.openStream(&sp, 0, RTAUDIO_FLOAT32, 48000, &_audio_block_size, &rtaudio_callback, this) != RTAUDIO_NO_ERROR) { _audio_channels = 0; error_dialog( _video_view->get(), - _("Could not set up audio output. There will be no audio during the preview."), std_to_wx(last_rtaudio_error()) + _("Could not set up audio output. There will be no audio during the preview."), std_to_wx(audio.last_rtaudio_error()) ); } #else unsigned int st = 0; if (Config::instance()->sound_output()) { - while (st < _audio.getDeviceCount()) { + while (st < audio.getDeviceCount()) { try { - if (_audio.getDeviceInfo(st).name == Config::instance()->sound_output().get()) { + if (audio.getDeviceInfo(st).name == Config::instance()->sound_output().get()) { break; } } catch (RtAudioError&) { @@ -662,28 +668,28 @@ FilmViewer::config_changed (Config::Property p) } ++st; } - if (st == _audio.getDeviceCount()) { + if (st == audio.getDeviceCount()) { try { - st = _audio.getDefaultOutputDevice(); + st = audio.getDefaultOutputDevice(); } catch (RtAudioError&) { /* Something went wrong with that device so we don't want to use it anyway */ } } } else { try { - st = _audio.getDefaultOutputDevice(); + st = audio.getDefaultOutputDevice(); } catch (RtAudioError&) { /* Something went wrong with that device so we don't want to use it anyway */ } } try { - _audio_channels = _audio.getDeviceInfo(st).outputChannels; + _audio_channels = audio.getDeviceInfo(st).outputChannels; RtAudio::StreamParameters sp; sp.deviceId = st; sp.nChannels = _audio_channels; sp.firstChannel = 0; - _audio.openStream (&sp, 0, RTAUDIO_FLOAT32, 48000, &_audio_block_size, &rtaudio_callback, this); + audio.openStream(&sp, 0, RTAUDIO_FLOAT32, 48000, &_audio_block_size, &rtaudio_callback, this); } catch (RtAudioError& e) { _audio_channels = 0; error_dialog ( @@ -704,8 +710,10 @@ FilmViewer::config_changed (Config::Property p) DCPTime FilmViewer::uncorrected_time () const { - if (_audio.isStreamRunning()) { - return DCPTime::from_seconds (const_cast<RtAudio*>(&_audio)->getStreamTime()); + auto& audio = AudioBackend::instance()->rtaudio(); + + if (audio.isStreamRunning()) { + return DCPTime::from_seconds(audio.getStreamTime()); } return _video_view->position(); @@ -715,11 +723,13 @@ FilmViewer::uncorrected_time () const optional<DCPTime> FilmViewer::audio_time () const { - if (!_audio.isStreamRunning()) { + auto& audio = AudioBackend::instance()->rtaudio(); + + if (!audio.isStreamRunning()) { return {}; } - return DCPTime::from_seconds (const_cast<RtAudio*>(&_audio)->getStreamTime ()) - + return DCPTime::from_seconds(audio.getStreamTime()) - DCPTime::from_frames (average_latency(), _film->audio_frame_rate()); } @@ -743,13 +753,15 @@ FilmViewer::audio_callback (void* out_p, unsigned int frames) /* The audio we just got was (very) late; drop it and get some more. */ } - boost::mutex::scoped_lock lm (_latency_history_mutex, boost::try_to_lock); - if (lm) { - _latency_history.push_back (_audio.getStreamLatency ()); - if (_latency_history.size() > static_cast<size_t> (_latency_history_count)) { - _latency_history.pop_front (); - } - } + auto& audio = AudioBackend::instance()->rtaudio(); + + boost::mutex::scoped_lock lm (_latency_history_mutex, boost::try_to_lock); + if (lm) { + _latency_history.push_back(audio.getStreamLatency()); + if (_latency_history.size() > static_cast<size_t> (_latency_history_count)) { + _latency_history.pop_front (); + } + } return 0; } @@ -898,21 +910,3 @@ FilmViewer::unset_crop_guess () _video_view->update (); } - -#if (RTAUDIO_VERSION_MAJOR >= 6) -void -FilmViewer::rtaudio_error_callback(string const& error) -{ - boost::mutex::scoped_lock lm(_last_rtaudio_error_mutex); - _last_rtaudio_error = error; -} - - -string -FilmViewer::last_rtaudio_error() const -{ - boost::mutex::scoped_lock lm(_last_rtaudio_error_mutex); - return _last_rtaudio_error; -} -#endif - diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index 5824f8baa..a0aac62dd 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -171,13 +171,6 @@ private: void ui_finished (); void start_audio_stream_if_open (); -#if (RTAUDIO_VERSION_MAJOR >= 6) - void rtaudio_error_callback(std::string const& error); - mutable boost::mutex _last_rtaudio_error_mutex; - std::string _last_rtaudio_error; - std::string last_rtaudio_error() const; -#endif - dcpomatic::DCPTime uncorrected_time () const; Frame average_latency () const; @@ -190,7 +183,6 @@ private: bool _coalesce_player_changes = false; std::vector<int> _pending_player_changes; - RtAudio _audio; int _audio_channels = 0; unsigned int _audio_block_size = 1024; bool _playing = false; diff --git a/src/wx/wscript b/src/wx/wscript index a6eefa69f..f52f71afc 100644 --- a/src/wx/wscript +++ b/src/wx/wscript @@ -26,6 +26,7 @@ import i18n sources = """ about_dialog.cc + audio_backend.cc audio_dialog.cc audio_gain_dialog.cc audio_mapping_view.cc diff --git a/src/wx/wx_util.h b/src/wx/wx_util.h index 66b01640c..c85af1d4f 100644 --- a/src/wx/wx_util.h +++ b/src/wx/wx_util.h @@ -86,16 +86,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 |
