From 4698ec6f6b09ad16437be7f5eda6ffc46c7ba32a Mon Sep 17 00:00:00 2001 From: Rodrigo Braz Monteiro Date: Sat, 25 Feb 2006 06:04:46 +0000 Subject: [PATCH] Further abstraction of audio classes Originally committed to SVN as r168. --- core/audio_box.cpp | 6 +- core/audio_display.cpp | 24 +++-- core/audio_player.cpp | 25 +++++ core/audio_player.h | 9 ++ core/audio_player_portaudio.cpp | 186 ++++++++++++++++++++++++++++++++ core/audio_player_portaudio.h | 90 ++++++++++++++++ core/audio_provider.cpp | 137 +---------------------- core/audio_provider.h | 32 +----- core/video_display.cpp | 3 +- 9 files changed, 334 insertions(+), 178 deletions(-) create mode 100644 core/audio_player_portaudio.cpp create mode 100644 core/audio_player_portaudio.h diff --git a/core/audio_box.cpp b/core/audio_box.cpp index 829fa75af..a14643116 100644 --- a/core/audio_box.cpp +++ b/core/audio_box.cpp @@ -283,7 +283,7 @@ void AudioBox::OnVerticalZoom(wxScrollEvent &event) { float value = pow(float(pos)/50.0f,3); audioDisplay->SetScale(value); if (VerticalLink->GetValue()) { - audioDisplay->provider->volume = value; + audioDisplay->provider->SetVolume(value); VolumeBar->SetValue(pos); } } @@ -296,7 +296,7 @@ void AudioBox::OnVolume(wxScrollEvent &event) { int pos = event.GetPosition(); if (pos < 1) pos = 1; if (pos > 100) pos = 100; - audioDisplay->provider->volume = pow(float(pos)/50.0f,3); + audioDisplay->provider->SetVolume(pow(float(pos)/50.0f,3)); } } @@ -309,7 +309,7 @@ void AudioBox::OnVerticalLink(wxCommandEvent &event) { if (pos > 100) pos = 100; float value = pow(float(pos)/50.0f,3); if (VerticalLink->GetValue()) { - audioDisplay->provider->volume = value; + audioDisplay->provider->SetVolume(value); VolumeBar->SetValue(pos); } VolumeBar->Enable(!VerticalLink->GetValue()); diff --git a/core/audio_display.cpp b/core/audio_display.cpp index c678192eb..4c79b1665 100644 --- a/core/audio_display.cpp +++ b/core/audio_display.cpp @@ -1121,7 +1121,7 @@ void AudioDisplay::OnMouseEvent(wxMouseEvent& event) { } // Cursor drawing - if (!provider->playing) { + if (!provider->IsPlaying()) { // Draw bg wxClientDC dc(this); dc.BeginDrawing(); @@ -1206,7 +1206,7 @@ void AudioDisplay::OnMouseEvent(wxMouseEvent& event) { if (event.ButtonDown(wxMOUSE_BTN_RIGHT)) { curEndMS = GetMSAtX(x); mod = true; - provider->endPos = GetSampleAtX(x); + provider->SetEndPosition(GetSampleAtX(x)); } // Modified, commit changes @@ -1381,7 +1381,7 @@ void AudioDisplay::OnMouseEvent(wxMouseEvent& event) { // Update stuff if (updated) { - provider->endPos = GetSampleAtX(selEnd); + provider->SetEndPosition(GetSampleAtX(selEnd)); wxCursor cursor(wxCURSOR_SIZEWE); SetCursor(cursor); UpdateImage(true); @@ -1449,8 +1449,12 @@ void AudioDisplay::OnSize(wxSizeEvent &event) { // Timer event void AudioDisplay::OnUpdateTimer(wxTimerEvent &event) { // Get lock and check if it's OK - wxMutexLocker locker(provider->PAMutex); - if (!locker.IsOk() || !provider->playing) return; + if (provider->GetMutex()) { + wxMutexLocker locker(*provider->GetMutex()); + if (!locker.IsOk()) return; + } + + if (!provider->IsPlaying()) return; // Get DCs //wxMutexGuiEnter(); @@ -1464,13 +1468,13 @@ void AudioDisplay::OnUpdateTimer(wxTimerEvent &event) { // Draw cursor int curpos = -1; - if (provider->playing) { - if (provider->realPlayPos > provider->startPos && provider->realPlayPos < provider->endPos) { + if (provider->IsPlaying()) { + if (provider->GetCurrentPosition() > provider->GetStartPosition() && provider->GetCurrentPosition() < provider->GetEndPosition()) { dc.SetPen(wxPen(Options.AsColour(_T("Audio Play cursor")))); - curpos = GetXAtSample(provider->realPlayPos); + curpos = GetXAtSample(provider->GetCurrentPosition()); dc.DrawLine(curpos,0,curpos,h); } - else if (provider->realPlayPos > provider->endPos + 8192) { + else if (provider->GetCurrentPosition() > provider->GetEndPosition() + 8192) { provider->Stop(); } } @@ -1516,7 +1520,7 @@ void AudioDisplay::OnKeyDown(wxKeyEvent &event) { // Play if (Hotkeys.IsPressed(_T("Audio Play")) || Hotkeys.IsPressed(_T("Audio Play Alt"))) { - if (provider->playing) Stop(); + if (provider->IsPlaying()) Stop(); else { int start=0,end=0; GetTimesSelection(start,end); diff --git a/core/audio_player.cpp b/core/audio_player.cpp index 737b95411..1d86c6e00 100644 --- a/core/audio_player.cpp +++ b/core/audio_player.cpp @@ -45,12 +45,16 @@ // Constructor AudioPlayer::AudioPlayer() { provider = NULL; + displayTimer = NULL; } ////////////// // Destructor AudioPlayer::~AudioPlayer() { + if (displayTimer) { + displayTimer->Stop(); + } } @@ -61,6 +65,27 @@ void AudioPlayer::SetProvider(AudioProvider *_provider) { } +//////////////// +// Get provider +AudioProvider *AudioPlayer::GetProvider() { + return provider; +} + + +///////////// +// Get mutex +wxMutex *AudioPlayer::GetMutex() { + return NULL; +} + + +///////////// +// Set timer +void AudioPlayer::SetDisplayTimer(wxTimer *timer) { + displayTimer = timer; +} + + ///////////////////// // Ask to stop later void AudioPlayer::RequestStop() { diff --git a/core/audio_player.h b/core/audio_player.h index 9e93fb380..e9fe4624b 100644 --- a/core/audio_player.h +++ b/core/audio_player.h @@ -45,6 +45,8 @@ ////////////// // Prototypes class AudioProvider; +class wxTimer; +class wxMutex; /////////////////////////// @@ -55,6 +57,7 @@ private: protected: AudioProvider *provider; + wxTimer *displayTimer; public: AudioPlayer(); @@ -63,17 +66,23 @@ public: virtual void Play(__int64 start,__int64 count)=0; // Play sample range virtual void Stop(bool timerToo=true)=0; // Stop playing virtual void RequestStop(); // Request it to stop playing in a thread-safe way + virtual bool IsPlaying()=0; virtual void SetVolume(double volume)=0; virtual double GetVolume()=0; void SetProvider(AudioProvider *provider); + AudioProvider *GetProvider(); + virtual __int64 GetStartPosition()=0; virtual __int64 GetEndPosition()=0; virtual __int64 GetCurrentPosition()=0; virtual void SetEndPosition(__int64 pos)=0; virtual void SetCurrentPosition(__int64 pos)=0; + void SetDisplayTimer(wxTimer *timer); + virtual wxMutex *GetMutex(); + DECLARE_EVENT_TABLE() }; diff --git a/core/audio_player_portaudio.cpp b/core/audio_player_portaudio.cpp new file mode 100644 index 000000000..deeef2c39 --- /dev/null +++ b/core/audio_player_portaudio.cpp @@ -0,0 +1,186 @@ +// Copyright (c) 2005, 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 +// + + +/////////// +// Headers +#include "audio_player_portaudio.h" +#include "audio_provider.h" +#include "utils.h" + + +///////////////////// +// Reference counter +int PortAudioPlayer::pa_refcount = 0; + + +/////////////// +// Constructor +PortAudioPlayer::PortAudioPlayer() { + // Initialize portaudio + if (!pa_refcount) { + PaError err = Pa_Initialize(); + if (err != paNoError) + throw wxString::Format(_T("Failed opening PortAudio with error: %s"), wxString(Pa_GetErrorText(err),wxConvLocal)); + pa_refcount++; + } + + // Variables + playing = false; + stopping = false; + volume = 1.0f; +} + + +////////////// +// Destructor +PortAudioPlayer::~PortAudioPlayer() { + // Deinit portaudio + if (!--pa_refcount) Pa_Terminate(); +} + + +////////////////////// +// PortAudio callback +int PortAudioPlayer::paCallback(void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *userData) { + // Get provider + PortAudioPlayer *player = (PortAudioPlayer *) userData; + AudioProvider *provider = player->GetProvider(); + int end = 0; + + // Calculate how much left + __int64 lenAvailable = player->endPos - player->playPos; + unsigned __int64 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); + } + + // Pad end with blank + if (avail < (unsigned __int64) framesPerBuffer) { + //provider->softStop = true; + } + + // 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; + player->realPlayPos = player->playPos - (outTime - Pa_StreamTime(player->stream)); + + // Cap to start if lower + return end; +} + + +//////// +// Play +void PortAudioPlayer::Play(__int64 start,__int64 count) { + // Stop if it's already playing + wxMutexLocker locker(PAMutex); + + // Set values + endPos = start + count; + realPlayPos = start; + playPos = start; + startPos = start; + startMS = startPos * 1000 / provider->GetSampleRate(); + + // Start playing + if (!playing) { + PaError err = Pa_StartStream(stream); + if (err != paNoError) { + return; + } + } + playing = true; + + // 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); + realPlayPos = 0; + + // 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,16,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 (...) {} +} diff --git a/core/audio_player_portaudio.h b/core/audio_player_portaudio.h new file mode 100644 index 000000000..3e9ce663e --- /dev/null +++ b/core/audio_player_portaudio.h @@ -0,0 +1,90 @@ +// Copyright (c) 2005, 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 +// + + +#pragma once + + +/////////// +// Headers +#include "audio_player.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 playPos; + volatile __int64 startPos; + volatile __int64 endPos; + volatile __int64 realPlayPos; + volatile __int64 startMS; + void *stream; + + static int PortAudioPlayer::paCallback(void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *userData); + +protected: + void OpenStream(); + void CloseStream(); + +public: + PortAudioPlayer(); + ~PortAudioPlayer(); + + void Play(__int64 start,__int64 count); + void Stop(bool timerToo=true); + bool IsPlaying() { return playing; } + + __int64 GetStartPosition() { return startPos; } + __int64 GetEndPosition() { return endPos; } + __int64 GetCurrentPosition() { return realPlayPos; } + void SetEndPosition(__int64 pos) { endPos = pos; } + void SetCurrentPosition(__int64 pos) { playPos = pos; realPlayPos = pos; } + + void SetVolume(double vol) { volume = vol; } + double GetVolume() { return volume; } + + wxMutex *GetMutex() { return &PAMutex; } +}; diff --git a/core/audio_provider.cpp b/core/audio_provider.cpp index 1a2240452..138373bc5 100644 --- a/core/audio_provider.cpp +++ b/core/audio_provider.cpp @@ -48,42 +48,28 @@ #include "main.h" #include "dialog_progress.h" -extern "C" { -#include -} - -int AudioProvider::pa_refcount = 0; - #define CacheBits ((22)) #define CacheBlockSize ((1 << CacheBits)) ////////////// // Constructor AudioProvider::AudioProvider(wxString _filename, AudioDisplay *_display) { + SetProvider(this); + SetDisplayTimer(&_display->UpdateTimer); type = AUDIO_PROVIDER_NONE; - playing = false; - stopping = false; blockcache = NULL; blockcount = 0; raw = NULL; display = _display; blockcount = 0; - volume = 1.0f; filename = _filename; - // Initialize portaudio - if (!pa_refcount) { - PaError err = Pa_Initialize(); - if (err != paNoError) - throw wxString::Format(_T("Failed opening PortAudio with error: %s"), wxString(Pa_GetErrorText(err),wxConvLocal)); - pa_refcount++; - } - try { OpenAVSAudio(); OpenStream(); - } catch (...) { + } + catch (...) { Unload(); throw; } @@ -120,9 +106,6 @@ void AudioProvider::Unload() { // Clear buffers delete raw; - - if (!--pa_refcount) - Pa_Terminate(); } @@ -469,115 +452,3 @@ wxString AudioProvider::DiskCacheName() { return DiskCachePath() + _T("audio.tmp"); } - -////////////////////// -// PortAudio callback -int paCallback(void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *userData) { - // Get provider - AudioProvider *provider = (AudioProvider *) userData; - int end = 0; - - // Calculate how much left - __int64 lenAvailable = provider->endPos - provider->playPos; - unsigned __int64 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,provider->playPos,lenAvailable); - } - - // Pad end with blank - if (avail < (unsigned __int64) framesPerBuffer) { - provider->softStop = true; - } - - // Set volume - short *output = (short*) outputBuffer; - for (unsigned int i=0;ivolume),(1<<15)-1); - - // Fill rest with blank - for (unsigned int i=avail;iplayPos += framesPerBuffer; - provider->realPlayPos = provider->playPos - (outTime - Pa_StreamTime(provider->stream)); - - // Cap to start if lower - return end; -} - - -//////// -// Play -void AudioProvider::Play(__int64 start,__int64 count) { - // Stop if it's already playing - wxMutexLocker locker(PAMutex); - - // Set values - endPos = start + count; - realPlayPos = start; - playPos = start; - startPos = start; - startMS = startPos * 1000 / GetSampleRate(); - - // Start playing - if (!playing) { - PaError err = Pa_StartStream(stream); - if (err != paNoError) { - return; - } - } - - playing = true; - - if (!display->UpdateTimer.IsRunning()) display->UpdateTimer.Start(15); -} - - -//////// -// Stop -void AudioProvider::Stop(bool timerToo) { - //wxMutexLocker locker(PAMutex); - softStop = false; - - // Stop stream - playing = false; - Pa_StopStream (stream); - realPlayPos = 0; - - // Stop timer - if (timerToo) { - display->UpdateTimer.Stop(); - } -} - - -/////////////// -// Open stream -void AudioProvider::OpenStream() { - // Open stream - PaError err = Pa_OpenDefaultStream(&stream,0,GetChannels(),paInt16,GetSampleRate(),256,16,paCallback,this); - if (err != paNoError) - throw wxString(_T("Failed initializing PortAudio stream with error: ") + wxString(Pa_GetErrorText(err),wxConvLocal)); -} - - -/////////////// -// Close stream -void AudioProvider::CloseStream() { - try { - Stop(false); - Pa_CloseStream(stream); - } catch (...) {} -} diff --git a/core/audio_provider.h b/core/audio_provider.h index 6dfa18568..d282f24a3 100644 --- a/core/audio_provider.h +++ b/core/audio_provider.h @@ -43,7 +43,7 @@ #include #include #include "avisynth_wrap.h" -#include "audio_player.h" +#include "audio_player_portaudio.h" ////////////// @@ -63,10 +63,8 @@ enum AudioProviderType { //////////////////////// // Audio provider class -class AudioProvider : public AviSynthWrapper, public AudioPlayer { +class AudioProvider : public AviSynthWrapper, public PortAudioPlayer { private: - static int pa_refcount; - wxMutex diskmutex; AudioProviderType type; @@ -88,8 +86,6 @@ private: int sample_rate; int bytes_per_sample; - void OpenStream(); - void CloseStream(); void ConvertToRAMCache(PClip &tempclip); void ConvertToDiskCache(PClip &tempclip); void LoadFromClip(AVSValue clip); @@ -100,19 +96,6 @@ private: void Unload(); public: - wxMutex PAMutex; - volatile bool stopping; - bool softStop; - bool playing; - float volume; - - volatile __int64 playPos; - volatile __int64 startPos; - volatile __int64 endPos; - volatile __int64 realPlayPos; - volatile __int64 startMS; - void *stream; - AudioProvider(wxString _filename, AudioDisplay *_display); ~AudioProvider(); @@ -124,15 +107,4 @@ public: int GetChannels(); __int64 GetNumSamples(); int GetSampleRate(); - - void Play(__int64 start,__int64 count); - void Stop(bool timerToo=true); - - __int64 GetEndPosition() { return endPos; } - __int64 GetCurrentPosition() { return realPlayPos; } - void SetEndPosition(__int64 pos) { endPos = pos; } - void SetCurrentPosition(__int64 pos) { playPos = pos; realPlayPos = pos; } - - void SetVolume(double vol) { volume = vol; } - double GetVolume() { return volume; } }; diff --git a/core/video_display.cpp b/core/video_display.cpp index 0e7a7c1f6..08813d04d 100644 --- a/core/video_display.cpp +++ b/core/video_display.cpp @@ -887,8 +887,7 @@ void VideoDisplay::OnPlayTimer(wxTimerEvent &event) { // Sync audio if (nextFrame % 25 == 0) { __int64 audPos = audio->GetSampleAtMS(VFR_Output.GetTimeAtFrame(nextFrame)); - audio->provider->playPos = audPos; - audio->provider->realPlayPos = audPos; + audio->provider->SetCurrentPosition(audPos); } }