Further abstraction of audio classes

Originally committed to SVN as r168.
This commit is contained in:
Rodrigo Braz Monteiro 2006-02-25 06:04:46 +00:00
parent 68431d0c1a
commit 4698ec6f6b
9 changed files with 334 additions and 178 deletions

View file

@ -283,7 +283,7 @@ void AudioBox::OnVerticalZoom(wxScrollEvent &event) {
float value = pow(float(pos)/50.0f,3); float value = pow(float(pos)/50.0f,3);
audioDisplay->SetScale(value); audioDisplay->SetScale(value);
if (VerticalLink->GetValue()) { if (VerticalLink->GetValue()) {
audioDisplay->provider->volume = value; audioDisplay->provider->SetVolume(value);
VolumeBar->SetValue(pos); VolumeBar->SetValue(pos);
} }
} }
@ -296,7 +296,7 @@ void AudioBox::OnVolume(wxScrollEvent &event) {
int pos = event.GetPosition(); int pos = event.GetPosition();
if (pos < 1) pos = 1; if (pos < 1) pos = 1;
if (pos > 100) pos = 100; 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; if (pos > 100) pos = 100;
float value = pow(float(pos)/50.0f,3); float value = pow(float(pos)/50.0f,3);
if (VerticalLink->GetValue()) { if (VerticalLink->GetValue()) {
audioDisplay->provider->volume = value; audioDisplay->provider->SetVolume(value);
VolumeBar->SetValue(pos); VolumeBar->SetValue(pos);
} }
VolumeBar->Enable(!VerticalLink->GetValue()); VolumeBar->Enable(!VerticalLink->GetValue());

View file

@ -1121,7 +1121,7 @@ void AudioDisplay::OnMouseEvent(wxMouseEvent& event) {
} }
// Cursor drawing // Cursor drawing
if (!provider->playing) { if (!provider->IsPlaying()) {
// Draw bg // Draw bg
wxClientDC dc(this); wxClientDC dc(this);
dc.BeginDrawing(); dc.BeginDrawing();
@ -1206,7 +1206,7 @@ void AudioDisplay::OnMouseEvent(wxMouseEvent& event) {
if (event.ButtonDown(wxMOUSE_BTN_RIGHT)) { if (event.ButtonDown(wxMOUSE_BTN_RIGHT)) {
curEndMS = GetMSAtX(x); curEndMS = GetMSAtX(x);
mod = true; mod = true;
provider->endPos = GetSampleAtX(x); provider->SetEndPosition(GetSampleAtX(x));
} }
// Modified, commit changes // Modified, commit changes
@ -1381,7 +1381,7 @@ void AudioDisplay::OnMouseEvent(wxMouseEvent& event) {
// Update stuff // Update stuff
if (updated) { if (updated) {
provider->endPos = GetSampleAtX(selEnd); provider->SetEndPosition(GetSampleAtX(selEnd));
wxCursor cursor(wxCURSOR_SIZEWE); wxCursor cursor(wxCURSOR_SIZEWE);
SetCursor(cursor); SetCursor(cursor);
UpdateImage(true); UpdateImage(true);
@ -1449,8 +1449,12 @@ void AudioDisplay::OnSize(wxSizeEvent &event) {
// Timer event // Timer event
void AudioDisplay::OnUpdateTimer(wxTimerEvent &event) { void AudioDisplay::OnUpdateTimer(wxTimerEvent &event) {
// Get lock and check if it's OK // Get lock and check if it's OK
wxMutexLocker locker(provider->PAMutex); if (provider->GetMutex()) {
if (!locker.IsOk() || !provider->playing) return; wxMutexLocker locker(*provider->GetMutex());
if (!locker.IsOk()) return;
}
if (!provider->IsPlaying()) return;
// Get DCs // Get DCs
//wxMutexGuiEnter(); //wxMutexGuiEnter();
@ -1464,13 +1468,13 @@ void AudioDisplay::OnUpdateTimer(wxTimerEvent &event) {
// Draw cursor // Draw cursor
int curpos = -1; int curpos = -1;
if (provider->playing) { if (provider->IsPlaying()) {
if (provider->realPlayPos > provider->startPos && provider->realPlayPos < provider->endPos) { if (provider->GetCurrentPosition() > provider->GetStartPosition() && provider->GetCurrentPosition() < provider->GetEndPosition()) {
dc.SetPen(wxPen(Options.AsColour(_T("Audio Play cursor")))); dc.SetPen(wxPen(Options.AsColour(_T("Audio Play cursor"))));
curpos = GetXAtSample(provider->realPlayPos); curpos = GetXAtSample(provider->GetCurrentPosition());
dc.DrawLine(curpos,0,curpos,h); dc.DrawLine(curpos,0,curpos,h);
} }
else if (provider->realPlayPos > provider->endPos + 8192) { else if (provider->GetCurrentPosition() > provider->GetEndPosition() + 8192) {
provider->Stop(); provider->Stop();
} }
} }
@ -1516,7 +1520,7 @@ void AudioDisplay::OnKeyDown(wxKeyEvent &event) {
// Play // Play
if (Hotkeys.IsPressed(_T("Audio Play")) || Hotkeys.IsPressed(_T("Audio Play Alt"))) { if (Hotkeys.IsPressed(_T("Audio Play")) || Hotkeys.IsPressed(_T("Audio Play Alt"))) {
if (provider->playing) Stop(); if (provider->IsPlaying()) Stop();
else { else {
int start=0,end=0; int start=0,end=0;
GetTimesSelection(start,end); GetTimesSelection(start,end);

View file

@ -45,12 +45,16 @@
// Constructor // Constructor
AudioPlayer::AudioPlayer() { AudioPlayer::AudioPlayer() {
provider = NULL; provider = NULL;
displayTimer = NULL;
} }
////////////// //////////////
// Destructor // Destructor
AudioPlayer::~AudioPlayer() { 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 // Ask to stop later
void AudioPlayer::RequestStop() { void AudioPlayer::RequestStop() {

View file

@ -45,6 +45,8 @@
////////////// //////////////
// Prototypes // Prototypes
class AudioProvider; class AudioProvider;
class wxTimer;
class wxMutex;
/////////////////////////// ///////////////////////////
@ -55,6 +57,7 @@ private:
protected: protected:
AudioProvider *provider; AudioProvider *provider;
wxTimer *displayTimer;
public: public:
AudioPlayer(); AudioPlayer();
@ -63,17 +66,23 @@ public:
virtual void Play(__int64 start,__int64 count)=0; // Play sample range virtual void Play(__int64 start,__int64 count)=0; // Play sample range
virtual void Stop(bool timerToo=true)=0; // Stop playing virtual void Stop(bool timerToo=true)=0; // Stop playing
virtual void RequestStop(); // Request it to stop playing in a thread-safe way virtual void RequestStop(); // Request it to stop playing in a thread-safe way
virtual bool IsPlaying()=0;
virtual void SetVolume(double volume)=0; virtual void SetVolume(double volume)=0;
virtual double GetVolume()=0; virtual double GetVolume()=0;
void SetProvider(AudioProvider *provider); void SetProvider(AudioProvider *provider);
AudioProvider *GetProvider();
virtual __int64 GetStartPosition()=0;
virtual __int64 GetEndPosition()=0; virtual __int64 GetEndPosition()=0;
virtual __int64 GetCurrentPosition()=0; virtual __int64 GetCurrentPosition()=0;
virtual void SetEndPosition(__int64 pos)=0; virtual void SetEndPosition(__int64 pos)=0;
virtual void SetCurrentPosition(__int64 pos)=0; virtual void SetCurrentPosition(__int64 pos)=0;
void SetDisplayTimer(wxTimer *timer);
virtual wxMutex *GetMutex();
DECLARE_EVENT_TABLE() DECLARE_EVENT_TABLE()
}; };

View file

@ -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;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;
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 (...) {}
}

View file

@ -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.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 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; }
};

View file

@ -48,42 +48,28 @@
#include "main.h" #include "main.h"
#include "dialog_progress.h" #include "dialog_progress.h"
extern "C" {
#include <portaudio.h>
}
int AudioProvider::pa_refcount = 0;
#define CacheBits ((22)) #define CacheBits ((22))
#define CacheBlockSize ((1 << CacheBits)) #define CacheBlockSize ((1 << CacheBits))
////////////// //////////////
// Constructor // Constructor
AudioProvider::AudioProvider(wxString _filename, AudioDisplay *_display) { AudioProvider::AudioProvider(wxString _filename, AudioDisplay *_display) {
SetProvider(this);
SetDisplayTimer(&_display->UpdateTimer);
type = AUDIO_PROVIDER_NONE; type = AUDIO_PROVIDER_NONE;
playing = false;
stopping = false;
blockcache = NULL; blockcache = NULL;
blockcount = 0; blockcount = 0;
raw = NULL; raw = NULL;
display = _display; display = _display;
blockcount = 0; blockcount = 0;
volume = 1.0f;
filename = _filename; 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 { try {
OpenAVSAudio(); OpenAVSAudio();
OpenStream(); OpenStream();
} catch (...) { }
catch (...) {
Unload(); Unload();
throw; throw;
} }
@ -120,9 +106,6 @@ void AudioProvider::Unload() {
// Clear buffers // Clear buffers
delete raw; delete raw;
if (!--pa_refcount)
Pa_Terminate();
} }
@ -469,115 +452,3 @@ wxString AudioProvider::DiskCacheName() {
return DiskCachePath() + _T("audio.tmp"); 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;i<avail;i++) output[i] = MID(-(1<<15),int(output[i] * provider->volume),(1<<15)-1);
// Fill rest with blank
for (unsigned int i=avail;i<framesPerBuffer;i++) output[i]=0;
// Set play position (and real one)
provider->playPos += 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 (...) {}
}

View file

@ -43,7 +43,7 @@
#include <fstream> #include <fstream>
#include <time.h> #include <time.h>
#include "avisynth_wrap.h" #include "avisynth_wrap.h"
#include "audio_player.h" #include "audio_player_portaudio.h"
////////////// //////////////
@ -63,10 +63,8 @@ enum AudioProviderType {
//////////////////////// ////////////////////////
// Audio provider class // Audio provider class
class AudioProvider : public AviSynthWrapper, public AudioPlayer { class AudioProvider : public AviSynthWrapper, public PortAudioPlayer {
private: private:
static int pa_refcount;
wxMutex diskmutex; wxMutex diskmutex;
AudioProviderType type; AudioProviderType type;
@ -88,8 +86,6 @@ private:
int sample_rate; int sample_rate;
int bytes_per_sample; int bytes_per_sample;
void OpenStream();
void CloseStream();
void ConvertToRAMCache(PClip &tempclip); void ConvertToRAMCache(PClip &tempclip);
void ConvertToDiskCache(PClip &tempclip); void ConvertToDiskCache(PClip &tempclip);
void LoadFromClip(AVSValue clip); void LoadFromClip(AVSValue clip);
@ -100,19 +96,6 @@ private:
void Unload(); void Unload();
public: 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(wxString _filename, AudioDisplay *_display);
~AudioProvider(); ~AudioProvider();
@ -124,15 +107,4 @@ public:
int GetChannels(); int GetChannels();
__int64 GetNumSamples(); __int64 GetNumSamples();
int GetSampleRate(); 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; }
}; };

View file

@ -887,8 +887,7 @@ void VideoDisplay::OnPlayTimer(wxTimerEvent &event) {
// Sync audio // Sync audio
if (nextFrame % 25 == 0) { if (nextFrame % 25 == 0) {
__int64 audPos = audio->GetSampleAtMS(VFR_Output.GetTimeAtFrame(nextFrame)); __int64 audPos = audio->GetSampleAtMS(VFR_Output.GetTimeAtFrame(nextFrame));
audio->provider->playPos = audPos; audio->provider->SetCurrentPosition(audPos);
audio->provider->realPlayPos = audPos;
} }
} }