From e71270f0f02f469b171993062c8e8d69f35a9cf6 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Mon, 24 Mar 2014 07:54:22 -0700 Subject: [PATCH] Refactor the rest of the factories --- build/Aegisub/Aegisub.vcxproj | 8 - build/Aegisub/Aegisub.vcxproj.filters | 24 --- src/Makefile | 1 - src/audio_player.cpp | 87 ++++++---- src/audio_player_alsa.cpp | 204 +++++++++++------------ src/audio_player_alsa.h | 64 ------- src/audio_player_dsound.cpp | 118 +++++++------ src/audio_player_dsound.h | 92 ---------- src/audio_player_dsound2.cpp | 83 +++++++-- src/audio_player_dsound2.h | 99 ----------- src/audio_player_openal.cpp | 88 +++++++++- src/audio_player_openal.h | 111 ------------ src/audio_player_oss.cpp | 145 +++++++++++----- src/audio_player_oss.h | 115 ------------- src/audio_player_portaudio.cpp | 11 +- src/audio_player_pulse.cpp | 72 +++++++- src/audio_player_pulse.h | 98 ----------- src/factory_manager.h | 56 ------- src/include/aegisub/audio_player.h | 10 +- src/include/aegisub/spellchecker.h | 6 +- src/include/aegisub/subtitles_provider.h | 14 +- src/main.cpp | 6 +- src/plugin_manager.cpp | 50 ------ src/plugin_manager.h | 30 ---- src/spellchecker.cpp | 64 ++----- src/subtitles_provider.cpp | 94 +++++------ src/subtitles_provider_csri.cpp | 28 +++- src/subtitles_provider_csri.h | 73 ++------ src/subtitles_provider_libass.cpp | 65 +++++--- src/subtitles_provider_libass.h | 62 ++----- 30 files changed, 738 insertions(+), 1240 deletions(-) delete mode 100644 src/audio_player_alsa.h delete mode 100644 src/audio_player_dsound.h delete mode 100644 src/audio_player_dsound2.h delete mode 100644 src/audio_player_openal.h delete mode 100644 src/audio_player_oss.h delete mode 100644 src/audio_player_pulse.h delete mode 100644 src/plugin_manager.cpp delete mode 100644 src/plugin_manager.h diff --git a/build/Aegisub/Aegisub.vcxproj b/build/Aegisub/Aegisub.vcxproj index c504b4202..0b1dcd629 100644 --- a/build/Aegisub/Aegisub.vcxproj +++ b/build/Aegisub/Aegisub.vcxproj @@ -110,13 +110,7 @@ - - - - - - @@ -197,7 +191,6 @@ - @@ -391,7 +384,6 @@ - diff --git a/build/Aegisub/Aegisub.vcxproj.filters b/build/Aegisub/Aegisub.vcxproj.filters index 02e122708..6214b4aa1 100644 --- a/build/Aegisub/Aegisub.vcxproj.filters +++ b/build/Aegisub/Aegisub.vcxproj.filters @@ -213,24 +213,6 @@ Config - - Audio\Players - - - Audio\Players - - - Audio\Players - - - Audio\Players - - - Audio\Players - - - Audio\Players - Audio\Players @@ -528,9 +510,6 @@ Video\Providers - - Utilities - Video\Visual tools @@ -1055,9 +1034,6 @@ Main UI - - Utilities - Video\Visual tools diff --git a/src/Makefile b/src/Makefile index 292ee01e4..f6a38b2d1 100644 --- a/src/Makefile +++ b/src/Makefile @@ -203,7 +203,6 @@ SRC += \ mkv_wrap.cpp \ pen.cpp \ persist_location.cpp \ - plugin_manager.cpp \ preferences.cpp \ preferences_base.cpp \ resolution_resampler.cpp \ diff --git a/src/audio_player.cpp b/src/audio_player.cpp index dfe8e8561..9c1b6aaa0 100644 --- a/src/audio_player.cpp +++ b/src/audio_player.cpp @@ -35,56 +35,75 @@ #include "config.h" #include "include/aegisub/audio_player.h" -#include "audio_player_alsa.h" -#include "audio_player_dsound.h" -#include "audio_player_dsound2.h" -#include "audio_player_openal.h" -#include "audio_player_oss.h" -#include "audio_player_portaudio.h" -#include "audio_player_pulse.h" #include "audio_controller.h" +#include "factory_manager.h" #include "options.h" +#include + AudioPlayer::AudioPlayer(AudioProvider *provider) : provider(provider) { } +std::unique_ptr CreateAlsaPlayer(AudioProvider *providers); +std::unique_ptr CreateDirectSoundPlayer(AudioProvider *providers); +std::unique_ptr CreateDirectSound2Player(AudioProvider *providers); +std::unique_ptr CreateOpenALPlayer(AudioProvider *providers); +std::unique_ptr CreatePortAudioPlayer(AudioProvider *providers); +std::unique_ptr CreatePulseAudioPlayer(AudioProvider *providers); +std::unique_ptr CreateOSSPlayer(AudioProvider *providers); + +namespace { + struct factory { + const char *name; + std::unique_ptr (*create)(AudioProvider *); + bool hidden; + }; + + const factory factories[] = { +#ifdef WITH_ALSA + {"ALSA", CreateAlsaPlayer, false}, +#endif +#ifdef WITH_DIRECTSOUND + {"DirectSound-old", CreateDirectSoundPlayer, false}, + {"DirectSound", CreateDirectSound2Player, false}, +#endif +#ifdef WITH_OPENAL + {"OpenAL", CreateOpenALPlayer, false}, +#endif +#ifdef WITH_PORTAUDIO + {"PortAudio", CreatePortAudioPlayer, false}, +#endif +#ifdef WITH_LIBPULSE + {"PulseAudio", CreatePulseAudioPlayer, false}, +#endif +#ifdef WITH_OSS + {"OSS", CreateOSSPlayer, false}, +#endif + }; +} + +std::vector AudioPlayerFactory::GetClasses() { + return ::GetClasses(boost::make_iterator_range(std::begin(factories), std::end(factories))); +} + std::unique_ptr AudioPlayerFactory::GetAudioPlayer(AudioProvider *provider) { - std::vector list = GetClasses(OPT_GET("Audio/Player")->GetString()); - if (list.empty()) throw agi::NoAudioPlayersError("No audio players are available.", nullptr); + if (std::distance(std::begin(factories), std::end(factories)) == 0) + throw agi::NoAudioPlayersError("No audio players are available.", nullptr); + + auto preferred = OPT_GET("Audio/Player")->GetString(); + auto sorted = GetSorted(boost::make_iterator_range(std::begin(factories), std::end(factories)), preferred); std::string error; - for (auto const& factory_name : list) { + for (auto factory : sorted) { try { - return Create(factory_name, provider); + return factory->create(provider); } catch (agi::AudioPlayerOpenError const& err) { - error += factory_name + " factory: " + err.GetChainedMessage() + "\n"; + error += std::string(factory->name) + " factory: " + err.GetChainedMessage() + "\n"; } } throw agi::AudioPlayerOpenError(error, nullptr); } - -void AudioPlayerFactory::RegisterProviders() { -#ifdef WITH_ALSA - Register("ALSA"); -#endif -#ifdef WITH_DIRECTSOUND - Register("DirectSound-old"); - Register("DirectSound"); -#endif -#ifdef WITH_OPENAL - Register("OpenAL"); -#endif -#ifdef WITH_PORTAUDIO - Register("PortAudio"); -#endif -#ifdef WITH_LIBPULSE - Register("PulseAudio"); -#endif -#ifdef WITH_OSS - Register("OSS"); -#endif -} diff --git a/src/audio_player_alsa.cpp b/src/audio_player_alsa.cpp index 7621a34f1..61f009f07 100644 --- a/src/audio_player_alsa.cpp +++ b/src/audio_player_alsa.cpp @@ -35,8 +35,7 @@ #include "config.h" #ifdef WITH_ALSA - -#include "audio_player_alsa.h" +#include "include/aegisub/audio_player.h" #include "audio_controller.h" #include "include/aegisub/audio_provider.h" @@ -48,15 +47,73 @@ #include #include - +#include #include +#include + +namespace { +struct PlaybackState { + pthread_mutex_t mutex; + pthread_cond_t cond; + + bool playing = false; + bool alive = false; + + bool signal_start = false; + bool signal_stop = false; + bool signal_close = false; + bool signal_volume = false; + + double volume = 1.0; + int64_t start_position = 0; + int64_t end_position = 0; + + AudioProvider *provider = nullptr; + std::string device_name; + + int64_t last_position = 0; + timespec last_position_time; + + PlaybackState() + { + pthread_mutex_init(&mutex, 0); + pthread_cond_init(&cond, 0); + memset(&last_position_time, 0, sizeof last_position_time); + } + + ~PlaybackState() + { + pthread_cond_destroy(&cond); + pthread_mutex_destroy(&mutex); + } +}; + +class AlsaPlayer final : public AudioPlayer { + PlaybackState ps; + pthread_t thread; + +public: + AlsaPlayer(AudioProvider *provider); + ~AlsaPlayer(); + + void Play(int64_t start, int64_t count); + void Stop(); + bool IsPlaying(); + + int64_t GetStartPosition(); + int64_t GetEndPosition(); + int64_t GetCurrentPosition(); + void SetEndPosition(int64_t pos); + void SetCurrentPosition(int64_t pos); + + void SetVolume(double vol); +}; class PthreadMutexLocker { pthread_mutex_t &mutex; - PthreadMutexLocker(const PthreadMutexLocker &); // uncopyable - PthreadMutexLocker(); // no default - PthreadMutexLocker& operator=(PthreadMutexLocker const&); + PthreadMutexLocker(const PthreadMutexLocker &) = delete; + PthreadMutexLocker& operator=(PthreadMutexLocker const&) = delete; public: explicit PthreadMutexLocker(pthread_mutex_t &mutex) : mutex(mutex) @@ -83,78 +140,22 @@ public: } }; - class ScopedAliveFlag { bool &flag; - ScopedAliveFlag(const ScopedAliveFlag &); // uncopyable - ScopedAliveFlag(); // no default - ScopedAliveFlag& operator=(ScopedAliveFlag const&); + ScopedAliveFlag(const ScopedAliveFlag &) = delete; + ScopedAliveFlag& operator=(ScopedAliveFlag const&) = delete; public: explicit ScopedAliveFlag(bool &var) : flag(var) { flag = true; } ~ScopedAliveFlag() { flag = false; } }; - -struct PlaybackState { - pthread_mutex_t mutex; - pthread_cond_t cond; - - bool playing; - bool alive; - - bool signal_start; - bool signal_stop; - bool signal_close; - bool signal_volume; - - double volume; - int64_t start_position; - int64_t end_position; - - AudioProvider *provider; - std::string device_name; - - int64_t last_position; - timespec last_position_time; - - PlaybackState() - { - pthread_mutex_init(&mutex, 0); - pthread_cond_init(&cond, 0); - - Reset(); - volume = 1.0; - } - - ~PlaybackState() - { - pthread_cond_destroy(&cond); - pthread_mutex_destroy(&mutex); - } - - void Reset() - { - playing = false; - alive = false; - signal_start = false; - signal_stop = false; - signal_close = false; - signal_volume = false; - start_position = 0; - end_position = 0; - last_position = 0; - memset(&last_position_time, 0, sizeof last_position_time); - provider = 0; - } -}; - void *playback_thread(void *arg) { // This is exception-free territory! // Return a pointer to a static string constant describing the error, or 0 on no error - PlaybackState &ps = *(PlaybackState*)arg; + auto &ps = *static_cast(arg); PthreadMutexLocker ml(ps.mutex); ScopedAliveFlag alive_flag(ps.alive); @@ -363,73 +364,66 @@ do_setup: return 0; } - AlsaPlayer::AlsaPlayer(AudioProvider *provider) : AudioPlayer(provider) -, ps(agi::util::make_unique()) { - ps->provider = provider; + ps.provider = provider; - ps->device_name = OPT_GET("Player/Audio/ALSA/Device")->GetString(); + ps.device_name = OPT_GET("Player/Audio/ALSA/Device")->GetString(); - if (pthread_create(&thread, 0, &playback_thread, ps.get()) != 0) + if (pthread_create(&thread, 0, &playback_thread, &ps) != 0) throw agi::AudioPlayerOpenError("AlsaPlayer: Creating the playback thread failed", 0); } - AlsaPlayer::~AlsaPlayer() { { - PthreadMutexLocker ml(ps->mutex); - ps->signal_stop = true; - ps->signal_close = true; + PthreadMutexLocker ml(ps.mutex); + ps.signal_stop = true; + ps.signal_close = true; LOG_D("audio/player/alsa") << "close stream, stop+close signal"; - pthread_cond_signal(&ps->cond); + pthread_cond_signal(&ps.cond); } pthread_join(thread, 0); // FIXME: check for errors } - void AlsaPlayer::Play(int64_t start, int64_t count) { - PthreadMutexLocker ml(ps->mutex); - ps->signal_start = true; - ps->signal_stop = true; // make sure to stop any ongoing playback first - ps->start_position = start; - ps->end_position = start + count; - pthread_cond_signal(&ps->cond); + PthreadMutexLocker ml(ps.mutex); + ps.signal_start = true; + ps.signal_stop = true; // make sure to stop any ongoing playback first + ps.start_position = start; + ps.end_position = start + count; + pthread_cond_signal(&ps.cond); } - void AlsaPlayer::Stop() { - PthreadMutexLocker ml(ps->mutex); - ps->signal_stop = true; + PthreadMutexLocker ml(ps.mutex); + ps.signal_stop = true; LOG_D("audio/player/alsa") << "stop stream, stop signal"; - pthread_cond_signal(&ps->cond); + pthread_cond_signal(&ps.cond); } bool AlsaPlayer::IsPlaying() { - PthreadMutexLocker ml(ps->mutex); - return ps->playing; + PthreadMutexLocker ml(ps.mutex); + return ps.playing; } - void AlsaPlayer::SetEndPosition(int64_t pos) { - PthreadMutexLocker ml(ps->mutex); - ps->end_position = pos; + PthreadMutexLocker ml(ps.mutex); + ps.end_position = pos; } int64_t AlsaPlayer::GetEndPosition() { - PthreadMutexLocker ml(ps->mutex); - return ps->end_position; + PthreadMutexLocker ml(ps.mutex); + return ps.end_position; } - int64_t AlsaPlayer::GetCurrentPosition() { int64_t lastpos; @@ -437,10 +431,10 @@ int64_t AlsaPlayer::GetCurrentPosition() int64_t samplerate; { - PthreadMutexLocker ml(ps->mutex); - lastpos = ps->last_position; - lasttime = ps->last_position_time; - samplerate = ps->provider->GetSampleRate(); + PthreadMutexLocker ml(ps.mutex); + lastpos = ps.last_position; + lasttime = ps.last_position_time; + samplerate = ps.provider->GetSampleRate(); } timespec now; @@ -460,10 +454,16 @@ int64_t AlsaPlayer::GetCurrentPosition() void AlsaPlayer::SetVolume(double vol) { - PthreadMutexLocker ml(ps->mutex); - ps->volume = vol; - ps->signal_volume = true; - pthread_cond_signal(&ps->cond); + PthreadMutexLocker ml(ps.mutex); + ps.volume = vol; + ps.signal_volume = true; + pthread_cond_signal(&ps.cond); +} +} + +std::unique_ptr CreateAlsaPlayer(AudioProvider *provider) +{ + return agi::util::make_unique(provider); } #endif // WITH_ALSA diff --git a/src/audio_player_alsa.h b/src/audio_player_alsa.h deleted file mode 100644 index a524f7894..000000000 --- a/src/audio_player_alsa.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2011, Niels Martin Hansen -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of the Aegisub Group nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Aegisub Project http://www.aegisub.org/ - -/// @file audio_player_alsa.h -/// @see audio_player_alsa.cpp -/// @ingroup audio_output -/// - -#ifdef WITH_ALSA -#include "include/aegisub/audio_player.h" - -#include -#include - -struct PlaybackState; - -class AlsaPlayer final : public AudioPlayer { - std::unique_ptr ps; - pthread_t thread; - -public: - AlsaPlayer(AudioProvider *provider); - ~AlsaPlayer(); - - void Play(int64_t start, int64_t count); - void Stop(); - bool IsPlaying(); - - int64_t GetStartPosition(); - int64_t GetEndPosition(); - int64_t GetCurrentPosition(); - void SetEndPosition(int64_t pos); - void SetCurrentPosition(int64_t pos); - - void SetVolume(double vol); -}; - -#endif diff --git a/src/audio_player_dsound.cpp b/src/audio_player_dsound.cpp index c32287834..f5844f294 100644 --- a/src/audio_player_dsound.cpp +++ b/src/audio_player_dsound.cpp @@ -35,28 +35,72 @@ #include "config.h" #ifdef WITH_DIRECTSOUND - -#include +#include "include/aegisub/audio_player.h" #include "audio_controller.h" -#include "audio_player_dsound.h" +#include "include/aegisub/audio_provider.h" #include "frame_main.h" #include "main.h" #include "utils.h" +#include +#include + +#include +#include + +namespace { +class DirectSoundPlayer; + +class DirectSoundPlayerThread final : public wxThread { + DirectSoundPlayer *parent; + HANDLE stopnotify; + +public: + void Stop(); // Notify thread to stop audio playback. Thread safe. + DirectSoundPlayerThread(DirectSoundPlayer *parent); + ~DirectSoundPlayerThread(); + + wxThread::ExitCode Entry(); +}; + +class DirectSoundPlayer final : public AudioPlayer { + friend class DirectSoundPlayerThread; + + volatile bool playing = false; + float volume = 1.0f; + int offset = 0; + + DWORD bufSize = 0; + volatile int64_t playPos = 0; + int64_t startPos = 0; + volatile int64_t endPos = 0; + DWORD startTime = 0; + + IDirectSound8 *directSound = nullptr; + IDirectSoundBuffer8 *buffer = nullptr; + + bool FillBuffer(bool fill); + DirectSoundPlayerThread *thread = nullptr; + +public: + DirectSoundPlayer(AudioProvider *provider); + ~DirectSoundPlayer(); + + void Play(int64_t start,int64_t count); + void Stop(); + + bool IsPlaying() { return playing; } + + int64_t GetEndPosition() { return endPos; } + int64_t GetCurrentPosition(); + void SetEndPosition(int64_t pos); + + void SetVolume(double vol) { volume = vol; } +}; + DirectSoundPlayer::DirectSoundPlayer(AudioProvider *provider) : AudioPlayer(provider) -, playing(false) -, volume(1.0f) -, offset(0) -, bufSize(0) -, playPos(0) -, startPos(0) -, endPos(0) -, startTime(0) -, directSound(0) -, buffer(0) -, thread(0) { // Initialize the DirectSound object HRESULT res; @@ -105,23 +149,13 @@ DirectSoundPlayer::DirectSoundPlayer(AudioProvider *provider) DirectSoundPlayer::~DirectSoundPlayer() { Stop(); - // Unref the DirectSound buffer - if (buffer) { + if (buffer) buffer->Release(); - buffer = nullptr; - } - // Unref the DirectSound object - if (directSound) { + if (directSound) directSound->Release(); - directSound = nullptr; - } } -/// @brief Fill buffer -/// @param fill -/// @return -/// bool DirectSoundPlayer::FillBuffer(bool fill) { if (playPos >= endPos) return false; @@ -181,7 +215,6 @@ RetryLock: goto RetryLock; } - // Error if (FAILED(res)) return false; // Convert size to number of samples @@ -197,19 +230,13 @@ RetryLock: if (count2) provider->GetAudioWithVolume(ptr2, playPos+count1, count2, volume); playPos += count1+count2; - // Unlock buffer->Unlock(ptr1,count1*bytesps,ptr2,count2*bytesps); - // Update offset offset = (offset + count1*bytesps + count2*bytesps) % bufSize; return playPos < endPos; } -/// @brief Play -/// @param start -/// @param count -/// void DirectSoundPlayer::Play(int64_t start,int64_t count) { // Make sure that it's stopped Stop(); @@ -245,9 +272,6 @@ void DirectSoundPlayer::Play(int64_t start,int64_t count) { startTime = GetTickCount(); } -/// @brief Stop -/// @param timerToo -/// void DirectSoundPlayer::Stop() { // Stop the thread if (thread) { @@ -259,7 +283,6 @@ void DirectSoundPlayer::Stop() { } // The thread is now guaranteed dead and there are no concurrency problems to worry about - // Stop if (buffer) buffer->Stop(); // the thread should have done this already // Reset variables @@ -270,16 +293,10 @@ void DirectSoundPlayer::Stop() { offset = 0; } -/// @brief Set end -/// @param pos -/// void DirectSoundPlayer::SetEndPosition(int64_t pos) { if (playing) endPos = pos; } -/// @brief Get current position -/// @return -/// int64_t DirectSoundPlayer::GetCurrentPosition() { // Check if buffer is loaded if (!buffer || !playing) return 0; @@ -291,23 +308,15 @@ int64_t DirectSoundPlayer::GetCurrentPosition() { return startPos + tdiff * provider->GetSampleRate() / 1000; } -/// @brief Thread constructor -/// @param par -/// DirectSoundPlayerThread::DirectSoundPlayerThread(DirectSoundPlayer *par) : wxThread(wxTHREAD_JOINABLE) { parent = par; stopnotify = CreateEvent(nullptr, true, false, nullptr); } -/// @brief Thread destructor -/// DirectSoundPlayerThread::~DirectSoundPlayerThread() { CloseHandle(stopnotify); } -/// @brief Thread entry point -/// @return -/// wxThread::ExitCode DirectSoundPlayerThread::Entry() { CoInitialize(0); @@ -355,12 +364,15 @@ wxThread::ExitCode DirectSoundPlayerThread::Entry() { return 0; } -/// @brief Stop playback thread -/// void DirectSoundPlayerThread::Stop() { // Increase the stopnotify by one, causing a wait for it to succeed SetEvent(stopnotify); } +} + +std::unique_ptr CreateDirectSoundPlayer(AudioProvider *provider) { + return agi::util::make_unique(provider); +} #endif // WITH_DIRECTSOUND diff --git a/src/audio_player_dsound.h b/src/audio_player_dsound.h deleted file mode 100644 index ec9fd244e..000000000 --- a/src/audio_player_dsound.h +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2006, Rodrigo Braz Monteiro -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of the Aegisub Group nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Aegisub Project http://www.aegisub.org/ - -/// @file audio_player_dsound.h -/// @see audio_player_dsound.cpp -/// @ingroup audio_output -/// - -#ifdef WITH_DIRECTSOUND - -#include - -#include - -#include "include/aegisub/audio_player.h" -#include "include/aegisub/audio_provider.h" - -class DirectSoundPlayer; - -class DirectSoundPlayerThread final : public wxThread { - DirectSoundPlayer *parent; - HANDLE stopnotify; - -public: - void Stop(); // Notify thread to stop audio playback. Thread safe. - DirectSoundPlayerThread(DirectSoundPlayer *parent); - ~DirectSoundPlayerThread(); - - wxThread::ExitCode Entry(); -}; - -class DirectSoundPlayer final : public AudioPlayer { - friend class DirectSoundPlayerThread; - - volatile bool playing; - float volume; - int offset; - - DWORD bufSize; - volatile int64_t playPos; - int64_t startPos; - volatile int64_t endPos; - DWORD startTime; - - IDirectSound8 *directSound; - IDirectSoundBuffer8 *buffer; - - bool FillBuffer(bool fill); - DirectSoundPlayerThread *thread; - -public: - DirectSoundPlayer(AudioProvider *provider); - ~DirectSoundPlayer(); - - void Play(int64_t start,int64_t count); - void Stop(); - - bool IsPlaying() { return playing; } - - int64_t GetEndPosition() { return endPos; } - int64_t GetCurrentPosition(); - void SetEndPosition(int64_t pos); - - void SetVolume(double vol) { volume = vol; } -}; -#endif diff --git a/src/audio_player_dsound2.cpp b/src/audio_player_dsound2.cpp index a91c1acfd..9952b4d62 100644 --- a/src/audio_player_dsound2.cpp +++ b/src/audio_player_dsound2.cpp @@ -35,7 +35,7 @@ #include "config.h" #ifdef WITH_DIRECTSOUND -#include "audio_player_dsound2.h" +#include "include/aegisub/audio_player.h" #include "audio_controller.h" #include "include/aegisub/audio_provider.h" @@ -52,6 +52,68 @@ #include #include +namespace { +class DirectSoundPlayer2Thread; + +/// @class DirectSoundPlayer2 +/// @brief New implementation of DirectSound-based audio player +/// +/// The core design idea is to have a playback thread that owns the DirectSound COM objects +/// and performs all playback operations, and use the player object as a proxy to +/// send commands to the playback thread. +class DirectSoundPlayer2 final : public AudioPlayer { + /// The playback thread + std::unique_ptr thread; + + /// Desired length in milliseconds to write ahead of the playback cursor + int WantedLatency; + + /// Multiplier for WantedLatency to get total buffer length + int BufferLength; + + /// @brief Tell whether playback thread is alive + /// @return True if there is a playback thread and it's ready + bool IsThreadAlive(); + +public: + /// @brief Constructor + DirectSoundPlayer2(AudioProvider *provider); + /// @brief Destructor + ~DirectSoundPlayer2(); + + /// @brief Start playback + /// @param start First audio frame to play + /// @param count Number of audio frames to play + void Play(int64_t start,int64_t count); + + /// @brief Stop audio playback + /// @param timerToo Whether to also stop the playback update timer + void Stop(); + + /// @brief Tell whether playback is active + /// @return True if audio is playing back + bool IsPlaying(); + + /// @brief Get playback end position + /// @return Audio frame index + /// + /// Returns 0 if playback is stopped or there is no playback thread + int64_t GetEndPosition(); + /// @brief Get approximate playback position + /// @return Index of audio frame user is currently hearing + /// + /// Returns 0 if playback is stopped or there is no playback thread + int64_t GetCurrentPosition(); + + /// @brief Change playback end position + /// @param pos New end position + void SetEndPosition(int64_t pos); + + /// @brief Change playback volume + /// @param vol Amplification factor + void SetVolume(double vol); +}; + /// @brief RAII support class to init and de-init the COM library struct COMInitialization { @@ -157,7 +219,6 @@ class DirectSoundPlayer2Thread { /// @brief Check for error state and throw exception if one occurred void CheckError(); - /// Win32 handle to the thread Win32KernelHandle thread_handle; @@ -186,16 +247,16 @@ class DirectSoundPlayer2Thread { Win32KernelHandle error_happened; /// Statically allocated error message text describing reason for error_happened being set - const char *error_message; + const char *error_message = nullptr; /// Playback volume, 1.0 is "unchanged" - double volume; + double volume = 1.0; /// Audio frame to start playback at - int64_t start_frame; + int64_t start_frame = 0; /// Audio frame to end playback at - int64_t end_frame; + int64_t end_frame = 0; /// Desired length in milliseconds to write ahead of the playback cursor @@ -646,11 +707,6 @@ DirectSoundPlayer2Thread::DirectSoundPlayer2Thread(AudioProvider *provider, int , buffer_length(BufferLength) , provider(provider) { - error_message = 0; - volume = 1.0; - start_frame = 0; - end_frame = 0; - thread_handle = (HANDLE)_beginthreadex(0, 0, ThreadProc, this, 0, 0); if (!thread_handle) @@ -904,5 +960,10 @@ void DirectSoundPlayer2::SetVolume(double vol) LOG_E("audio/player/dsound") << msg; } } +} + +std::unique_ptr CreateDirectSound2Player(AudioProvider *provider) { + return agi::util::make_unique(provider); +} #endif // WITH_DIRECTSOUND diff --git a/src/audio_player_dsound2.h b/src/audio_player_dsound2.h deleted file mode 100644 index 62e0b0048..000000000 --- a/src/audio_player_dsound2.h +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) 2008, Niels Martin Hansen -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of the Aegisub Group nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Aegisub Project http://www.aegisub.org/ - -/// @file audio_player_dsound2.h -/// @see audio_player_dsound2.cpp -/// @ingroup audio_output -/// - -#ifdef WITH_DIRECTSOUND - -#include "include/aegisub/audio_player.h" - -class DirectSoundPlayer2Thread; - -/// @class DirectSoundPlayer2 -/// @brief New implementation of DirectSound-based audio player -/// -/// The core design idea is to have a playback thread that owns the DirectSound COM objects -/// and performs all playback operations, and use the player object as a proxy to -/// send commands to the playback thread. -class DirectSoundPlayer2 final : public AudioPlayer { - /// The playback thread - std::unique_ptr thread; - - /// Desired length in milliseconds to write ahead of the playback cursor - int WantedLatency; - - /// Multiplier for WantedLatency to get total buffer length - int BufferLength; - - /// @brief Tell whether playback thread is alive - /// @return True if there is a playback thread and it's ready - bool IsThreadAlive(); - -public: - /// @brief Constructor - DirectSoundPlayer2(AudioProvider *provider); - /// @brief Destructor - ~DirectSoundPlayer2(); - - /// @brief Start playback - /// @param start First audio frame to play - /// @param count Number of audio frames to play - void Play(int64_t start,int64_t count); - - /// @brief Stop audio playback - /// @param timerToo Whether to also stop the playback update timer - void Stop(); - - /// @brief Tell whether playback is active - /// @return True if audio is playing back - bool IsPlaying(); - - /// @brief Get playback end position - /// @return Audio frame index - /// - /// Returns 0 if playback is stopped or there is no playback thread - int64_t GetEndPosition(); - /// @brief Get approximate playback position - /// @return Index of audio frame user is currently hearing - /// - /// Returns 0 if playback is stopped or there is no playback thread - int64_t GetCurrentPosition(); - - /// @brief Change playback end position - /// @param pos New end position - void SetEndPosition(int64_t pos); - - /// @brief Change playback volume - /// @param vol Amplification factor - void SetVolume(double vol); -}; -#endif diff --git a/src/audio_player_openal.cpp b/src/audio_player_openal.cpp index a3dbc75da..b44cb3cd0 100644 --- a/src/audio_player_openal.cpp +++ b/src/audio_player_openal.cpp @@ -35,14 +35,29 @@ #include "config.h" #ifdef WITH_OPENAL - -#include - -#include "audio_player_openal.h" +#include "include/aegisub/audio_player.h" #include "audio_controller.h" +#include "include/aegisub/audio_provider.h" #include "utils.h" +#include +#include + +#ifdef __WINDOWS__ +#include +#include +#elif defined(__APPLE__) +#include +#include +#else +#include +#include +#endif + +#include +#include + // Auto-link to OpenAL lib for MSVC #ifdef _MSC_VER #pragma comment(lib, "openal32.lib") @@ -50,6 +65,65 @@ DEFINE_SIMPLE_EXCEPTION(OpenALException, agi::AudioPlayerOpenError, "audio/open/player/openal") +namespace { +class OpenALPlayer final : public AudioPlayer, wxTimer { + /// Number of OpenAL buffers to use + static const ALsizei num_buffers = 8; + + bool playing = false; ///< Is audio currently playing? + + float volume = 1.f; ///< Current audio volume + ALsizei samplerate; ///< Sample rate of the audio + int bpf; ///< Bytes per frame + + int64_t start_frame = 0; ///< First frame of playbacka + int64_t cur_frame = 0; ///< Next frame to write to playback buffers + int64_t end_frame = 0; ///< Last frame to play + + ALCdevice *device = nullptr; ///< OpenAL device handle + ALCcontext *context = nullptr; ///< OpenAL sound context + ALuint buffers[num_buffers]; ///< OpenAL sound buffers + ALuint source = 0; ///< OpenAL playback source + + /// Index into buffers, first free (unqueued) buffer to be filled + ALsizei buf_first_free = 0; + + /// Index into buffers, first queued (non-free) buffer + ALsizei buf_first_queued = 0; + + /// Number of free buffers + ALsizei buffers_free = 0; + + /// Number of buffers which have been fully played since playback was last started + ALsizei buffers_played = 0; + + wxStopWatch playback_segment_timer; + + /// Buffer to decode audio into + std::vector decode_buffer; + + /// Fill count OpenAL buffers + void FillBuffers(ALsizei count); + +protected: + /// wxTimer override to periodically fill available buffers + void Notify() override; + +public: + OpenALPlayer(AudioProvider *provider); + ~OpenALPlayer(); + + void Play(int64_t start,int64_t count) override; + void Stop() override; + bool IsPlaying() override { return playing; } + + int64_t GetEndPosition() override { return end_frame; } + int64_t GetCurrentPosition() override; + void SetEndPosition(int64_t pos) override; + + void SetVolume(double vol) override { volume = vol; } +}; + OpenALPlayer::OpenALPlayer(AudioProvider *provider) : AudioPlayer(provider) , samplerate(provider->GetSampleRate()) @@ -210,5 +284,11 @@ int64_t OpenALPlayer::GetCurrentPosition() long extra = playback_segment_timer.Time(); return buffers_played * decode_buffer.size() / bpf + start_frame + extra * samplerate / 1000; } +} + +std::unique_ptr CreateOpenALPlayer(AudioProvider *provider) +{ + return agi::util::make_unique(provider); +} #endif // WITH_OPENAL diff --git a/src/audio_player_openal.h b/src/audio_player_openal.h deleted file mode 100644 index bab9e7137..000000000 --- a/src/audio_player_openal.h +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 2007, Niels Martin Hansen -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of the Aegisub Group nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Aegisub Project http://www.aegisub.org/ - -/// @file audio_player_openal.h -/// @see audio_player_openal.cpp -/// @ingroup audio_output -/// - -#ifdef WITH_OPENAL -#include "include/aegisub/audio_player.h" -#include "include/aegisub/audio_provider.h" - -#ifdef __WINDOWS__ -#include -#include -#elif defined(__APPLE__) -#include -#include -#else -#include -#include -#endif - -#include - -#include - -class OpenALPlayer final : public AudioPlayer, wxTimer { - /// Number of OpenAL buffers to use - static const ALsizei num_buffers = 8; - - bool playing = false; ///< Is audio currently playing? - - float volume = 1.f; ///< Current audio volume - ALsizei samplerate; ///< Sample rate of the audio - int bpf; ///< Bytes per frame - - int64_t start_frame = 0; ///< First frame of playbacka - int64_t cur_frame = 0; ///< Next frame to write to playback buffers - int64_t end_frame = 0; ///< Last frame to play - - ALCdevice *device = nullptr; ///< OpenAL device handle - ALCcontext *context = nullptr; ///< OpenAL sound context - ALuint buffers[num_buffers]; ///< OpenAL sound buffers - ALuint source = 0; ///< OpenAL playback source - - /// Index into buffers, first free (unqueued) buffer to be filled - ALsizei buf_first_free = 0; - - /// Index into buffers, first queued (non-free) buffer - ALsizei buf_first_queued = 0; - - /// Number of free buffers - ALsizei buffers_free = 0; - - /// Number of buffers which have been fully played since playback was last started - ALsizei buffers_played = 0; - - wxStopWatch playback_segment_timer; - - /// Buffer to decode audio into - std::vector decode_buffer; - - /// Fill count OpenAL buffers - void FillBuffers(ALsizei count); - -protected: - /// wxTimer override to periodically fill available buffers - void Notify() override; - -public: - OpenALPlayer(AudioProvider *provider); - ~OpenALPlayer(); - - void Play(int64_t start,int64_t count) override; - void Stop() override; - bool IsPlaying() override { return playing; } - - int64_t GetEndPosition() override { return end_frame; } - int64_t GetCurrentPosition() override; - void SetEndPosition(int64_t pos) override; - - void SetVolume(double vol) override { volume = vol; } -}; -#endif diff --git a/src/audio_player_oss.cpp b/src/audio_player_oss.cpp index 35198cbad..0b7d4f156 100644 --- a/src/audio_player_oss.cpp +++ b/src/audio_player_oss.cpp @@ -33,12 +33,7 @@ #include "config.h" #ifdef WITH_OSS - -#include - -#include - -#include "audio_player_oss.h" +#include "include/aegisub/audio_player.h" #include "audio_controller.h" #include "compat.h" @@ -46,19 +41,112 @@ #include "options.h" #include "utils.h" +#include +#include + +#include +#include +#include +#include + +#ifdef HAVE_SOUNDCARD_H +# include +#elif defined(HAVE_SYS_SOUNDCARD_H) +# include +#endif + +namespace { DEFINE_SIMPLE_EXCEPTION(OSSError, agi::AudioPlayerOpenError, "audio/player/open/oss") +class OSSPlayerThread; -OSSPlayer::OSSPlayer(AudioProvider *provider) -: AudioPlayer(provider) -{ - OpenStream(); -} +class OSSPlayer final : public AudioPlayer { + friend class OSSPlayerThread; -OSSPlayer::~OSSPlayer() -{ - Stop(); - ::close(dspdev); -} + /// sample rate of audio + unsigned int rate = 0; + + /// Worker thread that does the actual writing + OSSPlayerThread *thread = nullptr; + + /// Is the player currently playing? + volatile bool playing = false; + + /// Current volume level + volatile float volume = 1.f; + + /// first frame of playback + volatile unsigned long start_frame = 0; + + /// last written frame + 1 + volatile unsigned long cur_frame = 0; + + /// last frame to play + volatile unsigned long end_frame = 0; + + /// bytes per frame + unsigned long bpf = 0; + + /// OSS audio device handle + volatile int dspdev = 0; + + void OpenStream(); + +public: + OSSPlayer(AudioProvider *provider) + : AudioPlayer(provider) + { + OpenStream(); + } + + ~OSSPlayer() { + Stop(); + ::close(dspdev); + } + + + void Play(int64_t start, int64_t count); + void Stop(); + bool IsPlaying() { return playing; } + + int64_t GetEndPosition() { return end_frame; } + void SetEndPosition(int64_t pos); + + int64_t GetCurrentPosition(); + + void SetVolume(double vol) { volume = vol; } +}; + +/// Worker thread to asynchronously write audio data to the output device +class OSSPlayerThread final : public wxThread { + /// Parent player + OSSPlayer *parent; + +public: + /// Constructor + /// @param parent Player to get audio data and playback state from + OSSPlayerThread(OSSPlayer *parent) : wxThread(wxTHREAD_JOINABLE) , parent(parent) { } + + /// Main thread entry point + wxThread::ExitCode Entry() { + // Use small enough writes for good timing accuracy with all + // timing methods. + const unsigned long wsize = parent->rate / 25; + void *buf = malloc(wsize * parent->bpf); + + while (!TestDestroy() && parent->cur_frame < parent->end_frame) { + int rsize = std::min(wsize, parent->end_frame - parent->cur_frame); + parent->provider->GetAudioWithVolume(buf, parent->cur_frame, + rsize, parent->volume); + int written = ::write(parent->dspdev, buf, rsize * parent->bpf); + parent->cur_frame += written / parent->bpf; + } + free(buf); + parent->cur_frame = parent->end_frame; + + LOG_D("player/audio/oss") << "Thread dead"; + return 0; + } +}; void OSSPlayer::OpenStream() { @@ -193,31 +281,10 @@ int64_t OSSPlayer::GetCurrentPosition() // Return the last written frame, timing will suffer return cur_frame; } - -OSSPlayerThread::OSSPlayerThread(OSSPlayer *par) -: wxThread(wxTHREAD_JOINABLE) -, parent(par) -{ } -wxThread::ExitCode OSSPlayerThread::Entry() { - // Use small enough writes for good timing accuracy with all - // timing methods. - const unsigned long wsize = parent->rate / 25; - void *buf = malloc(wsize * parent->bpf); - - while (!TestDestroy() && parent->cur_frame < parent->end_frame) { - int rsize = std::min(wsize, parent->end_frame - parent->cur_frame); - parent->provider->GetAudioWithVolume(buf, parent->cur_frame, - rsize, parent->volume); - int written = ::write(parent->dspdev, buf, rsize * parent->bpf); - parent->cur_frame += written / parent->bpf; - } - free(buf); - parent->cur_frame = parent->end_frame; - - LOG_D("player/audio/oss") << "Thread dead"; - return 0; +std::unique_ptr CreateOSSPlayer(AudioProvider *provider) { + return agi::util::make_unique(provider); } #endif // WITH_OSS diff --git a/src/audio_player_oss.h b/src/audio_player_oss.h deleted file mode 100644 index 178054442..000000000 --- a/src/audio_player_oss.h +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) 2009, Grigori Goronzy -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of the Aegisub Group nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Aegisub Project http://www.aegisub.org/ - -/// @file audio_player_oss.h -/// @see audio_player_oss.cpp -/// @ingroup audio_output -/// - -#ifdef WITH_OSS -#include - -#include - -#include -#ifdef HAVE_SOUNDCARD_H -# include -#else -# ifdef HAVE_SYS_SOUNDCARD_H -# include -# endif -#endif - -#include "include/aegisub/audio_player.h" - -class AudioProvider; -class OSSPlayer; - -/// Worker thread to asynchronously write audio data to the output device -class OSSPlayerThread final : public wxThread { - /// Parent player - OSSPlayer *parent; - -public: - /// Constructor - /// @param parent Player to get audio data and playback state from - OSSPlayerThread(OSSPlayer *parent); - - /// Main thread entry point - wxThread::ExitCode Entry(); -}; - -class OSSPlayer final : public AudioPlayer { - friend class OSSPlayerThread; - - /// sample rate of audio - unsigned int rate = 0; - - /// Worker thread that does the actual writing - OSSPlayerThread *thread = nullptr; - - /// Is the player currently playing? - volatile bool playing = false; - - /// Current volume level - volatile float volume = 1.f; - - /// first frame of playback - volatile unsigned long start_frame = 0; - - /// last written frame + 1 - volatile unsigned long cur_frame = 0; - - /// last frame to play - volatile unsigned long end_frame = 0; - - /// bytes per frame - unsigned long bpf = 0; - - /// OSS audio device handle - volatile int dspdev = 0; - - void OpenStream(); - -public: - OSSPlayer(AudioProvider *provider); - ~OSSPlayer(); - - void Play(int64_t start, int64_t count); - void Stop(); - bool IsPlaying() { return playing; } - - int64_t GetEndPosition() { return end_frame; } - void SetEndPosition(int64_t pos); - - int64_t GetCurrentPosition(); - - void SetVolume(double vol) { volume = vol; } -}; -#endif diff --git a/src/audio_player_portaudio.cpp b/src/audio_player_portaudio.cpp index f99cc7c18..822705fb8 100644 --- a/src/audio_player_portaudio.cpp +++ b/src/audio_player_portaudio.cpp @@ -32,13 +32,9 @@ /// @ingroup audio_output /// - #include "config.h" #ifdef WITH_PORTAUDIO - -#include - #include "audio_player_portaudio.h" #include "audio_controller.h" @@ -47,6 +43,9 @@ #include "options.h" #include "utils.h" +#include +#include + DEFINE_SIMPLE_EXCEPTION(PortAudioError, agi::AudioPlayerOpenError, "audio/player/open/portaudio") // Uncomment to enable extremely spammy debug logging @@ -284,4 +283,8 @@ bool PortAudioPlayer::IsPlaying() { return !!Pa_IsStreamActive(stream); } +std::unique_ptr CreatePortAudioPlayer(AudioProvider *provider) { + return agi::util::make_unique(provider); +} + #endif // WITH_PORTAUDIO diff --git a/src/audio_player_pulse.cpp b/src/audio_player_pulse.cpp index a6cfb6cda..2d601e48d 100644 --- a/src/audio_player_pulse.cpp +++ b/src/audio_player_pulse.cpp @@ -35,18 +35,74 @@ #include "config.h" #ifdef WITH_LIBPULSE - -#include - -#include - -#include "audio_player_pulse.h" +#include "include/aegisub/audio_player.h" #include "audio_controller.h" #include "include/aegisub/audio_provider.h" #include "utils.h" #include +#include + +#include +#include +#include + +namespace { +class PulseAudioPlayer final : public AudioPlayer { + float volume = 1.f; + bool is_playing = false; + + volatile unsigned long start_frame = 0; + volatile unsigned long cur_frame = 0; + volatile unsigned long end_frame = 0; + + unsigned long bpf = 0; // bytes per frame + + wxSemaphore context_notify{0, 1}; + wxSemaphore context_success{0, 1}; + volatile int context_success_val; + + wxSemaphore stream_notify{0, 1}; + wxSemaphore stream_success{0, 1}; + volatile int stream_success_val; + + pa_threaded_mainloop *mainloop = nullptr; // pulseaudio mainloop handle + pa_context *context = nullptr; // connection context + volatile pa_context_state_t cstate; + + pa_stream *stream = nullptr; + volatile pa_stream_state_t sstate; + + volatile pa_usec_t play_start_time; // timestamp when playback was started + + int paerror = 0; + + /// Called by PA to notify about contetxt operation completion + static void pa_context_success(pa_context *c, int success, PulseAudioPlayer *thread); + /// Called by PA to notify about other context-related stuff + static void pa_context_notify(pa_context *c, PulseAudioPlayer *thread); + /// Called by PA when a stream operation completes + static void pa_stream_success(pa_stream *p, int success, PulseAudioPlayer *thread); + /// Called by PA to request more data written to stream + static void pa_stream_write(pa_stream *p, size_t length, PulseAudioPlayer *thread); + /// Called by PA to notify about other stream-related stuff + static void pa_stream_notify(pa_stream *p, PulseAudioPlayer *thread); + +public: + PulseAudioPlayer(AudioProvider *provider); + ~PulseAudioPlayer(); + + void Play(int64_t start,int64_t count); + void Stop(); + bool IsPlaying() { return is_playing; } + + int64_t GetEndPosition() { return end_frame; } + int64_t GetCurrentPosition(); + void SetEndPosition(int64_t pos); + + void SetVolume(double vol) { volume = vol; } +}; PulseAudioPlayer::PulseAudioPlayer(AudioProvider *provider) : AudioPlayer(provider) { // Initialise a mainloop @@ -277,5 +333,9 @@ void PulseAudioPlayer::pa_stream_notify(pa_stream *p, PulseAudioPlayer *thread) thread->sstate = pa_stream_get_state(thread->stream); thread->stream_notify.Post(); } +} +std::unique_ptr CreatePulseAudioPlayer(AudioProvider *provider) { + return agi::util::make_unique(provider); +} #endif // WITH_LIBPULSE diff --git a/src/audio_player_pulse.h b/src/audio_player_pulse.h deleted file mode 100644 index 7c190d44b..000000000 --- a/src/audio_player_pulse.h +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2007, Niels Martin Hansen -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of the Aegisub Group nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Aegisub Project http://www.aegisub.org/ - -/// @file audio_player_pulse.h -/// @see audio_player_pulse.cpp -/// @ingroup audio_output -/// - -#ifdef WITH_LIBPULSE -#include - -#include "include/aegisub/audio_player.h" - -class PulseAudioPlayer; - -class PulseAudioPlayer final : public AudioPlayer { - float volume = 1.f; - bool is_playing = false; - - volatile unsigned long start_frame = 0; - volatile unsigned long cur_frame = 0; - volatile unsigned long end_frame = 0; - - unsigned long bpf = 0; // bytes per frame - - - wxSemaphore context_notify{0, 1}; - wxSemaphore context_success{0, 1}; - volatile int context_success_val; - - wxSemaphore stream_notify{0, 1}; - wxSemaphore stream_success{0, 1}; - volatile int stream_success_val; - - pa_threaded_mainloop *mainloop = nullptr; // pulseaudio mainloop handle - pa_context *context = nullptr; // connection context - volatile pa_context_state_t cstate; - - pa_stream *stream = nullptr; - volatile pa_stream_state_t sstate; - - volatile pa_usec_t play_start_time; // timestamp when playback was started - - int paerror = 0; - - /// Called by PA to notify about contetxt operation completion - static void pa_context_success(pa_context *c, int success, PulseAudioPlayer *thread); - /// Called by PA to notify about other context-related stuff - static void pa_context_notify(pa_context *c, PulseAudioPlayer *thread); - /// Called by PA when a stream operation completes - static void pa_stream_success(pa_stream *p, int success, PulseAudioPlayer *thread); - /// Called by PA to request more data written to stream - static void pa_stream_write(pa_stream *p, size_t length, PulseAudioPlayer *thread); - /// Called by PA to notify about other stream-related stuff - static void pa_stream_notify(pa_stream *p, PulseAudioPlayer *thread); - -public: - PulseAudioPlayer(AudioProvider *provider); - ~PulseAudioPlayer(); - - void Play(int64_t start,int64_t count); - void Stop(); - bool IsPlaying() { return is_playing; } - - int64_t GetEndPosition() { return end_frame; } - int64_t GetCurrentPosition(); - void SetEndPosition(int64_t pos); - - void SetVolume(double vol) { volume = vol; } -}; - -#endif diff --git a/src/factory_manager.h b/src/factory_manager.h index deb977377..89a20d0ba 100644 --- a/src/factory_manager.h +++ b/src/factory_manager.h @@ -14,65 +14,9 @@ // // Aegisub Project http://www.aegisub.org/ -/// @file factory_manager.h -/// @brief Template/base-class for factory classes -/// @ingroup utility -/// - -#pragma once - -#include -#include -#include #include #include -namespace agi { class ProgressSink; } - -template -class Factory { - typedef Base *(*func)(Args const&...); - typedef std::map> map; - - static map& classes() { - static map classes; - return classes; - } - -public: - static std::vector GetClasses(std::string favorite="") { - std::vector list; - std::string cmp; - for (auto& c : favorite) c = ::tolower(c); - for (auto const& cls : classes()) { - cmp.clear(); - std::transform(cls.first.begin(), cls.first.end(), std::back_inserter(cmp), ::tolower); - if (cmp == favorite) - list.insert(list.begin(), cls.first); - else if (!cls.second.first) - list.push_back(cls.first); - } - return list; - } - - static std::unique_ptr Create(std::string const& name, Args const&... args) { - auto factory = classes().find(name); - if (factory == classes().end()) return nullptr; - return std::unique_ptr(factory->second.second(args...)); - } - - template - static void Register(std::string name, bool hide = false, std::vector subtypes = {}) { - func factory = [](Args const&... args) -> Base * { return new T(args...); }; - if (subtypes.empty()) - classes().insert(std::make_pair(name, std::make_pair(hide, factory))); - else { - for (auto const& subtype : subtypes) - classes().insert(std::make_pair(name + '/' + subtype, std::make_pair(hide, factory))); - } - } -}; - namespace { template std::vector GetClasses(Container const& c) { diff --git a/src/include/aegisub/audio_player.h b/src/include/aegisub/audio_player.h index cab2f2b5e..0a5ac9693 100644 --- a/src/include/aegisub/audio_player.h +++ b/src/include/aegisub/audio_player.h @@ -34,7 +34,10 @@ #pragma once -#include "factory_manager.h" +#include +#include +#include +#include class AudioProvider; @@ -57,8 +60,7 @@ public: virtual void SetEndPosition(int64_t pos)=0; }; -class AudioPlayerFactory final : public Factory { -public: - static void RegisterProviders(); +struct AudioPlayerFactory { + static std::vector GetClasses(); static std::unique_ptr GetAudioPlayer(AudioProvider *provider); }; diff --git a/src/include/aegisub/spellchecker.h b/src/include/aegisub/spellchecker.h index 6d952edb9..f5eb4b4f5 100644 --- a/src/include/aegisub/spellchecker.h +++ b/src/include/aegisub/spellchecker.h @@ -19,12 +19,10 @@ /// @ingroup main_headers spelling /// -#include "factory_manager.h" +#include namespace agi { class SpellChecker; } -class SpellCheckerFactory final : public Factory { -public: +struct SpellCheckerFactory { static std::unique_ptr GetSpellChecker(); - static void RegisterProviders(); }; diff --git a/src/include/aegisub/subtitles_provider.h b/src/include/aegisub/subtitles_provider.h index 4a7a71c0a..e1041d336 100644 --- a/src/include/aegisub/subtitles_provider.h +++ b/src/include/aegisub/subtitles_provider.h @@ -34,21 +34,21 @@ #pragma once -#include "factory_manager.h" +#include +#include +#include class AssFile; struct VideoFrame; class SubtitlesProvider { public: - virtual ~SubtitlesProvider() { }; - + virtual ~SubtitlesProvider() { } virtual void LoadSubtitles(AssFile *subs)=0; - virtual void DrawSubtitles(VideoFrame &dst,double time)=0; + virtual void DrawSubtitles(VideoFrame &dst, double time)=0; }; -class SubtitlesProviderFactory final : public Factory { -public: +struct SubtitlesProviderFactory { static std::unique_ptr GetProvider(); - static void RegisterProviders(); + static std::vector GetClasses(); }; diff --git a/src/main.cpp b/src/main.cpp index f2d4291a3..a14800891 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -42,6 +42,7 @@ #include "ass_dialogue.h" #include "ass_file.h" #include "auto4_base.h" +#include "auto4_lua_factory.h" #include "compat.h" #include "crash_writer.h" #include "export_fixstyle.h" @@ -50,9 +51,9 @@ #include "include/aegisub/context.h" #include "libresrc/libresrc.h" #include "options.h" -#include "plugin_manager.h" #include "subs_controller.h" #include "subtitle_format.h" +#include "subtitles_provider_libass.h" #include "video_context.h" #include "version.h" #include "utils.h" @@ -238,7 +239,8 @@ bool AegisubApp::OnInit() { exception_message = _("Oops, Aegisub has crashed!\n\nAn attempt has been made to save a copy of your file to:\n\n%s\n\nAegisub will now close."); // Load plugins - RegisterBuiltInPlugins(); + Automation4::ScriptFactory::Register(agi::util::make_unique()); + libass::CacheFonts(); // Load Automation scripts StartupLog("Load global Automation scripts"); diff --git a/src/plugin_manager.cpp b/src/plugin_manager.cpp deleted file mode 100644 index 7044e1ec5..000000000 --- a/src/plugin_manager.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2008, Rodrigo Braz Monteiro -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of the Aegisub Group nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Aegisub Project http://www.aegisub.org/ - -/// @file plugin_manager.cpp -/// @brief Keep track of and set up variable parts of the application -/// @ingroup main -/// - -#include "config.h" - -#include "include/aegisub/audio_player.h" -#include "include/aegisub/spellchecker.h" -#include "include/aegisub/subtitles_provider.h" -#include "plugin_manager.h" -#include "auto4_lua_factory.h" - -#include - -void RegisterBuiltInPlugins() { - AudioPlayerFactory::RegisterProviders(); - SubtitlesProviderFactory::RegisterProviders(); - SpellCheckerFactory::RegisterProviders(); - Automation4::ScriptFactory::Register(agi::util::make_unique()); -} diff --git a/src/plugin_manager.h b/src/plugin_manager.h deleted file mode 100644 index 04c9f9a6e..000000000 --- a/src/plugin_manager.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2008, Rodrigo Braz Monteiro -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of the Aegisub Group nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Aegisub Project http://www.aegisub.org/ - -void RegisterBuiltInPlugins(); diff --git a/src/spellchecker.cpp b/src/spellchecker.cpp index a77c26974..5dc0a2a10 100644 --- a/src/spellchecker.cpp +++ b/src/spellchecker.cpp @@ -1,64 +1,30 @@ -// Copyright (c) 2006, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2014, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. // -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of the Aegisub Group nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // // Aegisub Project http://www.aegisub.org/ -/// @file spellchecker.cpp -/// @brief Implementatin of base-class for spell checkers -/// @ingroup spelling -/// - #include "config.h" #include "include/aegisub/spellchecker.h" #include "spellchecker_hunspell.h" -#include "options.h" - -#include +#include std::unique_ptr SpellCheckerFactory::GetSpellChecker() { - std::vector list = GetClasses(OPT_GET("Tool/Spell Checker/Backend")->GetString()); - if (list.empty()) return nullptr; - - std::string error; - for (auto const& name : list) { - try { - auto checker = Create(name); - if (checker) return checker; - } - catch (...) { error += name + " factory: Unknown error\n"; } - } - - throw error; -} - -void SpellCheckerFactory::RegisterProviders() { #ifdef WITH_HUNSPELL - Register("Hunspell"); + return agi::util::make_unique(); +#else + return {}; #endif } diff --git a/src/subtitles_provider.cpp b/src/subtitles_provider.cpp index 14167d5c1..4468ae7a5 100644 --- a/src/subtitles_provider.cpp +++ b/src/subtitles_provider.cpp @@ -1,71 +1,67 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2014, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. // -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of the Aegisub Group nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // // Aegisub Project http://www.aegisub.org/ -/// @file subtitles_provider.cpp -/// @brief Base class for subtitle renderers -/// @ingroup subtitle_rendering -/// - #include "config.h" +#include "include/aegisub/subtitles_provider.h" + +#include "factory_manager.h" #include "options.h" #include "subtitles_provider_csri.h" #include "subtitles_provider_libass.h" -#include "include/aegisub/subtitles_provider.h" + +namespace { + struct factory { + std::string name; + std::string subtype; + std::unique_ptr (*create)(std::string const& subtype); + bool hidden; + }; + + std::vector const& factories() { + static std::vector factories; + if (factories.size()) return factories; +#ifdef WITH_CSRI + for (auto const& subtype : csri::List()) + factories.push_back(factory{"CSRI/" + subtype, subtype, csri::Create, false}); +#endif + factories.push_back(factory{"libass", "", libass::Create, false}); + return factories; + } +} + +std::vector SubtitlesProviderFactory::GetClasses() { + return ::GetClasses(factories()); +} std::unique_ptr SubtitlesProviderFactory::GetProvider() { - std::vector list = GetClasses(OPT_GET("Subtitle/Provider")->GetString()); - if (list.empty()) throw std::string("No subtitle providers are available."); + auto preferred = OPT_GET("Subtitle/Provider")->GetString(); + auto sorted = GetSorted(factories(), preferred); std::string error; - for (auto const& factory : list) { + for (auto factory : sorted) { try { - size_t pos = factory.find('/'); - std::string subType = pos < factory.size() - 1 ? factory.substr(pos + 1) : ""; - auto provider = Create(factory, subType); + auto provider = factory->create(factory->subtype); if (provider) return provider; } catch (agi::UserCancelException const&) { throw; } - catch (std::string const& err) { error += factory + " factory: " + err + "\n"; } - catch (const char *err) { error += factory + " factory: " + std::string(err) + "\n"; } - catch (...) { error += factory + " factory: Unknown error\n"; } + catch (std::string const& err) { error += factory->name + ": " + err + "\n"; } + catch (const char *err) { error += factory->name + ": " + std::string(err) + "\n"; } + catch (...) { error += factory->name + ": Unknown error\n"; } } throw error; } - -void SubtitlesProviderFactory::RegisterProviders() { -#ifdef WITH_CSRI - std::vector csri_providers(CSRISubtitlesProvider::GetSubTypes()); - if (!csri_providers.empty()) - Register("CSRI", false, csri_providers); -#endif - Register("libass"); - LibassSubtitlesProvider::CacheFonts(); -} diff --git a/src/subtitles_provider_csri.cpp b/src/subtitles_provider_csri.cpp index dffd5bd3a..69f93c885 100644 --- a/src/subtitles_provider_csri.cpp +++ b/src/subtitles_provider_csri.cpp @@ -37,12 +37,15 @@ #ifdef WITH_CSRI #include "subtitles_provider_csri.h" +#include "include/aegisub/subtitles_provider.h" #include "options.h" #include "subtitle_format.h" #include "video_frame.h" #include #include +#include +#include #include #include @@ -53,9 +56,24 @@ #include +namespace { // CSRI renderers are not required to be thread safe (and VSFilter very much // is not) -static std::mutex csri_mutex; +std::mutex csri_mutex; + +class CSRISubtitlesProvider final : public SubtitlesProvider { + agi::scoped_holder instance; + csri_rend *renderer; + + /// Name of the file passed to renderers with can_open_mem false + agi::fs::path tempfile; +public: + CSRISubtitlesProvider(std::string subType); + ~CSRISubtitlesProvider(); + + void LoadSubtitles(AssFile *subs); + void DrawSubtitles(VideoFrame &dst, double time); +}; CSRISubtitlesProvider::CSRISubtitlesProvider(std::string type) : instance(nullptr, csri_close) @@ -106,8 +124,10 @@ void CSRISubtitlesProvider::DrawSubtitles(VideoFrame &dst,double time) { if (!csri_request_fmt(instance, &format)) csri_render(instance, &frame, time); } +} -std::vector CSRISubtitlesProvider::GetSubTypes() { +namespace csri { +std::vector List() { std::vector final; for (csri_rend *cur = csri_renderer_default(); cur; cur = csri_renderer_next(cur)) { std::string name(csri_renderer_info(cur)->name); @@ -119,4 +139,8 @@ std::vector CSRISubtitlesProvider::GetSubTypes() { return final; } +std::unique_ptr Create(std::string const& name) { + return agi::util::make_unique(name); +} +} #endif // WITH_CSRI diff --git a/src/subtitles_provider_csri.h b/src/subtitles_provider_csri.h index 0052fed09..6fe2f8728 100644 --- a/src/subtitles_provider_csri.h +++ b/src/subtitles_provider_csri.h @@ -1,63 +1,26 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2014, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. // -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of the Aegisub Group nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // // Aegisub Project http://www.aegisub.org/ -/// @file subtitles_provider_csri.h -/// @see subtitles_provider_csri.cpp -/// @ingroup subtitle_rendering -/// - -#ifdef WITH_CSRI -#include "include/aegisub/subtitles_provider.h" - +#include +#include #include -#include -#include +class SubtitlesProvider; -#include - -typedef void csri_rend; -typedef void csri_inst; - -class CSRISubtitlesProvider final : public SubtitlesProvider { - agi::scoped_holder instance; - csri_rend *renderer; - - /// Name of the file passed to renderers with can_open_mem false - agi::fs::path tempfile; -public: - CSRISubtitlesProvider(std::string subType); - ~CSRISubtitlesProvider(); - - void LoadSubtitles(AssFile *subs); - void DrawSubtitles(VideoFrame &dst, double time); - - static std::vector GetSubTypes(); -}; -#endif +namespace csri { + std::vector List(); + std::unique_ptr Create(std::string const& subtype); +} diff --git a/src/subtitles_provider_libass.cpp b/src/subtitles_provider_libass.cpp index 1557800c9..ed163bd2b 100644 --- a/src/subtitles_provider_libass.cpp +++ b/src/subtitles_provider_libass.cpp @@ -36,17 +36,13 @@ #include "subtitles_provider_libass.h" -#ifdef __APPLE__ -#include -#include -#endif - #include "ass_info.h" #include "ass_dialogue.h" #include "ass_file.h" #include "ass_style.h" #include "dialog_progress.h" #include "frame_main.h" +#include "include/aegisub/subtitles_provider.h" #include "main.h" #include "utils.h" #include "video_frame.h" @@ -60,6 +56,15 @@ #include #include +#ifdef __APPLE__ +#include +#include +#endif + +extern "C" { +#include +} + namespace { std::unique_ptr cache_queue; ASS_Library *library; @@ -84,9 +89,20 @@ void msg_callback(int level, const char *fmt, va_list args, void *) { #else #define CONFIG_PATH nullptr #endif -} -LibassSubtitlesProvider::LibassSubtitlesProvider(std::string) { +class LibassSubtitlesProvider final : public SubtitlesProvider { + ASS_Renderer* ass_renderer = nullptr; + ASS_Track* ass_track = nullptr; + +public: + LibassSubtitlesProvider(); + ~LibassSubtitlesProvider(); + + void LoadSubtitles(AssFile *subs) override; + void DrawSubtitles(VideoFrame &dst, double time) override; +}; + +LibassSubtitlesProvider::LibassSubtitlesProvider() { auto done = std::make_shared(false); auto renderer = std::make_shared(nullptr); cache_queue->Async([=]{ @@ -115,23 +131,21 @@ LibassSubtitlesProvider::~LibassSubtitlesProvider() { if (ass_renderer) ass_renderer_done(ass_renderer); } -namespace { - struct Writer { - std::vector data; - AssEntryGroup group = AssEntryGroup::GROUP_MAX; +struct Writer { + std::vector data; + AssEntryGroup group = AssEntryGroup::GROUP_MAX; - template - void Write(T const& list) { - for (auto const& line : list) { - if (group != line.Group()) { - group = line.Group(); - boost::push_back(data, line.GroupHeader() + "\r\n"); - } - boost::push_back(data, line.GetEntryData() + "\r\n"); + template + void Write(T const& list) { + for (auto const& line : list) { + if (group != line.Group()) { + group = line.Group(); + boost::push_back(data, line.GroupHeader() + "\r\n"); } + boost::push_back(data, line.GetEntryData() + "\r\n"); } - }; -} + } +}; void LibassSubtitlesProvider::LoadSubtitles(AssFile *subs) { Writer writer; @@ -188,8 +202,14 @@ void LibassSubtitlesProvider::DrawSubtitles(VideoFrame &frame,double time) { }); } } +} -void LibassSubtitlesProvider::CacheFonts() { +namespace libass { +std::unique_ptr Create(std::string const&) { + return agi::util::make_unique(); +} + +void CacheFonts() { // Initialize the cache worker thread cache_queue = agi::dispatch::Create(); @@ -204,3 +224,4 @@ void LibassSubtitlesProvider::CacheFonts() { ass_renderer_done(ass_renderer); }); } +} diff --git a/src/subtitles_provider_libass.h b/src/subtitles_provider_libass.h index 1c497c92c..e5dbd57b5 100644 --- a/src/subtitles_provider_libass.h +++ b/src/subtitles_provider_libass.h @@ -1,53 +1,25 @@ -// Copyright (c) 2006-2007, Rodrigo Braz Monteiro, Evgeniy Stepanov -// All rights reserved. +// Copyright (c) 2014, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. // -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of the Aegisub Group nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // // Aegisub Project http://www.aegisub.org/ -/// @file subtitles_provider_libass.h -/// @see subtitles_provider_libass.cpp -/// @ingroup subtitle_rendering -/// +#include +#include -#include "include/aegisub/subtitles_provider.h" +class SubtitlesProvider; -extern "C" { -#include +namespace libass { + std::unique_ptr Create(std::string const&); + void CacheFonts(); } - -class LibassSubtitlesProvider final : public SubtitlesProvider { - ASS_Renderer* ass_renderer = nullptr; - ASS_Track* ass_track = nullptr; - -public: - LibassSubtitlesProvider(std::string); - ~LibassSubtitlesProvider(); - - void LoadSubtitles(AssFile *subs) override; - void DrawSubtitles(VideoFrame &dst, double time) override; - - static void CacheFonts(); -};