Supplant the portaudio player with the portaudio2 player. This still doesn't handle some of the issues that happen on linux, that will be fixed in a later commit, updates #876.

Originally committed to SVN as r3043.
This commit is contained in:
Amar Takhar 2009-06-10 04:45:57 +00:00
parent 17f1ccac06
commit 1b9f38747b
8 changed files with 168 additions and 571 deletions

View file

@ -21,8 +21,7 @@ m4_define([libswscale_required_version], [0.7.1]) # (r18642)
m4_define([lua_auto3_required_version], [5.0]) m4_define([lua_auto3_required_version], [5.0])
m4_define([lua_auto4_required_version], [5.1]) m4_define([lua_auto4_required_version], [5.1])
m4_define([portaudio_required_version], [1]) m4_define([portaudio_required_version], [19])
m4_define([portaudio2_required_version], [19])
m4_define([pulseaudio_required_version], [0.5]) m4_define([pulseaudio_required_version], [0.5])
m4_define([fontconfig_required_version], [2.4]) m4_define([fontconfig_required_version], [2.4])
@ -450,74 +449,18 @@ AM_CONDITIONAL([HAVE_ALSA], [test "$with_alsa" != "no"])
AC_SUBST(ALSA_LDFLAGS) AC_SUBST(ALSA_LDFLAGS)
###################
## PortAudio2 (v19)
###################
AC_ARG_WITH(portaudio2,[ --without-portaudio2 build without PortAudio v19 audio provider.
(default: auto)], pulseaudio2_disabled="(disabled)")
if test "$with_portaudio2" != "no"; then
PKG_CHECK_MODULES(PORTAUDIO2, portaudio-2.0 >= portaudio2_required_version, [with_portaudio2="yes"], [with_portaudio2="no"])
fi
if test "$with_portaudio2" != "no"; then
AC_AGI_COMPILE([PortAudio2], [portaudio2], [$PORTAUDIO2_CFLAGS], [$PORTAUDIO2_LIBS],[
#include <portaudio.h>
int main(void) {
PaError err = Pa_Initialize();
if (err != paNoError) return 1;
return 0;
} ])
fi
if test "$agi_cv_with_portaudio2" = "no" && test "$with_portaudio2" = "yes"; then
AC_MSG_WARN([PortAudio2 detected, but it doesn't work...])
with_portaudio2="no"
fi
if test "$agi_cv_with_portaudio2" = "yes" && test "$with_portaudio2" = "yes"; then
AC_DEFINE(WITH_PORTAUDIO2, 1, [Enable PortAudio v19 support])
found_audio_player="yes"
fi
AM_CONDITIONAL([HAVE_PORTAUDIO2], [test "$with_portaudio2" != "no"])
############ ############
## PortAudio ## PortAudio
############ ############
AC_ARG_WITH(portaudio,[ --without-portaudio build without PortAudio audio provider.
(default: auto)], portaudio_disabled="(disabled)")
if test "$with_portaudio2" = "yes"; then
with_portaudio="no"
portaudio_disabled="(disabled by v19)"
fi
if test -z "$PORTAUDIO_LDFLAGS"; then
PORTAUDIO_LDFLAGS="$LDFLAGS -lportaudio"
fi
if test -z "$PORTAUDIO_CFLAGS"; then
PORTAUDIO_CFLAGS="$PTHREAD_CFLAGS $PTHREAD_LIBS"
fi
AC_ARG_WITH(portaudio,[ --without-portaudio build without PortAudio v19 audio provider.
(default: auto)], pulseaudio_disabled="(disabled)")
if test "$with_portaudio" != "no"; then if test "$with_portaudio" != "no"; then
aegisub_save_CPPFLAGS="$CPPFLAGS" PKG_CHECK_MODULES(PORTAUDIO, portaudio-2.0 >= portaudio_required_version, [with_portaudio="yes"], [with_portaudio="no"])
aegisub_save_LDFLAGS="$LDFLAGS"
CPPFLAGS="$PORTAUDIO_CFLAGS"
LDFLAGS="$LDFLAGS $PORTAUDIO_LDFLAGS"
AC_CHECK_LIB([portaudio], [Pa_Initialize], [with_portaudio="yes"], [with_portaudio="no"])
AC_CHECK_LIB([portaudio], [Pa_GetStreamTime], [
AC_DEFINE(HAVE_PA_GETSTREAMTIME, 1, [Define to 1 if Pa_GetStreamTime is available in PortAudio])
], [])
CPPFLAGS="$aegisub_save_CPPFLAGS"
LDFLAGS="$aegisub_save_LDFLAGS"
fi fi
if test "$with_portaudio" != "no"; then if test "$with_portaudio" != "no"; then
AC_AGI_COMPILE([PortAudio], [portaudio], [$PORTAUDIO_CFLAGS], [$PORTAUDIO_LDFLAGS],[ AC_AGI_COMPILE([PortAudio], [portaudio], [$PORTAUDIO_CFLAGS], [$PORTAUDIO_LIBS],[
#include <portaudio.h> #include <portaudio.h>
int main(void) { int main(void) {
PaError err = Pa_Initialize(); PaError err = Pa_Initialize();
@ -531,14 +474,12 @@ if test "$agi_cv_with_portaudio" = "no" && test "$with_portaudio" = "yes"; then
with_portaudio="no" with_portaudio="no"
fi fi
if test "$with_portaudio" = "yes" && test "$agi_cv_with_portaudio" = "yes"; then if test "$agi_cv_with_portaudio" = "yes" && test "$with_portaudio" = "yes"; then
AC_DEFINE(WITH_PORTAUDIO, 1, [Enable PortAudio v19 support])
found_audio_player="yes" found_audio_player="yes"
AC_DEFINE(WITH_PORTAUDIO, 1, [Enable PortAudio Audio Provider])
fi fi
AM_CONDITIONAL([HAVE_PORTAUDIO], [test "$with_portaudio" != "no"]) AM_CONDITIONAL([HAVE_PORTAUDIO], [test "$with_portaudio" != "no"])
AC_SUBST(PORTAUDIO_LDFLAGS)
AC_SUBST(PORTAUDIO_CFLAGS)
############# #############
@ -1420,7 +1361,6 @@ Audio Players
ALSA: $with_alsa $alsa_disabled ALSA: $with_alsa $alsa_disabled
OpenAL: $with_openal $openal_disabled OpenAL: $with_openal $openal_disabled
PortAudio: $with_portaudio $portaudio_disabled PortAudio: $with_portaudio $portaudio_disabled
PortAudio2 (v19): $with_portaudio2 $portaudio2_disabled
PulseAudio: $with_pulseaudio $pulseaudio_disabled PulseAudio: $with_pulseaudio $pulseaudio_disabled
A/V Support A/V Support

View file

@ -33,7 +33,7 @@ endif
if FOUND_AUDIO_PLAYER if FOUND_AUDIO_PLAYER
noinst_LIBRARIES += libaudio_player.a noinst_LIBRARIES += libaudio_player.a
libaudio_player_a_SOURCES = audio_player.cpp libaudio_player_a_SOURCES = audio_player.cpp
libaudio_player_a_CPPFLAGS = @ALSA_CFLAGS@ @PORTAUDIO_CFLAGS@ @PORTAUDIO2_CFLAGS@ @LIBPULSE_CFLAGS@ @OPENAL_CFLAGS@ libaudio_player_a_CPPFLAGS = @ALSA_CFLAGS@ @PORTAUDIO_CFLAGS@ @LIBPULSE_CFLAGS@ @OPENAL_CFLAGS@
aegisub_2_1_LDADD += libaudio_player.a aegisub_2_1_LDADD += libaudio_player.a
endif endif
@ -48,18 +48,10 @@ if HAVE_PORTAUDIO
noinst_LIBRARIES += libaudio_portaudio.a noinst_LIBRARIES += libaudio_portaudio.a
libaudio_portaudio_a_SOURCES = audio_player_portaudio.cpp libaudio_portaudio_a_SOURCES = audio_player_portaudio.cpp
libaudio_portaudio_a_CPPFLAGS = @PORTAUDIO_CFLAGS@ libaudio_portaudio_a_CPPFLAGS = @PORTAUDIO_CFLAGS@
aegisub_2_1_LDFLAGS += @PORTAUDIO_LDFLAGS@ aegisub_2_1_LDFLAGS += @PORTAUDIO_LIBS@
aegisub_2_1_LDADD += libaudio_portaudio.a aegisub_2_1_LDADD += libaudio_portaudio.a
endif endif
if HAVE_PORTAUDIO2
noinst_LIBRARIES += libaudio_portaudio2.a
libaudio_portaudio2_a_SOURCES = audio_player_portaudio2.cpp
libaudio_portaudio2_a_CPPFLAGS = @PORTAUDIO2_CFLAGS@
aegisub_2_1_LDFLAGS += @PORTAUDIO2_LIBS@
aegisub_2_1_LDADD += libaudio_portaudio2.a
endif
if HAVE_PULSEAUDIO if HAVE_PULSEAUDIO
noinst_LIBRARIES += libaudio_pulseaudio.a noinst_LIBRARIES += libaudio_pulseaudio.a
libaudio_pulseaudio_a_SOURCES = audio_player_pulse.cpp libaudio_pulseaudio_a_SOURCES = audio_player_pulse.cpp
@ -193,7 +185,6 @@ EXTRA_aegisub_2_1_SOURCES = \
audio_player_dsound.cpp \ audio_player_dsound.cpp \
audio_player_dsound2.cpp \ audio_player_dsound2.cpp \
audio_player_portaudio.cpp \ audio_player_portaudio.cpp \
audio_player_portaudio2.cpp \
audio_player_pulse.cpp \ audio_player_pulse.cpp \
audio_provider_avs.cpp \ audio_provider_avs.cpp \
audio_provider_lavc.cpp \ audio_provider_lavc.cpp \

View file

@ -54,9 +54,6 @@
#ifdef WITH_PORTAUDIO #ifdef WITH_PORTAUDIO
#include "audio_player_portaudio.h" #include "audio_player_portaudio.h"
#endif #endif
#ifdef WITH_PORTAUDIO2
#include "audio_player_portaudio2.h"
#endif
#ifdef WITH_PULSEAUDIO #ifdef WITH_PULSEAUDIO
#include "audio_player_pulse.h" #include "audio_player_pulse.h"
#endif #endif
@ -172,9 +169,6 @@ void AudioPlayerFactoryManager::RegisterProviders() {
#ifdef WITH_PORTAUDIO #ifdef WITH_PORTAUDIO
RegisterFactory(new PortAudioPlayerFactory(),_T("PortAudio")); RegisterFactory(new PortAudioPlayerFactory(),_T("PortAudio"));
#endif #endif
#ifdef WITH_PORTAUDIO2
RegisterFactory(new PortAudioPlayerFactory(),_T("PortAudio2"));
#endif
#ifdef WITH_PULSEAUDIO #ifdef WITH_PULSEAUDIO
RegisterFactory(new PulseAudioPlayerFactory(),_T("PulseAudio")); RegisterFactory(new PulseAudioPlayerFactory(),_T("PulseAudio"));
#endif #endif

View file

@ -43,20 +43,11 @@
// Headers // Headers
#include "audio_player_portaudio.h" #include "audio_player_portaudio.h"
#include "audio_provider_manager.h" #include "audio_provider_manager.h"
#include "options.h"
#include "utils.h" #include "utils.h"
#ifdef HAVE_PA_GETSTREAMTIME // Uncomment to enable debug features.
#define Pa_StreamTime Pa_GetStreamTime /* PortAudio v19 */ //#define PORTAUDIO_DEBUG
#define PaTimestamp PaTime
#endif
///////////
// Library
#if __VISUALC__ >= 1200
#pragma comment(lib,"portaudio.lib")
#endif
///////////////////// /////////////////////
// Reference counter // Reference counter
@ -69,11 +60,13 @@ PortAudioPlayer::PortAudioPlayer() {
// Initialize portaudio // Initialize portaudio
if (!pa_refcount) { if (!pa_refcount) {
PaError err = Pa_Initialize(); PaError err = Pa_Initialize();
if (err != paNoError) { if (err != paNoError) {
static wchar_t errormsg[2048]; static wchar_t errormsg[2048];
swprintf(errormsg, 2048, L"Failed opening PortAudio: %s", Pa_GetErrorText(err)); swprintf(errormsg, 2048, L"Failed opening PortAudio: %s", Pa_GetErrorText(err));
throw (const wchar_t *)errormsg; throw (const wchar_t *)errormsg;
} }
pa_refcount++; pa_refcount++;
} }
@ -92,82 +85,100 @@ PortAudioPlayer::~PortAudioPlayer() {
if (!--pa_refcount) Pa_Terminate(); if (!--pa_refcount) Pa_Terminate();
} }
//////////////////////
// PortAudio callback ///////////////
#ifndef HAVE_PA_GETSTREAMTIME // Open stream
int PortAudioPlayer::paCallback(void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *userData) { void PortAudioPlayer::OpenStream() {
#else // Open stream
int PortAudioPlayer::paCallback(const void *inputBuffer, void *outputBuffer, PaStreamParameters pa_output_p;
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timei,
PaStreamCallbackFlags flags, void *userData) { int pa_config_default = Options.AsInt(_T("Audio PortAudio Device"));
#endif PaDeviceIndex pa_device;
// Get provider
if (pa_config_default < 0) {
pa_device = Pa_GetDefaultOutputDevice();
wxLogDebug(_T("PortAudioPlayer::OpenStream Using Default Output Device: %d"), pa_device);
} else {
pa_device = pa_config_default;
wxLogDebug(_T("PortAudioPlayer::OpenStream Using Config Device: %d"), pa_device);
}
pa_output_p.device = pa_device;
pa_output_p.channelCount = provider->GetChannels();
pa_output_p.sampleFormat = paInt16;
pa_output_p.suggestedLatency = Pa_GetDeviceInfo(pa_device)->defaultLowOutputLatency;
pa_output_p.hostApiSpecificStreamInfo = NULL;
wxLogDebug(_T("PortAudioPlayer::OpenStream Output channels: %d, Latency: %f Sample Rate: %ld\n"),
pa_output_p.channelCount, pa_output_p.suggestedLatency, pa_output_p.sampleFormat);
PaError err = Pa_OpenStream(&stream, NULL, &pa_output_p, provider->GetSampleRate(), 256, paPrimeOutputBuffersUsingStreamCallback, paCallback, this);
if (err != paNoError) {
const PaHostErrorInfo *pa_err = Pa_GetLastHostErrorInfo();
if (pa_err->errorCode != 0) {
wxLogDebug(_T("PortAudioPlayer::OpenStream HostError: API: %d, %s (%ld)\n"), pa_err->hostApiType, pa_err->errorText, pa_err->errorCode);
}
throw wxString(_T("Failed initializing PortAudio stream with error: ") + wxString(Pa_GetErrorText(err),wxConvLocal));
}
}
///////////////
// Close stream
void PortAudioPlayer::CloseStream() {
try {
Stop(false);
Pa_CloseStream(stream);
} catch (...) {}
}
// Called when the callback has finished.
void PortAudioPlayer::paStreamFinishedCallback(void *userData) {
PortAudioPlayer *player = (PortAudioPlayer *) userData; PortAudioPlayer *player = (PortAudioPlayer *) userData;
AudioProvider *provider = player->GetProvider();
int end = 0;
// Calculate how much left player->playing = false;
int64_t lenAvailable = player->endPos - player->playPos;
uint64_t avail = 0; if (player->displayTimer) {
if (lenAvailable > 0) { player->displayTimer->Stop();
avail = lenAvailable;
if (avail > framesPerBuffer) {
lenAvailable = framesPerBuffer;
avail = lenAvailable;
}
}
else {
lenAvailable = 0;
avail = 0;
} }
// Play something wxLogDebug(_T("PortAudioPlayer::paStreamFinishedCallback Stopping stream."));
if (lenAvailable > 0) {
provider->GetAudio(outputBuffer,player->playPos,lenAvailable);
}
// Set volume
short *output = (short*) outputBuffer;
for (unsigned int i=0;i<avail;i++) output[i] = MID(-(1<<15),int(output[i] * player->GetVolume()),(1<<15)-1);
// Fill rest with blank
for (unsigned int i=avail;i<framesPerBuffer;i++) output[i]=0;
// Set play position (and real one)
player->playPos += framesPerBuffer;
#ifndef __APPLE__
player->realPlayPos = (int64_t)(Pa_StreamTime(player->stream) - player->paStart) + player->startPos;
#else
// AudioDeviceGetCurrentTime(), used by Pa_StreamTime() on OS X, is buggered, so use playPos for now
player->realPlayPos = player->playPos;
#endif
// Cap to start if lower
return end;
} }
//////// ////////
// Play // Play
void PortAudioPlayer::Play(int64_t start,int64_t count) { void PortAudioPlayer::Play(int64_t start,int64_t count) {
PaError err;
// Stop if it's already playing // Stop if it's already playing
wxMutexLocker locker(PAMutex); wxMutexLocker locker(PAMutex);
// Set values // Set values
endPos = start + count; endPos = start + count;
playPos = start; playPos = start;
realPlayPos = start;
startPos = start; startPos = start;
// Start playing // Start playing
if (!playing) { if (!playing) {
PaError err = Pa_StartStream(stream);
err = Pa_SetStreamFinishedCallback(stream, paStreamFinishedCallback);
if (err != paNoError) {
wxLogDebug(_T("PortAudioPlayer::Play Could not set FinishedCallback\n"));
return;
}
err = Pa_StartStream(stream);
if (err != paNoError) { if (err != paNoError) {
return; return;
} }
} }
playing = true; playing = true;
paStart = Pa_StreamTime(stream); paStart = Pa_GetStreamTime(stream);
// Update timer // Update timer
if (displayTimer && !displayTimer->IsRunning()) displayTimer->Start(15); if (displayTimer && !displayTimer->IsRunning()) displayTimer->Start(15);
@ -191,30 +202,82 @@ void PortAudioPlayer::Stop(bool timerToo) {
} }
/////////////// //////////////////////
// Open stream /// PortAudio callback
void PortAudioPlayer::OpenStream() { int PortAudioPlayer::paCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) {
// Open stream
PaError err = Pa_OpenDefaultStream(&stream,0,provider->GetChannels(),paInt16,provider->GetSampleRate(),256, // Get provider
#ifndef HAVE_PA_GETSTREAMTIME PortAudioPlayer *player = (PortAudioPlayer *) userData;
16, /* Pa v19 doesn't have a numberOfBuffers parameter */ AudioProvider *provider = player->GetProvider();
#ifdef PORTAUDIO_DEBUG
printf("paCallBack: playPos: %lld startPos: %lld paStart: %f Pa_GetStreamTime: %f AdcTime: %f DacTime: %f framesPerBuffer: %lu CPU: %f\n",
player->playPos, player->startPos, player->paStart, Pa_GetStreamTime(player->stream),
timeInfo->inputBufferAdcTime, timeInfo->outputBufferDacTime, framesPerBuffer, Pa_GetStreamCpuLoad(player->stream));
#endif #endif
paCallback,this);
if (err != paNoError) { // Calculate how much left
throw wxString(_T("Failed initializing PortAudio stream with error: ") + wxString(Pa_GetErrorText(err),wxConvLocal)); int64_t lenAvailable = (player->endPos - player->playPos) > 0 ? framesPerBuffer : 0;
// Play something
if (lenAvailable > 0) {
provider->GetAudioWithVolume(outputBuffer, player->playPos, lenAvailable, player->GetVolume());
// Set play position
player->playPos += framesPerBuffer;
// Continue as normal
return 0;
} }
// Abort stream and stop the callback.
return paAbort;
}
////////////////////////
/// Get current stream position.
int64_t PortAudioPlayer::GetCurrentPosition()
{
if (!playing) return 0;
const PaStreamInfo* streamInfo = Pa_GetStreamInfo(stream);
#ifdef PORTAUDIO_DEBUG
PaTime pa_getstream = Pa_GetStreamTime(stream);
int64_t real = ((pa_getstream - paStart) * streamInfo->sampleRate) + startPos;
printf("GetCurrentPosition: Pa_GetStreamTime: %f startPos: %lld playPos: %lld paStart: %f real: %lld diff: %f\n",
pa_getstream, startPos, playPos, paStart, real, pa_getstream-paStart);
return real;
#else
return ((Pa_GetStreamTime(stream) - paStart) * streamInfo->sampleRate) + startPos;
#endif
} }
/////////////// ///////////////
// Close stream /// Return a list of available output devices.
void PortAudioPlayer::CloseStream() { /// @param Setting from config file.
try { wxArrayString PortAudioPlayer::GetOutputDevices(wxString favorite) {
Stop(false); wxArrayString list;
Pa_CloseStream(stream); int devices = Pa_GetDeviceCount();
} catch (...) {} int i;
}
if (devices < 0) {
// some error here
}
for (i=0; i<devices; i++) {
const PaDeviceInfo *dev_info = Pa_GetDeviceInfo(i);
wxString name(dev_info->name, wxConvUTF8);
list.Insert(name, i);
}
return list;
}
#endif // WITH_PORTAUDIO #endif // WITH_PORTAUDIO

View file

@ -47,6 +47,7 @@ extern "C" {
} }
//////////////////// ////////////////////
// Portaudio player // Portaudio player
class PortAudioPlayer : public AudioPlayer { class PortAudioPlayer : public AudioPlayer {
@ -62,17 +63,19 @@ private:
volatile int64_t startPos; volatile int64_t startPos;
volatile int64_t endPos; volatile int64_t endPos;
void *stream; void *stream;
PaTimestamp paStart; PaTime paStart;
volatile int64_t realPlayPos;
#ifndef HAVE_PA_GETSTREAMTIME static int paCallback(
static int paCallback(void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *userData); const void *inputBuffer,
#else void *outputBuffer,
static int paCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo *timei, const PaStreamCallbackTimeInfo*
PaStreamCallbackFlags flags, void *userData); timeInfo,
#endif PaStreamCallbackFlags
statusFlags,
void *userData);
static void paStreamFinishedCallback(void *userData);
public: public:
PortAudioPlayer(); PortAudioPlayer();
@ -87,13 +90,14 @@ public:
int64_t GetStartPosition() { return startPos; } int64_t GetStartPosition() { return startPos; }
int64_t GetEndPosition() { return endPos; } int64_t GetEndPosition() { return endPos; }
int64_t GetCurrentPosition() { return realPlayPos; } int64_t GetCurrentPosition();
void SetEndPosition(int64_t pos) { endPos = pos; } void SetEndPosition(int64_t pos) { endPos = pos; }
void SetCurrentPosition(int64_t pos) { playPos = pos; realPlayPos = pos; } void SetCurrentPosition(int64_t pos) { playPos = pos; }
void SetVolume(double vol) { volume = vol; } void SetVolume(double vol) { volume = vol; }
double GetVolume() { return volume; } double GetVolume() { return volume; }
wxArrayString GetOutputDevices(wxString favorite);
wxMutex *GetMutex() { return &PAMutex; } wxMutex *GetMutex() { return &PAMutex; }
}; };
@ -105,4 +109,4 @@ public:
AudioPlayer *CreatePlayer() { return new PortAudioPlayer(); } AudioPlayer *CreatePlayer() { return new PortAudioPlayer(); }
}; };
#endif #endif //ifdef WITH_PORTAUDIO

View file

@ -1,283 +0,0 @@
// Copyright (c) 2005-2007, 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
//
// Website: http://aegisub.cellosoft.com
// Contact: mailto:zeratul@cellosoft.com
//
#include "config.h"
#ifdef WITH_PORTAUDIO2
///////////
// Headers
#include "audio_player_portaudio2.h"
#include "audio_provider_manager.h"
#include "options.h"
#include "utils.h"
// Uncomment to enable debug features.
//#define PORTAUDIO2_DEBUG
/////////////////////
// Reference counter
int PortAudioPlayer::pa_refcount = 0;
///////////////
// Constructor
PortAudioPlayer::PortAudioPlayer() {
// Initialize portaudio
if (!pa_refcount) {
PaError err = Pa_Initialize();
if (err != paNoError) {
static wchar_t errormsg[2048];
swprintf(errormsg, 2048, L"Failed opening PortAudio: %s", Pa_GetErrorText(err));
throw (const wchar_t *)errormsg;
}
pa_refcount++;
}
// Variables
playing = false;
stopping = false;
volume = 1.0f;
paStart = 0.0;
}
//////////////
// Destructor
PortAudioPlayer::~PortAudioPlayer() {
// Deinit portaudio
if (!--pa_refcount) Pa_Terminate();
}
///////////////
// Open stream
void PortAudioPlayer::OpenStream() {
// Open stream
PaStreamParameters pa_output_p;
int pa_config_default = Options.AsInt(_T("Audio PortAudio Device"));
PaDeviceIndex pa_device;
if (pa_config_default < 0) {
pa_device = Pa_GetDefaultOutputDevice();
wxLogDebug(_T("PortAudioPlayer::OpenStream Using Default Output Device: %d"), pa_device);
} else {
pa_device = pa_config_default;
wxLogDebug(_T("PortAudioPlayer::OpenStream Using Config Device: %d"), pa_device);
}
pa_output_p.device = pa_device;
pa_output_p.channelCount = provider->GetChannels();
pa_output_p.sampleFormat = paInt16;
pa_output_p.suggestedLatency = Pa_GetDeviceInfo(pa_device)->defaultLowOutputLatency;
pa_output_p.hostApiSpecificStreamInfo = NULL;
wxLogDebug(_T("PortAudioPlayer::OpenStream Output channels: %d, Latency: %f Sample Rate: %ld\n"),
pa_output_p.channelCount, pa_output_p.suggestedLatency, pa_output_p.sampleFormat);
PaError err = Pa_OpenStream(&stream, NULL, &pa_output_p, provider->GetSampleRate(), 256, paPrimeOutputBuffersUsingStreamCallback, paCallback, this);
if (err != paNoError) {
const PaHostErrorInfo *pa_err = Pa_GetLastHostErrorInfo();
if (pa_err->errorCode != 0) {
wxLogDebug(_T("PortAudioPlayer::OpenStream HostError: API: %d, %s (%ld)\n"), pa_err->hostApiType, pa_err->errorText, pa_err->errorCode);
}
throw wxString(_T("Failed initializing PortAudio stream with error: ") + wxString(Pa_GetErrorText(err),wxConvLocal));
}
}
///////////////
// Close stream
void PortAudioPlayer::CloseStream() {
try {
Stop(false);
Pa_CloseStream(stream);
} catch (...) {}
}
// Called when the callback has finished.
void PortAudioPlayer::paStreamFinishedCallback(void *userData) {
PortAudioPlayer *player = (PortAudioPlayer *) userData;
player->playing = false;
if (player->displayTimer) {
player->displayTimer->Stop();
}
wxLogDebug(_T("PortAudioPlayer::paStreamFinishedCallback Stopping stream."));
}
////////
// Play
void PortAudioPlayer::Play(int64_t start,int64_t count) {
PaError err;
// Stop if it's already playing
wxMutexLocker locker(PAMutex);
// Set values
endPos = start + count;
playPos = start;
startPos = start;
// Start playing
if (!playing) {
err = Pa_SetStreamFinishedCallback(stream, paStreamFinishedCallback);
if (err != paNoError) {
wxLogDebug(_T("PortAudioPlayer::Play Could not set FinishedCallback\n"));
return;
}
err = Pa_StartStream(stream);
if (err != paNoError) {
return;
}
}
playing = true;
paStart = Pa_GetStreamTime(stream);
// Update timer
if (displayTimer && !displayTimer->IsRunning()) displayTimer->Start(15);
}
////////
// Stop
void PortAudioPlayer::Stop(bool timerToo) {
//wxMutexLocker locker(PAMutex);
//softStop = false;
// Stop stream
playing = false;
Pa_StopStream (stream);
// Stop timer
if (timerToo && displayTimer) {
displayTimer->Stop();
}
}
//////////////////////
/// PortAudio callback
int PortAudioPlayer::paCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) {
// Get provider
PortAudioPlayer *player = (PortAudioPlayer *) userData;
AudioProvider *provider = player->GetProvider();
#ifdef PORTAUDIO2_DEBUG
printf("paCallBack: playPos: %lld startPos: %lld paStart: %f Pa_GetStreamTime: %f AdcTime: %f DacTime: %f framesPerBuffer: %lu CPU: %f\n",
player->playPos, player->startPos, player->paStart, Pa_GetStreamTime(player->stream),
timeInfo->inputBufferAdcTime, timeInfo->outputBufferDacTime, framesPerBuffer, Pa_GetStreamCpuLoad(player->stream));
#endif
// Calculate how much left
int64_t lenAvailable = (player->endPos - player->playPos) > 0 ? framesPerBuffer : 0;
// Play something
if (lenAvailable > 0) {
provider->GetAudioWithVolume(outputBuffer, player->playPos, lenAvailable, player->GetVolume());
// Set play position
player->playPos += framesPerBuffer;
// Continue as normal
return 0;
}
// Abort stream and stop the callback.
return paAbort;
}
////////////////////////
/// Get current stream position.
int64_t PortAudioPlayer::GetCurrentPosition()
{
if (!playing) return 0;
const PaStreamInfo* streamInfo = Pa_GetStreamInfo(stream);
#ifdef PORTAUDIO2_DEBUG
PaTime pa_getstream = Pa_GetStreamTime(stream);
int64_t real = ((pa_getstream - paStart) * streamInfo->sampleRate) + startPos;
printf("GetCurrentPosition: Pa_GetStreamTime: %f startPos: %lld playPos: %lld paStart: %f real: %lld diff: %f\n",
pa_getstream, startPos, playPos, paStart, real, pa_getstream-paStart);
return real;
#else
return ((Pa_GetStreamTime(stream) - paStart) * streamInfo->sampleRate) + startPos;
#endif
}
///////////////
/// Return a list of available output devices.
/// @param Setting from config file.
wxArrayString PortAudioPlayer::GetOutputDevices(wxString favorite) {
wxArrayString list;
int devices = Pa_GetDeviceCount();
int i;
if (devices < 0) {
// some error here
}
for (i=0; i<devices; i++) {
const PaDeviceInfo *dev_info = Pa_GetDeviceInfo(i);
wxString name(dev_info->name, wxConvUTF8);
list.Insert(name, i);
}
return list;
}
#endif // WITH_PORTAUDIO2

View file

@ -1,112 +0,0 @@
// Copyright (c) 2005-2007, 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
//
// Website: http://aegisub.cellosoft.com
// Contact: mailto:zeratul@cellosoft.com
//
#ifdef WITH_PORTAUDIO2
///////////
// Headers
#include "include/aegisub/audio_player.h"
#include "include/aegisub/audio_provider.h"
#include "utils.h"
extern "C" {
#include <portaudio.h>
}
////////////////////
// Portaudio player
class PortAudioPlayer : public AudioPlayer {
private:
static int pa_refcount;
wxMutex PAMutex;
volatile bool stopping;
//bool softStop;
bool playing;
float volume;
volatile int64_t playPos;
volatile int64_t startPos;
volatile int64_t endPos;
void *stream;
PaTime paStart;
static int paCallback(
const void *inputBuffer,
void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo*
timeInfo,
PaStreamCallbackFlags
statusFlags,
void *userData);
static void paStreamFinishedCallback(void *userData);
public:
PortAudioPlayer();
~PortAudioPlayer();
void OpenStream();
void CloseStream();
void Play(int64_t start,int64_t count);
void Stop(bool timerToo=true);
bool IsPlaying() { return playing; }
int64_t GetStartPosition() { return startPos; }
int64_t GetEndPosition() { return endPos; }
int64_t GetCurrentPosition();
void SetEndPosition(int64_t pos) { endPos = pos; }
void SetCurrentPosition(int64_t pos) { playPos = pos; }
void SetVolume(double vol) { volume = vol; }
double GetVolume() { return volume; }
wxArrayString GetOutputDevices(wxString favorite);
wxMutex *GetMutex() { return &PAMutex; }
};
///////////
// Factory
class PortAudioPlayerFactory : public AudioPlayerFactory {
public:
AudioPlayer *CreatePlayer() { return new PortAudioPlayer(); }
};
#endif

View file

@ -131,7 +131,7 @@
#pragma comment(lib, "ffms2.lib") #pragma comment(lib, "ffms2.lib")
#endif #endif
#ifdef WITH_PORTAUDIO2 #ifdef WITH_PORTAUDIO
#pragma comment(lib,"portaudio_x86.lib") #pragma comment(lib,"portaudio_x86.lib")
#endif #endif