--- /dev/null
+/*
+ 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;
+}
+
+
*/
+#include "audio_backend.h"
#include "audio_mapping_view.h"
#include "check_box.h"
#include "config_dialog.h"
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);
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)
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
}
}
- RtAudio audio (DCPOMATIC_RTAUDIO_API);
-
map<int, wxString> apis;
apis[RtAudio::MACOSX_CORE] = _("CoreAudio");
apis[RtAudio::WINDOWS_ASIO] = _("ASIO");
*/
+#include "audio_backend.h"
#include "closed_captions_dialog.h"
#include "film_viewer.h"
#include "gl_video_view.h"
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()) {
DCPOMATIC_ASSERT(_player);
+ auto& audio = AudioBackend::instance()->rtaudio();
+
_butler = std::make_shared<Butler>(
_film,
*_player,
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);
FilmViewer::suspend ()
{
++_suspended;
- if (_audio.isStreamRunning()) {
- _audio.abortStream();
+
+ auto& audio = AudioBackend::instance()->rtaudio();
+ if (audio.isStreamRunning()) {
+ audio.abortStream();
}
}
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 (
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) {
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;
}
}
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&) {
}
++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 (
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();
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());
}
/* 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;
}
_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
-