diff --git a/aegisub/src/audio_player_portaudio2.cpp b/aegisub/src/audio_player_portaudio2.cpp new file mode 100644 index 000000000..81d3f53ee --- /dev/null +++ b/aegisub/src/audio_player_portaudio2.cpp @@ -0,0 +1,220 @@ +// 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_PORTAUDIO + + +/////////// +// Headers +#include "audio_player_portaudio.h" +#include "audio_provider_manager.h" +#include "utils.h" + +#ifdef HAVE_PA_GETSTREAMTIME +#define Pa_StreamTime Pa_GetStreamTime /* PortAudio v19 */ +#define PaTimestamp PaTime +#endif + + +/////////// +// Library +#if __VISUALC__ >= 1200 +#pragma comment(lib,"portaudio.lib") +#endif + + +///////////////////// +// 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(); +} + +////////////////////// +// PortAudio callback +#ifndef HAVE_PA_GETSTREAMTIME +int PortAudioPlayer::paCallback(void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *userData) { +#else +int PortAudioPlayer::paCallback(const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timei, + PaStreamCallbackFlags flags, void *userData) { +#endif + // Get provider + PortAudioPlayer *player = (PortAudioPlayer *) userData; + AudioProvider *provider = player->GetProvider(); + int end = 0; + + // Calculate how much left + int64_t lenAvailable = player->endPos - player->playPos; + uint64_t avail = 0; + if (lenAvailable > 0) { + avail = lenAvailable; + if (avail > framesPerBuffer) { + lenAvailable = framesPerBuffer; + avail = lenAvailable; + } + } + else { + lenAvailable = 0; + avail = 0; + } + + // Play something + if (lenAvailable > 0) { + provider->GetAudio(outputBuffer,player->playPos,lenAvailable); + } + + // Set volume + short *output = (short*) outputBuffer; + for (unsigned int i=0;iGetVolume()),(1<<15)-1); + + // Fill rest with blank + for (unsigned int i=avail;iplayPos += 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 +void PortAudioPlayer::Play(int64_t start,int64_t count) { + // Stop if it's already playing + wxMutexLocker locker(PAMutex); + + // Set values + endPos = start + count; + playPos = start; + realPlayPos = start; + startPos = start; + + // Start playing + if (!playing) { + PaError err = Pa_StartStream(stream); + if (err != paNoError) { + return; + } + } + playing = true; + paStart = Pa_StreamTime(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(); + } +} + + +/////////////// +// Open stream +void PortAudioPlayer::OpenStream() { + // Open stream + PaError err = Pa_OpenDefaultStream(&stream,0,provider->GetChannels(),paInt16,provider->GetSampleRate(),256, +#ifndef HAVE_PA_GETSTREAMTIME + 16, /* Pa v19 doesn't have a numberOfBuffers parameter */ +#endif + paCallback,this); + + if (err != paNoError) { + 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 (...) {} +} + + +#endif // WITH_PORTAUDIO diff --git a/aegisub/src/audio_player_portaudio2.h b/aegisub/src/audio_player_portaudio2.h new file mode 100644 index 000000000..85c3a571b --- /dev/null +++ b/aegisub/src/audio_player_portaudio2.h @@ -0,0 +1,108 @@ +// 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_PORTAUDIO + + +/////////// +// Headers +#include "include/aegisub/audio_player.h" +#include "include/aegisub/audio_provider.h" +#include "utils.h" +extern "C" { +#include +} + + +//////////////////// +// 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; + PaTimestamp paStart; + volatile int64_t realPlayPos; + +#ifndef HAVE_PA_GETSTREAMTIME + static int paCallback(void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *userData); +#else + static int paCallback(const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo *timei, + PaStreamCallbackFlags flags, void *userData); +#endif + +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() { return realPlayPos; } + void SetEndPosition(int64_t pos) { endPos = pos; } + void SetCurrentPosition(int64_t pos) { playPos = pos; realPlayPos = pos; } + + void SetVolume(double vol) { volume = vol; } + double GetVolume() { return volume; } + + wxMutex *GetMutex() { return &PAMutex; } +}; + + +/////////// +// Factory +class PortAudioPlayerFactory : public AudioPlayerFactory { +public: + AudioPlayer *CreatePlayer() { return new PortAudioPlayer(); } +}; + +#endif