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)
{
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