Remove AudioPlayer::OpenStream and CloseStream

Instead, just pass the provider to the constructor and let the player
worry about when to create its things. The ability to explicitly open
and close the playback wasn't actually required for anything, and it
complicated the implementations of the players.
This commit is contained in:
Thomas Goyne 2012-03-19 16:31:33 -07:00
parent 6e90d9498d
commit 95a1b7e9b5
17 changed files with 131 additions and 339 deletions

View File

@ -114,14 +114,24 @@ void AudioController::OnPlaybackTimer(wxTimerEvent &)
void AudioController::OnComputerSuspending(wxPowerEvent &) void AudioController::OnComputerSuspending(wxPowerEvent &)
{ {
Stop(); Stop();
player->CloseStream(); delete player;
player = 0;
} }
void AudioController::OnComputerResuming(wxPowerEvent &) void AudioController::OnComputerResuming(wxPowerEvent &)
{ {
if (provider) if (provider)
player->OpenStream(); {
try
{
player = AudioPlayerFactory::GetAudioPlayer(provider);
}
catch (...)
{
CloseAudio();
}
}
} }
#endif #endif
@ -135,9 +145,7 @@ void AudioController::OnAudioPlayerChanged()
try try
{ {
player = AudioPlayerFactory::GetAudioPlayer(); player = AudioPlayerFactory::GetAudioPlayer(provider);
player->SetProvider(provider);
player->OpenStream();
} }
catch (...) catch (...)
{ {
@ -238,15 +246,11 @@ void AudioController::OpenAudio(const wxString &url)
try try
{ {
player = AudioPlayerFactory::GetAudioPlayer(); player = AudioPlayerFactory::GetAudioPlayer(provider);
player->SetProvider(provider);
player->OpenStream();
} }
catch (...) catch (...)
{ {
delete player;
delete provider; delete provider;
player = 0;
provider = 0; provider = 0;
throw; throw;
} }

View File

@ -60,18 +60,19 @@
#include "compat.h" #include "compat.h"
#include "main.h" #include "main.h"
AudioPlayer::AudioPlayer() { AudioPlayer::AudioPlayer(AudioProvider *provider)
provider = NULL; : provider(provider)
{
} }
AudioPlayer* AudioPlayerFactory::GetAudioPlayer() { AudioPlayer* AudioPlayerFactory::GetAudioPlayer(AudioProvider *provider) {
std::vector<std::string> list = GetClasses(OPT_GET("Audio/Player")->GetString()); std::vector<std::string> list = GetClasses(OPT_GET("Audio/Player")->GetString());
if (list.empty()) throw agi::NoAudioPlayersError("No audio players are available.", 0); if (list.empty()) throw agi::NoAudioPlayersError("No audio players are available.", 0);
std::string error; std::string error;
for (size_t i = 0; i < list.size(); ++i) { for (size_t i = 0; i < list.size(); ++i) {
try { try {
return Create(list[i]); return Create(list[i], provider);
} }
catch (agi::AudioPlayerOpenError const& err) { catch (agi::AudioPlayerOpenError const& err) {
error += list[i] + " factory: " + err.GetChainedMessage() + "\n"; error += list[i] + " factory: " + err.GetChainedMessage() + "\n";
@ -102,4 +103,4 @@ void AudioPlayerFactory::RegisterProviders() {
#endif #endif
} }
template<> AudioPlayerFactory::map *FactoryBase<AudioPlayer *(*)()>::classes = NULL; template<> AudioPlayerFactory::map *FactoryBase<AudioPlayer *(*)(AudioProvider*)>::classes = NULL;

View File

@ -366,42 +366,22 @@ do_setup:
} }
AlsaPlayer::AlsaPlayer() AlsaPlayer::AlsaPlayer(AudioProvider *provider)
: ps(new PlaybackState) : AudioPlayer(provider)
, ps(new PlaybackState)
{ {
open = false;
}
AlsaPlayer::~AlsaPlayer()
{
CloseStream();
}
void AlsaPlayer::OpenStream()
{
if (open) return;
CloseStream();
ps->Reset();
ps->provider = provider; ps->provider = provider;
wxString device_name = lagi_wxString(OPT_GET("Player/Audio/ALSA/Device")->GetString()); wxString device_name = lagi_wxString(OPT_GET("Player/Audio/ALSA/Device")->GetString());
ps->device_name = std::string(device_name.utf8_str()); ps->device_name = std::string(device_name.utf8_str());
if (pthread_create(&thread, 0, &playback_thread, ps.get()) == 0) if (pthread_create(&thread, 0, &playback_thread, ps.get()) != 0)
open = true;
else
throw agi::AudioPlayerOpenError("AlsaPlayer: Creating the playback thread failed", 0); throw agi::AudioPlayerOpenError("AlsaPlayer: Creating the playback thread failed", 0);
} }
void AlsaPlayer::CloseStream() AlsaPlayer::~AlsaPlayer()
{ {
if (!open) return;
{ {
PthreadMutexLocker ml(ps->mutex); PthreadMutexLocker ml(ps->mutex);
ps->signal_stop = true; ps->signal_stop = true;
@ -411,15 +391,11 @@ void AlsaPlayer::CloseStream()
} }
pthread_join(thread, 0); // FIXME: check for errors pthread_join(thread, 0); // FIXME: check for errors
open = false;
} }
void AlsaPlayer::Play(int64_t start, int64_t count) void AlsaPlayer::Play(int64_t start, int64_t count)
{ {
OpenStream();
PthreadMutexLocker ml(ps->mutex); PthreadMutexLocker ml(ps->mutex);
ps->signal_start = true; ps->signal_start = true;
ps->signal_stop = true; // make sure to stop any ongoing playback first ps->signal_stop = true; // make sure to stop any ongoing playback first
@ -431,8 +407,6 @@ void AlsaPlayer::Play(int64_t start, int64_t count)
void AlsaPlayer::Stop() void AlsaPlayer::Stop()
{ {
if (!open) return;
PthreadMutexLocker ml(ps->mutex); PthreadMutexLocker ml(ps->mutex);
ps->signal_stop = true; ps->signal_stop = true;
LOG_D("audio/player/alsa") << "stop stream, stop signal"; LOG_D("audio/player/alsa") << "stop stream, stop signal";
@ -442,13 +416,12 @@ void AlsaPlayer::Stop()
bool AlsaPlayer::IsPlaying() bool AlsaPlayer::IsPlaying()
{ {
PthreadMutexLocker ml(ps->mutex); PthreadMutexLocker ml(ps->mutex);
return open && ps->playing; return ps->playing;
} }
void AlsaPlayer::SetEndPosition(int64_t pos) void AlsaPlayer::SetEndPosition(int64_t pos)
{ {
if (!open) return;
PthreadMutexLocker ml(ps->mutex); PthreadMutexLocker ml(ps->mutex);
ps->end_position = pos; ps->end_position = pos;
} }
@ -456,8 +429,6 @@ void AlsaPlayer::SetEndPosition(int64_t pos)
void AlsaPlayer::SetCurrentPosition(int64_t pos) void AlsaPlayer::SetCurrentPosition(int64_t pos)
{ {
if (!open) return;
PthreadMutexLocker ml(ps->mutex); PthreadMutexLocker ml(ps->mutex);
if (!ps->playing) return; if (!ps->playing) return;
@ -471,14 +442,12 @@ void AlsaPlayer::SetCurrentPosition(int64_t pos)
int64_t AlsaPlayer::GetStartPosition() int64_t AlsaPlayer::GetStartPosition()
{ {
if (!open) return 0;
PthreadMutexLocker ml(ps->mutex); PthreadMutexLocker ml(ps->mutex);
return ps->start_position; return ps->start_position;
} }
int64_t AlsaPlayer::GetEndPosition() int64_t AlsaPlayer::GetEndPosition()
{ {
if (!open) return 0;
PthreadMutexLocker ml(ps->mutex); PthreadMutexLocker ml(ps->mutex);
return ps->end_position; return ps->end_position;
} }
@ -486,8 +455,6 @@ int64_t AlsaPlayer::GetEndPosition()
int64_t AlsaPlayer::GetCurrentPosition() int64_t AlsaPlayer::GetCurrentPosition()
{ {
if (!open) return 0;
int64_t lastpos; int64_t lastpos;
timespec lasttime; timespec lasttime;
int64_t samplerate; int64_t samplerate;
@ -516,8 +483,6 @@ int64_t AlsaPlayer::GetCurrentPosition()
void AlsaPlayer::SetVolume(double vol) void AlsaPlayer::SetVolume(double vol)
{ {
if (!open) return;
PthreadMutexLocker ml(ps->mutex); PthreadMutexLocker ml(ps->mutex);
ps->volume = vol; ps->volume = vol;
ps->signal_volume = true; ps->signal_volume = true;
@ -527,8 +492,6 @@ void AlsaPlayer::SetVolume(double vol)
double AlsaPlayer::GetVolume() double AlsaPlayer::GetVolume()
{ {
if (!open) return 1.0;
PthreadMutexLocker ml(ps->mutex); PthreadMutexLocker ml(ps->mutex);
return ps->volume; return ps->volume;
} }

View File

@ -47,15 +47,11 @@ struct PlaybackState;
class AlsaPlayer : public AudioPlayer { class AlsaPlayer : public AudioPlayer {
agi::scoped_ptr<PlaybackState> ps; agi::scoped_ptr<PlaybackState> ps;
pthread_t thread; pthread_t thread;
bool open;
public: public:
AlsaPlayer(); AlsaPlayer(AudioProvider *provider);
~AlsaPlayer(); ~AlsaPlayer();
void OpenStream();
void CloseStream();
void Play(int64_t start, int64_t count); void Play(int64_t start, int64_t count);
void Stop(); void Stop();
bool IsPlaying(); bool IsPlaying();

View File

@ -46,30 +46,20 @@
#include "main.h" #include "main.h"
#include "utils.h" #include "utils.h"
/// @brief Constructor DirectSoundPlayer::DirectSoundPlayer(AudioProvider *provider)
/// : AudioPlayer(provider)
DirectSoundPlayer::DirectSoundPlayer() { , playing(false)
playing = false; , volume(1.0f)
volume = 1.0f; , offset(0)
playPos = 0; , bufSize(0)
startPos = 0; , playPos(0)
endPos = 0; , startPos(0)
offset = 0; , endPos(0)
, startTime(0)
buffer = NULL; , directSound(0)
directSound = NULL; , buffer(0)
thread = NULL; , thread(0)
} {
/// @brief Destructor
///
DirectSoundPlayer::~DirectSoundPlayer() {
CloseStream();
}
/// @brief Open stream
///
void DirectSoundPlayer::OpenStream() {
// Initialize the DirectSound object // Initialize the DirectSound object
HRESULT res; HRESULT res;
res = DirectSoundCreate8(&DSDEVID_DefaultPlayback,&directSound,NULL); // TODO: support selecting audio device res = DirectSoundCreate8(&DSDEVID_DefaultPlayback,&directSound,NULL); // TODO: support selecting audio device
@ -114,10 +104,7 @@ void DirectSoundPlayer::OpenStream() {
offset = 0; offset = 0;
} }
/// @brief Close stream DirectSoundPlayer::~DirectSoundPlayer() {
///
void DirectSoundPlayer::CloseStream() {
// Stop it
Stop(); Stop();
// Unref the DirectSound buffer // Unref the DirectSound buffer

View File

@ -115,42 +115,21 @@ private:
DirectSoundPlayerThread *thread; DirectSoundPlayerThread *thread;
public: public:
DirectSoundPlayer(); DirectSoundPlayer(AudioProvider *provider);
~DirectSoundPlayer(); ~DirectSoundPlayer();
void OpenStream();
void CloseStream();
void Play(int64_t start,int64_t count); void Play(int64_t start,int64_t count);
void Stop(); void Stop();
/// @brief DOCME
/// @return
///
bool IsPlaying() { return playing; } bool IsPlaying() { return playing; }
/// @brief DOCME
/// @return
///
int64_t GetStartPosition() { return startPos; } int64_t GetStartPosition() { return startPos; }
/// @brief DOCME
/// @return
///
int64_t GetEndPosition() { return endPos; } int64_t GetEndPosition() { return endPos; }
int64_t GetCurrentPosition(); int64_t GetCurrentPosition();
void SetEndPosition(int64_t pos); void SetEndPosition(int64_t pos);
void SetCurrentPosition(int64_t pos); void SetCurrentPosition(int64_t pos);
/// @brief DOCME
/// @param vol
/// @return
///
void SetVolume(double vol) { volume = vol; } void SetVolume(double vol) { volume = vol; }
/// @brief DOCME
/// @return
///
double GetVolume() { return volume; } double GetVolume() { return volume; }
}; };
#endif #endif

View File

@ -801,7 +801,8 @@ bool DirectSoundPlayer2Thread::IsDead()
} }
} }
DirectSoundPlayer2::DirectSoundPlayer2() DirectSoundPlayer2::DirectSoundPlayer2(AudioProvider *provider)
: AudioPlayer(provider)
{ {
// The buffer will hold BufferLength times WantedLatency milliseconds of audio // The buffer will hold BufferLength times WantedLatency milliseconds of audio
WantedLatency = OPT_GET("Player/Audio/DirectSound/Buffer Latency")->GetInt(); WantedLatency = OPT_GET("Player/Audio/DirectSound/Buffer Latency")->GetInt();
@ -812,6 +813,16 @@ DirectSoundPlayer2::DirectSoundPlayer2()
WantedLatency = 100; WantedLatency = 100;
if (BufferLength <= 0) if (BufferLength <= 0)
BufferLength = 5; BufferLength = 5;
try
{
thread.reset(new DirectSoundPlayer2Thread(provider, WantedLatency, BufferLength));
}
catch (const char *msg)
{
LOG_E("audio/player/dsound") << msg;
throw agi::AudioPlayerOpenError(msg, 0);
}
} }
DirectSoundPlayer2::~DirectSoundPlayer2() DirectSoundPlayer2::~DirectSoundPlayer2()
@ -828,47 +839,10 @@ bool DirectSoundPlayer2::IsThreadAlive()
return thread; return thread;
} }
void DirectSoundPlayer2::OpenStream()
{
if (IsThreadAlive()) return;
try
{
thread.reset(new DirectSoundPlayer2Thread(provider, WantedLatency, BufferLength));
}
catch (const char *msg)
{
LOG_E("audio/player/dsound") << msg;
}
}
void DirectSoundPlayer2::CloseStream()
{
thread.reset();
}
void DirectSoundPlayer2::SetProvider(AudioProvider *new_provider)
{
try
{
if (IsThreadAlive() && new_provider != provider)
{
thread.reset(new DirectSoundPlayer2Thread(new_provider, WantedLatency, BufferLength));
}
AudioPlayer::SetProvider(new_provider);
}
catch (const char *msg)
{
LOG_E("audio/player/dsound") << msg;
}
}
void DirectSoundPlayer2::Play(int64_t start,int64_t count) void DirectSoundPlayer2::Play(int64_t start,int64_t count)
{ {
try try
{ {
OpenStream();
thread->Play(start, count); thread->Play(start, count);
} }
catch (const char *msg) catch (const char *msg)

View File

@ -64,23 +64,10 @@ class DirectSoundPlayer2 : public AudioPlayer {
public: public:
/// @brief Constructor /// @brief Constructor
DirectSoundPlayer2(); DirectSoundPlayer2(AudioProvider *provider);
/// @brief Destructor /// @brief Destructor
~DirectSoundPlayer2(); ~DirectSoundPlayer2();
/// @brief Prepare for playback
///
/// This means creating the playback thread
void OpenStream();
/// @brief Shutdown playback
void CloseStream();
/// @brief Change audio provider used
/// @param provider New audio provider to use
///
/// Will re-create the playback thread if the provider changed and playback was open
void SetProvider(AudioProvider *provider);
/// @brief Start playback /// @brief Start playback
/// @param start First audio frame to play /// @param start First audio frame to play
/// @param count Number of audio frames to play /// @param count Number of audio frames to play

View File

@ -52,8 +52,8 @@
DEFINE_SIMPLE_EXCEPTION(OpenALException, agi::AudioPlayerOpenError, "audio/open/player/openal") DEFINE_SIMPLE_EXCEPTION(OpenALException, agi::AudioPlayerOpenError, "audio/open/player/openal")
OpenALPlayer::OpenALPlayer() OpenALPlayer::OpenALPlayer(AudioProvider *provider)
: open(false) : AudioPlayer(provider)
, playing(false) , playing(false)
, volume(1.f) , volume(1.f)
, samplerate(0) , samplerate(0)
@ -64,17 +64,6 @@ OpenALPlayer::OpenALPlayer()
, device(0) , device(0)
, context(0) , context(0)
{ {
}
OpenALPlayer::~OpenALPlayer()
{
CloseStream();
}
void OpenALPlayer::OpenStream()
{
CloseStream();
bpf = provider->GetChannels() * provider->GetBytesPerSample(); bpf = provider->GetChannels() * provider->GetBytesPerSample();
try { try {
// Open device // Open device
@ -112,27 +101,16 @@ void OpenALPlayer::OpenStream()
// Determine buffer length // Determine buffer length
samplerate = provider->GetSampleRate(); samplerate = provider->GetSampleRate();
decode_buffer.resize(samplerate * bpf / num_buffers / 2); // buffers for half a second of audio decode_buffer.resize(samplerate * bpf / num_buffers / 2); // buffers for half a second of audio
// Now ready
open = true;
} }
void OpenALPlayer::CloseStream() OpenALPlayer::~OpenALPlayer()
{ {
if (!open) return;
Stop(); Stop();
alDeleteSources(1, &source); alDeleteSources(1, &source);
alDeleteBuffers(num_buffers, buffers); alDeleteBuffers(num_buffers, buffers);
alcDestroyContext(context); alcDestroyContext(context);
alcCloseDevice(device); alcCloseDevice(device);
context = 0;
device = 0;
// No longer working
open = false;
} }
void OpenALPlayer::Play(int64_t start, int64_t count) void OpenALPlayer::Play(int64_t start, int64_t count)
@ -165,7 +143,6 @@ void OpenALPlayer::Play(int64_t start, int64_t count)
void OpenALPlayer::Stop() void OpenALPlayer::Stop()
{ {
if (!open) return;
if (!playing) return; if (!playing) return;
// Reset data // Reset data

View File

@ -64,7 +64,6 @@ class OpenALPlayer : public AudioPlayer, wxTimer {
/// Number of OpenAL buffers to use /// Number of OpenAL buffers to use
static const ALsizei num_buffers = 8; static const ALsizei num_buffers = 8;
bool open; ///< Is the player ready to play?
bool playing; ///< Is audio currently playing? bool playing; ///< Is audio currently playing?
float volume; ///< Current audio volume float volume; ///< Current audio volume
@ -106,12 +105,9 @@ protected:
void Notify(); void Notify();
public: public:
OpenALPlayer(); OpenALPlayer(AudioProvider *provider);
~OpenALPlayer(); ~OpenALPlayer();
void OpenStream();
void CloseStream();
void Play(int64_t start,int64_t count); void Play(int64_t start,int64_t count);
void Stop(); void Stop();
bool IsPlaying() { return playing; } bool IsPlaying() { return playing; }

View File

@ -52,25 +52,29 @@
DEFINE_SIMPLE_EXCEPTION(OSSError, agi::AudioPlayerOpenError, "audio/player/open/oss") DEFINE_SIMPLE_EXCEPTION(OSSError, agi::AudioPlayerOpenError, "audio/player/open/oss")
OSSPlayer::OSSPlayer() OSSPlayer::OSSPlayer(AudioProvider *provider)
: AudioPlayer(provider)
, rate(0)
, thread(0)
, playing(false)
, volume(1.0f)
, start_frame(0)
, cur_frame(0)
, end_frame(0)
, bpf(0)
, dspdev(0)
{ {
volume = 1.0f; OpenStream();
open = false;
playing = false;
start_frame = cur_frame = end_frame = bpf = 0;
provider = 0;
thread = 0;
} }
OSSPlayer::~OSSPlayer() OSSPlayer::~OSSPlayer()
{ {
CloseStream(); Stop();
::close(dspdev);
} }
void OSSPlayer::OpenStream() void OSSPlayer::OpenStream()
{ {
CloseStream();
bpf = provider->GetChannels() * provider->GetBytesPerSample(); bpf = provider->GetChannels() * provider->GetBytesPerSample();
// Open device // Open device
@ -114,20 +118,6 @@ void OSSPlayer::OpenStream()
if (ioctl(dspdev, SNDCTL_DSP_SPEED, &rate) < 0) { if (ioctl(dspdev, SNDCTL_DSP_SPEED, &rate) < 0) {
throw OSSError("OSS player: setting samplerate failed", 0); throw OSSError("OSS player: setting samplerate failed", 0);
} }
// Now ready
open = true;
}
void OSSPlayer::CloseStream()
{
if (!open) return;
Stop();
::close(dspdev);
// No longer working
open = false;
} }
void OSSPlayer::Play(int64_t start, int64_t count) void OSSPlayer::Play(int64_t start, int64_t count)
@ -146,7 +136,6 @@ void OSSPlayer::Play(int64_t start, int64_t count)
void OSSPlayer::Stop() void OSSPlayer::Stop()
{ {
if (!open) return;
if (!playing) return; if (!playing) return;
// Stop the thread // Stop the thread

View File

@ -79,9 +79,6 @@ public:
class OSSPlayer : public AudioPlayer { class OSSPlayer : public AudioPlayer {
friend class OSSPlayerThread; friend class OSSPlayerThread;
/// Is the output file handle initialized and ready to be written to?
bool open;
/// sample rate of audio /// sample rate of audio
unsigned int rate; unsigned int rate;
@ -106,15 +103,14 @@ class OSSPlayer : public AudioPlayer {
/// bytes per frame /// bytes per frame
unsigned long bpf; unsigned long bpf;
// OSS audio device handle /// OSS audio device handle
volatile int dspdev; volatile int dspdev;
public:
OSSPlayer();
~OSSPlayer();
void OpenStream(); void OpenStream();
void CloseStream();
public:
OSSPlayer(AudioProvider *provider);
~OSSPlayer();
void Play(int64_t start, int64_t count); void Play(int64_t start, int64_t count);
void Stop(); void Stop();

View File

@ -71,9 +71,11 @@ static const PaHostApiTypeId pa_host_api_priority[] = {
}; };
static const size_t pa_host_api_priority_count = sizeof(pa_host_api_priority) / sizeof(pa_host_api_priority[0]); static const size_t pa_host_api_priority_count = sizeof(pa_host_api_priority) / sizeof(pa_host_api_priority[0]);
PortAudioPlayer::PortAudioPlayer() PortAudioPlayer::PortAudioPlayer(AudioProvider *provider)
: volume(1.0f) : AudioPlayer(provider)
, volume(1.0f)
, pa_start(0.0) , pa_start(0.0)
, stream(0)
{ {
PaError err = Pa_Initialize(); PaError err = Pa_Initialize();
@ -92,6 +94,9 @@ PortAudioPlayer::PortAudioPlayer()
if (devices.empty()) if (devices.empty())
throw PortAudioError("No PortAudio output devices found", 0); throw PortAudioError("No PortAudio output devices found", 0);
if (provider)
OpenStream();
} }
void PortAudioPlayer::GatherDevices(PaHostApiIndex host_idx) { void PortAudioPlayer::GatherDevices(PaHostApiIndex host_idx) {
@ -120,6 +125,10 @@ void PortAudioPlayer::GatherDevices(PaHostApiIndex host_idx) {
} }
PortAudioPlayer::~PortAudioPlayer() { PortAudioPlayer::~PortAudioPlayer() {
if (stream) {
Stop();
Pa_CloseStream(stream);
}
Pa_Terminate(); Pa_Terminate();
} }
@ -172,11 +181,6 @@ void PortAudioPlayer::OpenStream() {
throw PortAudioError("Failed initializing PortAudio stream: " + error, 0); throw PortAudioError("Failed initializing PortAudio stream: " + error, 0);
} }
void PortAudioPlayer::CloseStream() {
Stop();
Pa_CloseStream(stream);
}
void PortAudioPlayer::paStreamFinishedCallback(void *) { void PortAudioPlayer::paStreamFinishedCallback(void *) {
LOG_D("audio/player/portaudio") << "stopping stream"; LOG_D("audio/player/portaudio") << "stopping stream";
} }
@ -271,7 +275,7 @@ wxArrayString PortAudioPlayer::GetOutputDevices() {
list.push_back("Default"); list.push_back("Default");
try { try {
PortAudioPlayer player; PortAudioPlayer player(0);
for (std::map<std::string, DeviceVec>::iterator it = player.devices.begin(); it != player.devices.end(); ++it) for (std::map<std::string, DeviceVec>::iterator it = player.devices.begin(); it != player.devices.end(); ++it)
list.push_back(lagi_wxString(it->first)); list.push_back(lagi_wxString(it->first));

View File

@ -48,6 +48,8 @@ extern "C" {
#include <vector> #include <vector>
#endif #endif
class wxArrayString;
/// @class PortAudioPlayer /// @class PortAudioPlayer
/// @brief PortAudio Player /// @brief PortAudio Player
/// ///
@ -93,18 +95,15 @@ class PortAudioPlayer : public AudioPlayer {
/// @param host_idx Host API ID /// @param host_idx Host API ID
void GatherDevices(PaHostApiIndex host_idx); void GatherDevices(PaHostApiIndex host_idx);
void OpenStream();
public: public:
/// @brief Constructor /// @brief Constructor
PortAudioPlayer(); PortAudioPlayer(AudioProvider *provider);
/// @brief Destructor /// @brief Destructor
~PortAudioPlayer(); ~PortAudioPlayer();
/// @brief Open stream
void OpenStream();
/// @brief Close stream
void CloseStream();
/// @brief Play audio. /// @brief Play audio.
/// @param start Start position. /// @param start Start position.
/// @param count Frame count /// @param count Frame count

View File

@ -39,7 +39,9 @@
#ifdef WITH_LIBPULSE #ifdef WITH_LIBPULSE
#ifndef AGI_PRE #ifndef AGI_PRE
#include <stdio.h> #include <cstdio>
#include <wx/thread.h>
#endif #endif
#include "audio_player_pulse.h" #include "audio_player_pulse.h"
@ -48,38 +50,30 @@
#include "include/aegisub/audio_provider.h" #include "include/aegisub/audio_provider.h"
#include "utils.h" #include "utils.h"
PulseAudioPlayer::PulseAudioPlayer() #include <libaegisub/log.h>
: context_notify(0, 1)
PulseAudioPlayer::PulseAudioPlayer(AudioProvider *provider)
: AudioPlayer(provider)
, volume(1.0f)
, is_playing(false)
, start_frame(0)
, cur_frame(0)
, end_frame(0)
, bpf(0)
, context_notify(0, 1)
, context_success(0, 1) , context_success(0, 1)
, stream_notify(0, 1) , stream_notify(0, 1)
, stream_success(0, 1) , stream_success(0, 1)
, paerror(0)
{ {
volume = 1.0f;
paerror = 0;
open = false;
is_playing = false;
}
PulseAudioPlayer::~PulseAudioPlayer()
{
CloseStream();
}
void PulseAudioPlayer::OpenStream()
{
if (open) CloseStream();
// Initialise a mainloop // Initialise a mainloop
//printf("Initialising threaded main loop\n");
mainloop = pa_threaded_mainloop_new(); mainloop = pa_threaded_mainloop_new();
if (!mainloop) { if (!mainloop)
throw agi::AudioPlayerOpenError("Failed to initialise PulseAudio threaded mainloop object", 0); throw agi::AudioPlayerOpenError("Failed to initialise PulseAudio threaded mainloop object", 0);
}
//printf("Starting main loop\n");
pa_threaded_mainloop_start(mainloop); pa_threaded_mainloop_start(mainloop);
// Create context // Create context
//printf("Creating context\n");
context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "Aegisub"); context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "Aegisub");
if (!context) { if (!context) {
pa_threaded_mainloop_free(mainloop); pa_threaded_mainloop_free(mainloop);
@ -88,8 +82,8 @@ void PulseAudioPlayer::OpenStream()
pa_context_set_state_callback(context, (pa_context_notify_cb_t)pa_context_notify, this); pa_context_set_state_callback(context, (pa_context_notify_cb_t)pa_context_notify, this);
// Connect the context // Connect the context
//printf("Connecting context\n");
pa_context_connect(context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL); pa_context_connect(context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL);
// Wait for connection // Wait for connection
while (true) { while (true) {
context_notify.Wait(); context_notify.Wait();
@ -105,7 +99,6 @@ void PulseAudioPlayer::OpenStream()
} }
// otherwise loop once more // otherwise loop once more
} }
//printf("Context connected\n");
// Set up stream // Set up stream
bpf = provider->GetChannels() * provider->GetBytesPerSample(); bpf = provider->GetChannels() * provider->GetBytesPerSample();
@ -115,7 +108,7 @@ void PulseAudioPlayer::OpenStream()
ss.channels = provider->GetChannels(); ss.channels = provider->GetChannels();
pa_channel_map map; pa_channel_map map;
pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_DEFAULT); pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_DEFAULT);
//printf("Creating stream\n");
stream = pa_stream_new(context, "Sound", &ss, &map); stream = pa_stream_new(context, "Sound", &ss, &map);
if (!stream) { if (!stream) {
// argh! // argh!
@ -129,10 +122,9 @@ void PulseAudioPlayer::OpenStream()
pa_stream_set_write_callback(stream, (pa_stream_request_cb_t)pa_stream_write, this); pa_stream_set_write_callback(stream, (pa_stream_request_cb_t)pa_stream_write, this);
// Connect stream // Connect stream
//printf("Connecting playback stream\n");
paerror = pa_stream_connect_playback(stream, NULL, NULL, (pa_stream_flags_t)(PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_NOT_MONOTONOUS|PA_STREAM_AUTO_TIMING_UPDATE), NULL, NULL); paerror = pa_stream_connect_playback(stream, NULL, NULL, (pa_stream_flags_t)(PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_NOT_MONOTONOUS|PA_STREAM_AUTO_TIMING_UPDATE), NULL, NULL);
if (paerror) { if (paerror) {
printf("PulseAudio reported error: %s (%d)\n", pa_strerror(paerror), paerror); LOG_E("audio/player/pulse") << "Stream connection failed: " << pa_strerror(paerror) << "(" << paerror << ")";
throw agi::AudioPlayerOpenError(std::string("PulseAudio reported error: ") + pa_strerror(paerror), 0); throw agi::AudioPlayerOpenError(std::string("PulseAudio reported error: ") + pa_strerror(paerror), 0);
} }
while (true) { while (true) {
@ -141,22 +133,14 @@ void PulseAudioPlayer::OpenStream()
break; break;
} else if (sstate == PA_STREAM_FAILED) { } else if (sstate == PA_STREAM_FAILED) {
paerror = pa_context_errno(context); paerror = pa_context_errno(context);
printf("PulseAudio player: Stream connection failed: %s (%d)\n", pa_strerror(paerror), paerror); LOG_E("audio/player/pulse") << "Stream connection failed: " << pa_strerror(paerror) << "(" << paerror << ")";
throw agi::AudioPlayerOpenError("PulseAudio player: Something went wrong connecting the stream", 0); throw agi::AudioPlayerOpenError("PulseAudio player: Something went wrong connecting the stream", 0);
} }
} }
//printf("Connected playback stream, now playing\n\n");
// Hopefully this marks success
//printf("Finished opening PulseAudio\n\n");
open = true;
} }
void PulseAudioPlayer::CloseStream() PulseAudioPlayer::~PulseAudioPlayer()
{ {
if (!open) return;
//printf("Closing PuseAudio\n");
if (is_playing) Stop(); if (is_playing) Stop();
// Hope for the best and just do things as quickly as possible // Hope for the best and just do things as quickly as possible
@ -166,16 +150,10 @@ void PulseAudioPlayer::CloseStream()
pa_context_unref(context); pa_context_unref(context);
pa_threaded_mainloop_stop(mainloop); pa_threaded_mainloop_stop(mainloop);
pa_threaded_mainloop_free(mainloop); pa_threaded_mainloop_free(mainloop);
//printf("Closed PulseAudio\n");
open = false;
} }
void PulseAudioPlayer::Play(int64_t start,int64_t count) void PulseAudioPlayer::Play(int64_t start,int64_t count)
{ {
//printf("Starting PulseAudio playback\n");
if (!open) OpenStream();
if (is_playing) { if (is_playing) {
// If we're already playing, do a quick "reset" // If we're already playing, do a quick "reset"
is_playing = false; is_playing = false;
@ -187,14 +165,13 @@ void PulseAudioPlayer::Play(int64_t start,int64_t count)
pa_operation_unref(op); pa_operation_unref(op);
if (!stream_success_val) { if (!stream_success_val) {
paerror = pa_context_errno(context); paerror = pa_context_errno(context);
printf("PulseAudio player: Error flushing stream: %s (%d)\n", pa_strerror(paerror), paerror); LOG_E("audio/player/pulse") << "Error flushing stream: " << pa_strerror(paerror) << "(" << paerror << ")";
} }
} }
start_frame = start; start_frame = start;
cur_frame = start; cur_frame = start;
end_frame = start + count; end_frame = start + count;
//printf("start=%lu end=%lu\n", start_frame, end_frame);
is_playing = true; is_playing = true;
@ -202,9 +179,8 @@ void PulseAudioPlayer::Play(int64_t start,int64_t count)
pa_threaded_mainloop_lock(mainloop); pa_threaded_mainloop_lock(mainloop);
paerror = pa_stream_get_time(stream, (pa_usec_t*) &play_start_time); paerror = pa_stream_get_time(stream, (pa_usec_t*) &play_start_time);
pa_threaded_mainloop_unlock(mainloop); pa_threaded_mainloop_unlock(mainloop);
if (paerror) { if (paerror)
printf("PulseAudio player: Error getting stream time: %s (%d)\n", pa_strerror(paerror), paerror); LOG_E("audio/player/pulse") << "Error getting stream time: " << pa_strerror(paerror) << "(" << paerror << ")";
}
PulseAudioPlayer::pa_stream_write(stream, pa_stream_writable_size(stream), this); PulseAudioPlayer::pa_stream_write(stream, pa_stream_writable_size(stream), this);
@ -215,14 +191,13 @@ void PulseAudioPlayer::Play(int64_t start,int64_t count)
pa_operation_unref(op); pa_operation_unref(op);
if (!stream_success_val) { if (!stream_success_val) {
paerror = pa_context_errno(context); paerror = pa_context_errno(context);
printf("PulseAudio player: Error triggering stream: %s (%d)\n", pa_strerror(paerror), paerror); LOG_E("audio/player/pulse") << "Error triggering stream: " << pa_strerror(paerror) << "(" << paerror << ")";
} }
} }
void PulseAudioPlayer::Stop() void PulseAudioPlayer::Stop()
{ {
if (!is_playing) return; if (!is_playing) return;
//printf("Stopping PulseAudio\n");
is_playing = false; is_playing = false;
@ -231,7 +206,6 @@ void PulseAudioPlayer::Stop()
end_frame = 0; end_frame = 0;
// Flush the stream of data // Flush the stream of data
//printf("Flushing stream\n");
pa_threaded_mainloop_lock(mainloop); pa_threaded_mainloop_lock(mainloop);
pa_operation *op = pa_stream_flush(stream, (pa_stream_success_cb_t)pa_stream_success, this); pa_operation *op = pa_stream_flush(stream, (pa_stream_success_cb_t)pa_stream_success, this);
pa_threaded_mainloop_unlock(mainloop); pa_threaded_mainloop_unlock(mainloop);
@ -239,16 +213,8 @@ void PulseAudioPlayer::Stop()
pa_operation_unref(op); pa_operation_unref(op);
if (!stream_success_val) { if (!stream_success_val) {
paerror = pa_context_errno(context); paerror = pa_context_errno(context);
printf("PulseAudio player: Error flushing stream: %s (%d)\n", pa_strerror(paerror), paerror); LOG_E("audio/player/pulse") << "Error flushing stream: " << pa_strerror(paerror) << "(" << paerror << ")";
} }
// And unref it
//printf("Stopped stream\n\n");
}
bool PulseAudioPlayer::IsPlaying()
{
return is_playing;
} }
void PulseAudioPlayer::SetEndPosition(int64_t pos) void PulseAudioPlayer::SetEndPosition(int64_t pos)
@ -261,16 +227,6 @@ void PulseAudioPlayer::SetCurrentPosition(int64_t pos)
cur_frame = pos; cur_frame = pos;
} }
int64_t PulseAudioPlayer::GetStartPosition()
{
return start_frame;
}
int64_t PulseAudioPlayer::GetEndPosition()
{
return end_frame;
}
int64_t PulseAudioPlayer::GetCurrentPosition() int64_t PulseAudioPlayer::GetCurrentPosition()
{ {
if (!is_playing) return 0; if (!is_playing) return 0;
@ -317,7 +273,6 @@ void PulseAudioPlayer::pa_stream_write(pa_stream *p, size_t length, PulseAudioPl
thread->is_playing = false; thread->is_playing = false;
pa_operation *op = pa_stream_drain(p, NULL, NULL); pa_operation *op = pa_stream_drain(p, NULL, NULL);
pa_operation_unref(op); pa_operation_unref(op);
//printf("PA requested more buffer, but no more to stream\n");
return; return;
} else if (thread->cur_frame >= thread->end_frame) { } else if (thread->cur_frame >= thread->end_frame) {
@ -328,12 +283,10 @@ void PulseAudioPlayer::pa_stream_write(pa_stream *p, size_t length, PulseAudioPl
return; return;
} }
//printf("PA requested more buffer, %lu bytes\n", (unsigned long)length);
unsigned long bpf = thread->bpf; unsigned long bpf = thread->bpf;
unsigned long frames = length / thread->bpf; unsigned long frames = length / thread->bpf;
unsigned long maxframes = thread->end_frame - thread->cur_frame; unsigned long maxframes = thread->end_frame - thread->cur_frame;
if (frames > maxframes) frames = maxframes; if (frames > maxframes) frames = maxframes;
//printf("Handing it %lu frames\n", frames);
void *buf = malloc(frames * bpf); void *buf = malloc(frames * bpf);
thread->provider->GetAudioWithVolume(buf, thread->cur_frame, frames, thread->volume); thread->provider->GetAudioWithVolume(buf, thread->cur_frame, frames, thread->volume);
::pa_stream_write(p, buf, frames*bpf, free, 0, PA_SEEK_RELATIVE); ::pa_stream_write(p, buf, frames*bpf, free, 0, PA_SEEK_RELATIVE);

View File

@ -47,14 +47,9 @@ class PulseAudioPlayer;
/// ///
/// DOCME /// DOCME
class PulseAudioPlayer : public AudioPlayer { class PulseAudioPlayer : public AudioPlayer {
private:
/// DOCME /// DOCME
float volume; float volume;
/// DOCME
bool open;
/// DOCME /// DOCME
bool is_playing; bool is_playing;
@ -124,18 +119,15 @@ private:
static void pa_stream_notify(pa_stream *p, PulseAudioPlayer *thread); static void pa_stream_notify(pa_stream *p, PulseAudioPlayer *thread);
public: public:
PulseAudioPlayer(); PulseAudioPlayer(AudioProvider *provider);
~PulseAudioPlayer(); ~PulseAudioPlayer();
void OpenStream();
void CloseStream();
void Play(int64_t start,int64_t count); void Play(int64_t start,int64_t count);
void Stop(); void Stop();
bool IsPlaying(); bool IsPlaying() { return is_playing; }
int64_t GetStartPosition(); int64_t GetStartPosition() { return start_frame; }
int64_t GetEndPosition(); int64_t GetEndPosition() { return end_frame; }
int64_t GetCurrentPosition(); int64_t GetCurrentPosition();
void SetEndPosition(int64_t pos); void SetEndPosition(int64_t pos);
void SetCurrentPosition(int64_t pos); void SetCurrentPosition(int64_t pos);

View File

@ -50,12 +50,9 @@ protected:
AudioProvider *provider; AudioProvider *provider;
public: public:
AudioPlayer(); AudioPlayer(AudioProvider *provider);
virtual ~AudioPlayer() { } virtual ~AudioPlayer() { }
virtual void OpenStream()=0;
virtual void CloseStream()=0;
virtual void Play(int64_t start,int64_t count)=0; // Play sample range virtual void Play(int64_t start,int64_t count)=0; // Play sample range
virtual void Stop()=0; // Stop playing virtual void Stop()=0; // Stop playing
virtual bool IsPlaying()=0; virtual bool IsPlaying()=0;
@ -68,12 +65,10 @@ public:
virtual int64_t GetCurrentPosition()=0; virtual int64_t GetCurrentPosition()=0;
virtual void SetEndPosition(int64_t pos)=0; virtual void SetEndPosition(int64_t pos)=0;
virtual void SetCurrentPosition(int64_t pos)=0; virtual void SetCurrentPosition(int64_t pos)=0;
virtual void SetProvider(AudioProvider *new_provider) { provider = new_provider; }
}; };
class AudioPlayerFactory : public Factory0<AudioPlayer> { class AudioPlayerFactory : public Factory1<AudioPlayer, AudioProvider*> {
public: public:
static void RegisterProviders(); static void RegisterProviders();
static AudioPlayer *GetAudioPlayer(); static AudioPlayer *GetAudioPlayer(AudioProvider *provider);
}; };