Refactor the rest of the factories

This commit is contained in:
Thomas Goyne 2014-03-24 07:54:22 -07:00
parent dbe9bcfdad
commit e71270f0f0
30 changed files with 738 additions and 1240 deletions

View file

@ -110,13 +110,7 @@
<ClInclude Include="$(SrcDir)audio_display.h" />
<ClInclude Include="$(SrcDir)audio_karaoke.h" />
<ClInclude Include="$(SrcDir)audio_marker.h" />
<ClInclude Include="$(SrcDir)audio_player_alsa.h" />
<ClInclude Include="$(SrcDir)audio_player_dsound.h" />
<ClInclude Include="$(SrcDir)audio_player_dsound2.h" />
<ClInclude Include="$(SrcDir)audio_player_openal.h" />
<ClInclude Include="$(SrcDir)audio_player_oss.h" />
<ClInclude Include="$(SrcDir)audio_player_portaudio.h" />
<ClInclude Include="$(SrcDir)audio_player_pulse.h" />
<ClInclude Include="$(SrcDir)audio_renderer.h" />
<ClInclude Include="$(SrcDir)audio_renderer_spectrum.h" />
<ClInclude Include="$(SrcDir)audio_renderer_waveform.h" />
@ -197,7 +191,6 @@
<ClInclude Include="$(SrcDir)pen.h" />
<ClInclude Include="$(SrcDir)persist_location.h" />
<ClInclude Include="$(SrcDir)placeholder_ctrl.h" />
<ClInclude Include="$(SrcDir)plugin_manager.h" />
<ClInclude Include="$(SrcDir)preferences.h" />
<ClInclude Include="$(SrcDir)preferences_base.h" />
<ClInclude Include="$(SrcDir)resolution_resampler.h" />
@ -391,7 +384,6 @@
<ClCompile Include="$(SrcDir)mkv_wrap.cpp" />
<ClCompile Include="$(SrcDir)pen.cpp" />
<ClCompile Include="$(SrcDir)persist_location.cpp" />
<ClCompile Include="$(SrcDir)plugin_manager.cpp" />
<ClCompile Include="$(SrcDir)preferences.cpp" />
<ClCompile Include="$(SrcDir)preferences_base.cpp" />
<ClCompile Include="$(SrcDir)resolution_resampler.cpp" />

View file

@ -213,24 +213,6 @@
<ClInclude Include="$(SrcDir)config.h">
<Filter>Config</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)audio_player_pulse.h">
<Filter>Audio\Players</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)audio_player_alsa.h">
<Filter>Audio\Players</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)audio_player_dsound.h">
<Filter>Audio\Players</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)audio_player_dsound2.h">
<Filter>Audio\Players</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)audio_player_openal.h">
<Filter>Audio\Players</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)audio_player_oss.h">
<Filter>Audio\Players</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)audio_player_portaudio.h">
<Filter>Audio\Players</Filter>
</ClInclude>
@ -528,9 +510,6 @@
<ClInclude Include="$(SrcDir)include\aegisub\video_provider.h">
<Filter>Video\Providers</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)plugin_manager.h">
<Filter>Utilities</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)spline_curve.h">
<Filter>Video\Visual tools</Filter>
</ClInclude>
@ -1055,9 +1034,6 @@
<ClCompile Include="$(SrcDir)menu.cpp">
<Filter>Main UI</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)plugin_manager.cpp">
<Filter>Utilities</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)spline.cpp">
<Filter>Video\Visual tools</Filter>
</ClCompile>

View file

@ -203,7 +203,6 @@ SRC += \
mkv_wrap.cpp \
pen.cpp \
persist_location.cpp \
plugin_manager.cpp \
preferences.cpp \
preferences_base.cpp \
resolution_resampler.cpp \

View file

@ -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 <boost/range/iterator_range.hpp>
AudioPlayer::AudioPlayer(AudioProvider *provider)
: provider(provider)
{
}
std::unique_ptr<AudioPlayer> CreateAlsaPlayer(AudioProvider *providers);
std::unique_ptr<AudioPlayer> CreateDirectSoundPlayer(AudioProvider *providers);
std::unique_ptr<AudioPlayer> CreateDirectSound2Player(AudioProvider *providers);
std::unique_ptr<AudioPlayer> CreateOpenALPlayer(AudioProvider *providers);
std::unique_ptr<AudioPlayer> CreatePortAudioPlayer(AudioProvider *providers);
std::unique_ptr<AudioPlayer> CreatePulseAudioPlayer(AudioProvider *providers);
std::unique_ptr<AudioPlayer> CreateOSSPlayer(AudioProvider *providers);
namespace {
struct factory {
const char *name;
std::unique_ptr<AudioPlayer> (*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<std::string> AudioPlayerFactory::GetClasses() {
return ::GetClasses(boost::make_iterator_range(std::begin(factories), std::end(factories)));
}
std::unique_ptr<AudioPlayer> AudioPlayerFactory::GetAudioPlayer(AudioProvider *provider) {
std::vector<std::string> 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<AlsaPlayer>("ALSA");
#endif
#ifdef WITH_DIRECTSOUND
Register<DirectSoundPlayer>("DirectSound-old");
Register<DirectSoundPlayer2>("DirectSound");
#endif
#ifdef WITH_OPENAL
Register<OpenALPlayer>("OpenAL");
#endif
#ifdef WITH_PORTAUDIO
Register<PortAudioPlayer>("PortAudio");
#endif
#ifdef WITH_LIBPULSE
Register<PulseAudioPlayer>("PulseAudio");
#endif
#ifdef WITH_OSS
Register<OSSPlayer>("OSS");
#endif
}

View file

@ -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 <libaegisub/util.h>
#include <algorithm>
#include <alsa/asoundlib.h>
#include <inttypes.h>
#include <memory>
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<PlaybackState *>(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<PlaybackState>())
{
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<AudioPlayer> CreateAlsaPlayer(AudioProvider *provider)
{
return agi::util::make_unique<AlsaPlayer>(provider);
}
#endif // WITH_ALSA

View file

@ -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 <alsa/asoundlib.h>
#include <memory>
struct PlaybackState;
class AlsaPlayer final : public AudioPlayer {
std::unique_ptr<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);
};
#endif

View file

@ -35,28 +35,72 @@
#include "config.h"
#ifdef WITH_DIRECTSOUND
#include <libaegisub/log.h>
#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 <libaegisub/log.h>
#include <libaegisub/util.h>
#include <mmsystem.h>
#include <dsound.h>
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<AudioPlayer> CreateDirectSoundPlayer(AudioProvider *provider) {
return agi::util::make_unique<DirectSoundPlayer>(provider);
}
#endif // WITH_DIRECTSOUND

View file

@ -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 <mmsystem.h>
#include <dsound.h>
#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

View file

@ -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 <process.h>
#include <dsound.h>
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<DirectSoundPlayer2Thread> 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<AudioPlayer> CreateDirectSound2Player(AudioProvider *provider) {
return agi::util::make_unique<DirectSoundPlayer2>(provider);
}
#endif // WITH_DIRECTSOUND

View file

@ -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<DirectSoundPlayer2Thread> 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

View file

@ -35,14 +35,29 @@
#include "config.h"
#ifdef WITH_OPENAL
#include <libaegisub/log.h>
#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 <libaegisub/log.h>
#include <libaegisub/util.h>
#ifdef __WINDOWS__
#include <al.h>
#include <alc.h>
#elif defined(__APPLE__)
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
#endif
#include <vector>
#include <wx/timer.h>
// 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<char> 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<AudioPlayer> CreateOpenALPlayer(AudioProvider *provider)
{
return agi::util::make_unique<OpenALPlayer>(provider);
}
#endif // WITH_OPENAL

View file

@ -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 <al.h>
#include <alc.h>
#elif defined(__APPLE__)
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
#endif
#include <vector>
#include <wx/timer.h>
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<char> 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

View file

@ -33,12 +33,7 @@
#include "config.h"
#ifdef WITH_OSS
#include <sys/param.h>
#include <libaegisub/log.h>
#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 <libaegisub/log.h>
#include <libaegisub/util.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <wx/thread.h>
#ifdef HAVE_SOUNDCARD_H
# include <soundcard.h>
#elif defined(HAVE_SYS_SOUNDCARD_H)
# include <sys/soundcard.h>
#endif
namespace {
DEFINE_SIMPLE_EXCEPTION(OSSError, agi::AudioPlayerOpenError, "audio/player/open/oss")
class OSSPlayerThread;
OSSPlayer::OSSPlayer(AudioProvider *provider)
: AudioPlayer(provider)
{
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)
: AudioPlayer(provider)
{
OpenStream();
}
}
OSSPlayer::~OSSPlayer()
{
~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<AudioPlayer> CreateOSSPlayer(AudioProvider *provider) {
return agi::util::make_unique<OSSPlayer>(provider);
}
#endif // WITH_OSS

View file

@ -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 <sys/ioctl.h>
#include <wx/thread.h>
#include <fcntl.h>
#ifdef HAVE_SOUNDCARD_H
# include <soundcard.h>
#else
# ifdef HAVE_SYS_SOUNDCARD_H
# include <sys/soundcard.h>
# 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

View file

@ -32,13 +32,9 @@
/// @ingroup audio_output
///
#include "config.h"
#ifdef WITH_PORTAUDIO
#include <libaegisub/log.h>
#include "audio_player_portaudio.h"
#include "audio_controller.h"
@ -47,6 +43,9 @@
#include "options.h"
#include "utils.h"
#include <libaegisub/log.h>
#include <libaegisub/util.h>
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<AudioPlayer> CreatePortAudioPlayer(AudioProvider *provider) {
return agi::util::make_unique<PortAudioPlayer>(provider);
}
#endif // WITH_PORTAUDIO

View file

@ -35,18 +35,74 @@
#include "config.h"
#ifdef WITH_LIBPULSE
#include <cstdio>
#include <wx/thread.h>
#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 <libaegisub/log.h>
#include <libaegisub/util.h>
#include <cstdio>
#include <pulse/pulseaudio.h>
#include <wx/thread.h>
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<AudioPlayer> CreatePulseAudioPlayer(AudioProvider *provider) {
return agi::util::make_unique<PulseAudioPlayer>(provider);
}
#endif // WITH_LIBPULSE

View file

@ -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 <pulse/pulseaudio.h>
#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

View file

@ -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 <algorithm>
#include <map>
#include <memory>
#include <string>
#include <vector>
namespace agi { class ProgressSink; }
template<typename Base, typename... Args>
class Factory {
typedef Base *(*func)(Args const&...);
typedef std::map<std::string, std::pair<bool, func>> map;
static map& classes() {
static map classes;
return classes;
}
public:
static std::vector<std::string> GetClasses(std::string favorite="") {
std::vector<std::string> 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<Base> Create(std::string const& name, Args const&... args) {
auto factory = classes().find(name);
if (factory == classes().end()) return nullptr;
return std::unique_ptr<Base>(factory->second.second(args...));
}
template<class T>
static void Register(std::string name, bool hide = false, std::vector<std::string> 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<typename Container>
std::vector<std::string> GetClasses(Container const& c) {

View file

@ -34,7 +34,10 @@
#pragma once
#include "factory_manager.h"
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
class AudioProvider;
@ -57,8 +60,7 @@ public:
virtual void SetEndPosition(int64_t pos)=0;
};
class AudioPlayerFactory final : public Factory<AudioPlayer, AudioProvider*> {
public:
static void RegisterProviders();
struct AudioPlayerFactory {
static std::vector<std::string> GetClasses();
static std::unique_ptr<AudioPlayer> GetAudioPlayer(AudioProvider *provider);
};

View file

@ -19,12 +19,10 @@
/// @ingroup main_headers spelling
///
#include "factory_manager.h"
#include <memory>
namespace agi { class SpellChecker; }
class SpellCheckerFactory final : public Factory<agi::SpellChecker> {
public:
struct SpellCheckerFactory {
static std::unique_ptr<agi::SpellChecker> GetSpellChecker();
static void RegisterProviders();
};

View file

@ -34,21 +34,21 @@
#pragma once
#include "factory_manager.h"
#include <memory>
#include <string>
#include <vector>
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<SubtitlesProvider, std::string> {
public:
struct SubtitlesProviderFactory {
static std::unique_ptr<SubtitlesProvider> GetProvider();
static void RegisterProviders();
static std::vector<std::string> GetClasses();
};

View file

@ -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<Automation4::LuaScriptFactory>());
libass::CacheFonts();
// Load Automation scripts
StartupLog("Load global Automation scripts");

View file

@ -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 <libaegisub/util.h>
void RegisterBuiltInPlugins() {
AudioPlayerFactory::RegisterProviders();
SubtitlesProviderFactory::RegisterProviders();
SpellCheckerFactory::RegisterProviders();
Automation4::ScriptFactory::Register(agi::util::make_unique<Automation4::LuaScriptFactory>());
}

View file

@ -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();

View file

@ -1,64 +1,30 @@
// Copyright (c) 2006, Rodrigo Braz Monteiro
// All rights reserved.
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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 <libaegisub/spellchecker.h>
#include <libaegisub/util.h>
std::unique_ptr<agi::SpellChecker> SpellCheckerFactory::GetSpellChecker() {
std::vector<std::string> 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<HunspellSpellChecker>("Hunspell");
return agi::util::make_unique<HunspellSpellChecker>();
#else
return {};
#endif
}

View file

@ -1,71 +1,67 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro
// All rights reserved.
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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<SubtitlesProvider> (*create)(std::string const& subtype);
bool hidden;
};
std::vector<factory> const& factories() {
static std::vector<factory> 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<std::string> SubtitlesProviderFactory::GetClasses() {
return ::GetClasses(factories());
}
std::unique_ptr<SubtitlesProvider> SubtitlesProviderFactory::GetProvider() {
std::vector<std::string> 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<std::string> csri_providers(CSRISubtitlesProvider::GetSubTypes());
if (!csri_providers.empty())
Register<CSRISubtitlesProvider>("CSRI", false, csri_providers);
#endif
Register<LibassSubtitlesProvider>("libass");
LibassSubtitlesProvider::CacheFonts();
}

View file

@ -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 <libaegisub/fs.h>
#include <libaegisub/path.h>
#include <libaegisub/scoped_ptr.h>
#include <libaegisub/util.h>
#include <boost/filesystem.hpp>
#include <mutex>
@ -53,9 +56,24 @@
#include <csri/csri.h>
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<csri_inst*> 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<std::string> CSRISubtitlesProvider::GetSubTypes() {
namespace csri {
std::vector<std::string> List() {
std::vector<std::string> 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<std::string> CSRISubtitlesProvider::GetSubTypes() {
return final;
}
std::unique_ptr<SubtitlesProvider> Create(std::string const& name) {
return agi::util::make_unique<CSRISubtitlesProvider>(name);
}
}
#endif // WITH_CSRI

View file

@ -1,63 +1,26 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro
// All rights reserved.
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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 <memory>
#include <string>
#include <vector>
#include <libaegisub/fs_fwd.h>
#include <libaegisub/scoped_ptr.h>
class SubtitlesProvider;
#include <boost/filesystem/path.hpp>
typedef void csri_rend;
typedef void csri_inst;
class CSRISubtitlesProvider final : public SubtitlesProvider {
agi::scoped_holder<csri_inst*> 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<std::string> GetSubTypes();
};
#endif
namespace csri {
std::vector<std::string> List();
std::unique_ptr<SubtitlesProvider> Create(std::string const& subtype);
}

View file

@ -36,17 +36,13 @@
#include "subtitles_provider_libass.h"
#ifdef __APPLE__
#include <sys/param.h>
#include <libaegisub/util_osx.h>
#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 <memory>
#include <mutex>
#ifdef __APPLE__
#include <sys/param.h>
#include <libaegisub/util_osx.h>
#endif
extern "C" {
#include <ass/ass.h>
}
namespace {
std::unique_ptr<agi::dispatch::Queue> 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<bool>(false);
auto renderer = std::make_shared<ASS_Renderer*>(nullptr);
cache_queue->Async([=]{
@ -115,8 +131,7 @@ LibassSubtitlesProvider::~LibassSubtitlesProvider() {
if (ass_renderer) ass_renderer_done(ass_renderer);
}
namespace {
struct Writer {
struct Writer {
std::vector<char> data;
AssEntryGroup group = AssEntryGroup::GROUP_MAX;
@ -130,8 +145,7 @@ namespace {
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<SubtitlesProvider> Create(std::string const&) {
return agi::util::make_unique<LibassSubtitlesProvider>();
}
void CacheFonts() {
// Initialize the cache worker thread
cache_queue = agi::dispatch::Create();
@ -204,3 +224,4 @@ void LibassSubtitlesProvider::CacheFonts() {
ass_renderer_done(ass_renderer);
});
}
}

View file

@ -1,53 +1,25 @@
// Copyright (c) 2006-2007, Rodrigo Braz Monteiro, Evgeniy Stepanov
// All rights reserved.
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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 <memory>
#include <string>
#include "include/aegisub/subtitles_provider.h"
class SubtitlesProvider;
extern "C" {
#include <ass/ass.h>
namespace libass {
std::unique_ptr<SubtitlesProvider> 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();
};