mirror of
https://gitlab.com/GaryOderNichts/re3-wiiu.git
synced 2024-11-27 11:34:14 +01:00
Multi-threaded audio streams
Under MULTITHREADED_AUDIO define.
This commit is contained in:
parent
5341840943
commit
107c4df69c
@ -1,8 +1,6 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#ifdef AUDIO_OAL
|
#ifdef AUDIO_OAL
|
||||||
#include "stream.h"
|
|
||||||
#include "sampman.h"
|
|
||||||
|
|
||||||
#if defined _MSC_VER && !defined CMAKE_NO_AUTOLINK
|
#if defined _MSC_VER && !defined CMAKE_NO_AUTOLINK
|
||||||
#ifdef AUDIO_OAL_USE_SNDFILE
|
#ifdef AUDIO_OAL_USE_SNDFILE
|
||||||
@ -22,6 +20,28 @@
|
|||||||
#include <opusfile.h>
|
#include <opusfile.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#ifdef MULTITHREADED_AUDIO
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include "MusicManager.h"
|
||||||
|
#include "stream.h"
|
||||||
|
|
||||||
|
std::thread gAudioThread;
|
||||||
|
std::mutex gAudioThreadQueueMutex;
|
||||||
|
std::condition_variable gAudioThreadCv;
|
||||||
|
bool gAudioThreadTerm = false;
|
||||||
|
std::queue<CStream*> gStreamsToProcess; // values are not unique, we will handle that ourself
|
||||||
|
#else
|
||||||
|
#include "stream.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "sampman.h"
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include "crossplatform.h"
|
#include "crossplatform.h"
|
||||||
#endif
|
#endif
|
||||||
@ -39,6 +59,10 @@ class CSortStereoBuffer
|
|||||||
{
|
{
|
||||||
uint16* PcmBuf;
|
uint16* PcmBuf;
|
||||||
size_t BufSize;
|
size_t BufSize;
|
||||||
|
//#ifdef MULTITHREADED_AUDIO
|
||||||
|
// std::mutex Mutex;
|
||||||
|
//#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CSortStereoBuffer() : PcmBuf(nil), BufSize(0) {}
|
CSortStereoBuffer() : PcmBuf(nil), BufSize(0) {}
|
||||||
~CSortStereoBuffer()
|
~CSortStereoBuffer()
|
||||||
@ -65,6 +89,9 @@ public:
|
|||||||
|
|
||||||
void SortStereo(void* buf, size_t size)
|
void SortStereo(void* buf, size_t size)
|
||||||
{
|
{
|
||||||
|
//#ifdef MULTITHREADED_AUDIO
|
||||||
|
// std::lock_guard<std::mutex> lock(Mutex);
|
||||||
|
//#endif
|
||||||
uint16* InBuf = (uint16*)buf;
|
uint16* InBuf = (uint16*)buf;
|
||||||
uint16* OutBuf = GetBuffer(size);
|
uint16* OutBuf = GetBuffer(size);
|
||||||
|
|
||||||
@ -279,6 +306,10 @@ public:
|
|||||||
#undef CLOSE_ON_ERROR
|
#undef CLOSE_ON_ERROR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FileOpen()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
~CWavFile()
|
~CWavFile()
|
||||||
{
|
{
|
||||||
Close();
|
Close();
|
||||||
@ -289,6 +320,7 @@ public:
|
|||||||
return m_bIsOpen;
|
return m_bIsOpen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint32 GetSampleSize()
|
uint32 GetSampleSize()
|
||||||
{
|
{
|
||||||
return sizeof(uint16);
|
return sizeof(uint16);
|
||||||
@ -405,6 +437,10 @@ public:
|
|||||||
m_pfSound = sf_open(path, SFM_READ, &m_soundInfo);
|
m_pfSound = sf_open(path, SFM_READ, &m_soundInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FileOpen()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
~CSndFile()
|
~CSndFile()
|
||||||
{
|
{
|
||||||
if ( m_pfSound )
|
if ( m_pfSound )
|
||||||
@ -464,8 +500,6 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef AUDIO_OAL_USE_MPG123
|
#ifdef AUDIO_OAL_USE_MPG123
|
||||||
// fuzzy seek eliminates stutter when playing ADF but spams errors a lot (and breaks radio sometimes)
|
|
||||||
//#define MP3_USE_FUZZY_SEEK
|
|
||||||
|
|
||||||
class CMP3File : public IDecoder
|
class CMP3File : public IDecoder
|
||||||
{
|
{
|
||||||
@ -474,43 +508,56 @@ protected:
|
|||||||
bool m_bOpened;
|
bool m_bOpened;
|
||||||
uint32 m_nRate;
|
uint32 m_nRate;
|
||||||
uint32 m_nChannels;
|
uint32 m_nChannels;
|
||||||
|
const char* m_pPath;
|
||||||
|
bool m_bFileNotOpenedYet;
|
||||||
|
|
||||||
CMP3File() :
|
CMP3File() :
|
||||||
m_pMH(nil),
|
m_pMH(nil),
|
||||||
m_bOpened(false),
|
m_bOpened(false),
|
||||||
m_nRate(0),
|
m_nRate(0),
|
||||||
|
m_bFileNotOpenedYet(false),
|
||||||
m_nChannels(0) {}
|
m_nChannels(0) {}
|
||||||
public:
|
public:
|
||||||
CMP3File(const char *path) :
|
CMP3File(const char *path) :
|
||||||
m_pMH(nil),
|
m_pMH(nil),
|
||||||
m_bOpened(false),
|
m_bOpened(false),
|
||||||
m_nRate(0),
|
m_nRate(0),
|
||||||
m_nChannels(0)
|
m_nChannels(0),
|
||||||
|
m_pPath(path),
|
||||||
|
m_bFileNotOpenedYet(false)
|
||||||
{
|
{
|
||||||
m_pMH = mpg123_new(nil, nil);
|
m_pMH = mpg123_new(nil, nil);
|
||||||
if ( m_pMH )
|
if ( m_pMH )
|
||||||
{
|
{
|
||||||
#ifdef MP3_USE_FUZZY_SEEK
|
|
||||||
mpg123_param(m_pMH, MPG123_FLAGS, MPG123_FUZZY | MPG123_SEEKBUFFER | MPG123_GAPLESS | MPG123_QUIET, 0.0);
|
|
||||||
#else
|
|
||||||
mpg123_param(m_pMH, MPG123_FLAGS, MPG123_SEEKBUFFER | MPG123_GAPLESS, 0.0);
|
mpg123_param(m_pMH, MPG123_FLAGS, MPG123_SEEKBUFFER | MPG123_GAPLESS, 0.0);
|
||||||
|
|
||||||
|
m_bOpened = true;
|
||||||
|
m_bFileNotOpenedYet = true;
|
||||||
|
// It's possible to move this to audioFileOpsThread(), but effect isn't noticable + probably not compatible with our current cutscene audio handling
|
||||||
|
#if 1
|
||||||
|
FileOpen();
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileOpen()
|
||||||
|
{
|
||||||
|
if(!m_bFileNotOpenedYet) return;
|
||||||
|
|
||||||
long rate = 0;
|
long rate = 0;
|
||||||
int channels = 0;
|
int channels = 0;
|
||||||
int encoding = 0;
|
int encoding = 0;
|
||||||
|
m_bOpened = mpg123_open(m_pMH, m_pPath) == MPG123_OK
|
||||||
m_bOpened = mpg123_open(m_pMH, path) == MPG123_OK
|
|
||||||
&& mpg123_getformat(m_pMH, &rate, &channels, &encoding) == MPG123_OK;
|
&& mpg123_getformat(m_pMH, &rate, &channels, &encoding) == MPG123_OK;
|
||||||
|
|
||||||
m_nRate = rate;
|
m_nRate = rate;
|
||||||
m_nChannels = channels;
|
m_nChannels = channels;
|
||||||
|
|
||||||
if ( IsOpened() )
|
if(IsOpened()) {
|
||||||
{
|
|
||||||
mpg123_format_none(m_pMH);
|
mpg123_format_none(m_pMH);
|
||||||
mpg123_format(m_pMH, rate, channels, encoding);
|
mpg123_format(m_pMH, rate, channels, encoding);
|
||||||
}
|
}
|
||||||
}
|
m_bFileNotOpenedYet = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
~CMP3File()
|
~CMP3File()
|
||||||
@ -535,7 +582,7 @@ public:
|
|||||||
|
|
||||||
uint32 GetSampleCount()
|
uint32 GetSampleCount()
|
||||||
{
|
{
|
||||||
if ( !IsOpened() ) return 0;
|
if ( !IsOpened() || m_bFileNotOpenedYet ) return 0;
|
||||||
return mpg123_length(m_pMH);
|
return mpg123_length(m_pMH);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -551,19 +598,19 @@ public:
|
|||||||
|
|
||||||
void Seek(uint32 milliseconds)
|
void Seek(uint32 milliseconds)
|
||||||
{
|
{
|
||||||
if ( !IsOpened() ) return;
|
if ( !IsOpened() || m_bFileNotOpenedYet ) return;
|
||||||
mpg123_seek(m_pMH, ms2samples(milliseconds), SEEK_SET);
|
mpg123_seek(m_pMH, ms2samples(milliseconds), SEEK_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 Tell()
|
uint32 Tell()
|
||||||
{
|
{
|
||||||
if ( !IsOpened() ) return 0;
|
if ( !IsOpened() || m_bFileNotOpenedYet ) return 0;
|
||||||
return samples2ms(mpg123_tell(m_pMH));
|
return samples2ms(mpg123_tell(m_pMH));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 Decode(void *buffer)
|
uint32 Decode(void *buffer)
|
||||||
{
|
{
|
||||||
if ( !IsOpened() ) return 0;
|
if ( !IsOpened() || m_bFileNotOpenedYet ) return 0;
|
||||||
|
|
||||||
size_t size;
|
size_t size;
|
||||||
int err = mpg123_read(m_pMH, (unsigned char *)buffer, GetBufferSize(), &size);
|
int err = mpg123_read(m_pMH, (unsigned char *)buffer, GetBufferSize(), &size);
|
||||||
@ -602,28 +649,41 @@ public:
|
|||||||
m_pMH = mpg123_new(nil, nil);
|
m_pMH = mpg123_new(nil, nil);
|
||||||
if (m_pMH)
|
if (m_pMH)
|
||||||
{
|
{
|
||||||
#ifdef MP3_USE_FUZZY_SEEK
|
|
||||||
mpg123_param(m_pMH, MPG123_FLAGS, MPG123_FUZZY | MPG123_SEEKBUFFER | MPG123_GAPLESS | MPG123_QUIET, 0.0);
|
|
||||||
#else
|
|
||||||
mpg123_param(m_pMH, MPG123_FLAGS, MPG123_SEEKBUFFER | MPG123_GAPLESS, 0.0);
|
mpg123_param(m_pMH, MPG123_FLAGS, MPG123_SEEKBUFFER | MPG123_GAPLESS, 0.0);
|
||||||
|
|
||||||
|
m_bOpened = true;
|
||||||
|
m_bFileNotOpenedYet = true;
|
||||||
|
m_pPath = path;
|
||||||
|
// It's possible to move this to audioFileOpsThread(), but effect isn't noticable + probably not compatible with our current cutscene audio handling
|
||||||
|
#if 1
|
||||||
|
FileOpen();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileOpen()
|
||||||
|
{
|
||||||
|
if(!m_bFileNotOpenedYet) return;
|
||||||
|
|
||||||
long rate = 0;
|
long rate = 0;
|
||||||
int channels = 0;
|
int channels = 0;
|
||||||
int encoding = 0;
|
int encoding = 0;
|
||||||
|
|
||||||
FILE* f = fopen(path, "rb");
|
FILE *f = fopen(m_pPath, "rb");
|
||||||
|
|
||||||
m_bOpened = mpg123_replace_reader_handle(m_pMH, r_read, r_seek, r_close) == MPG123_OK
|
m_bOpened = f && mpg123_replace_reader_handle(m_pMH, r_read, r_seek, r_close) == MPG123_OK
|
||||||
&& mpg123_open_handle(m_pMH, f) == MPG123_OK && mpg123_getformat(m_pMH, &rate, &channels, &encoding) == MPG123_OK;
|
&& mpg123_open_handle(m_pMH, f) == MPG123_OK && mpg123_getformat(m_pMH, &rate, &channels, &encoding) == MPG123_OK;
|
||||||
|
|
||||||
m_nRate = rate;
|
m_nRate = rate;
|
||||||
m_nChannels = channels;
|
m_nChannels = channels;
|
||||||
|
|
||||||
if (IsOpened())
|
if(IsOpened()) {
|
||||||
{
|
|
||||||
mpg123_format_none(m_pMH);
|
mpg123_format_none(m_pMH);
|
||||||
mpg123_format(m_pMH, rate, channels, encoding);
|
mpg123_format(m_pMH, rate, channels, encoding);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
m_bFileNotOpenedYet = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -744,6 +804,10 @@ public:
|
|||||||
m_ppVagBuffers[i] = new uint8[VB_BLOCK_SIZE];
|
m_ppVagBuffers[i] = new uint8[VB_BLOCK_SIZE];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FileOpen()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
~CVbFile()
|
~CVbFile()
|
||||||
{
|
{
|
||||||
if (m_pFile)
|
if (m_pFile)
|
||||||
@ -897,6 +961,10 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FileOpen()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
~COpusFile()
|
~COpusFile()
|
||||||
{
|
{
|
||||||
if (m_FileH)
|
if (m_FileH)
|
||||||
@ -961,11 +1029,183 @@ public:
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// For multi-thread: Someone always acquire stream's mutex before entering here
|
||||||
|
void
|
||||||
|
CStream::BuffersShouldBeFilled()
|
||||||
|
{
|
||||||
|
#ifdef MULTITHREADED_AUDIO
|
||||||
|
if (MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE) {
|
||||||
|
std::queue<std::pair<ALuint, ALuint>> tempQueue;
|
||||||
|
for(int i = 0; i < NUM_STREAMBUFFERS / 2; i++) {
|
||||||
|
tempQueue.push(std::pair<ALuint, ALuint>(m_alBuffers[i * 2], m_alBuffers[i * 2 + 1]));
|
||||||
|
}
|
||||||
|
m_fillBuffers.swap(tempQueue);
|
||||||
|
|
||||||
|
FlagAsToBeProcessed();
|
||||||
|
|
||||||
|
m_bActive = true; // to allow Update() to queue the filled buffers & play
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::queue<std::pair<ALuint, ALuint>>().swap(m_fillBuffers);
|
||||||
|
#endif
|
||||||
|
if ( FillBuffers() != 0 )
|
||||||
|
{
|
||||||
|
SetPlay(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns whether it's queued (not on multi-thread)
|
||||||
|
bool
|
||||||
|
CStream::BufferShouldBeFilledAndQueued(std::pair<ALuint, ALuint>* bufs)
|
||||||
|
{
|
||||||
|
#ifdef MULTITHREADED_AUDIO
|
||||||
|
if (MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE)
|
||||||
|
m_fillBuffers.push(*bufs);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
ALuint alBuffers[2] = {(*bufs).first, (*bufs).second}; // left - right
|
||||||
|
if (FillBuffer(alBuffers)) {
|
||||||
|
alSourceQueueBuffers(m_pAlSources[0], 1, &alBuffers[0]);
|
||||||
|
alSourceQueueBuffers(m_pAlSources[1], 1, &alBuffers[1]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MULTITHREADED_AUDIO
|
||||||
|
void
|
||||||
|
CStream::FlagAsToBeProcessed(bool close)
|
||||||
|
{
|
||||||
|
if (!close && MusicManager.m_nMusicMode == MUSICMODE_CUTSCENE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
gAudioThreadQueueMutex.lock();
|
||||||
|
gStreamsToProcess.push(this);
|
||||||
|
gAudioThreadQueueMutex.unlock();
|
||||||
|
|
||||||
|
gAudioThreadCv.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern CStream *aStream[];
|
||||||
|
void audioFileOpsThread()
|
||||||
|
{
|
||||||
|
std::queue<CStream*> m_streamsToDelete;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
CStream *stream;
|
||||||
|
{
|
||||||
|
// Just a semaphore
|
||||||
|
std::unique_lock<std::mutex> queueMutex(gAudioThreadQueueMutex);
|
||||||
|
gAudioThreadCv.wait(queueMutex, [m_streamsToDelete] { return gStreamsToProcess.size() > 0 || m_streamsToDelete.size() > 0 || gAudioThreadTerm; });
|
||||||
|
if (gAudioThreadTerm)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!gStreamsToProcess.empty()) {
|
||||||
|
stream = gStreamsToProcess.front();
|
||||||
|
gStreamsToProcess.pop();
|
||||||
|
} else {
|
||||||
|
// End of streams. Perform deleting streams
|
||||||
|
while(!m_streamsToDelete.empty()) {
|
||||||
|
CStream *stream = m_streamsToDelete.front();
|
||||||
|
m_streamsToDelete.pop();
|
||||||
|
if (stream->m_pSoundFile) {
|
||||||
|
delete stream->m_pSoundFile;
|
||||||
|
stream->m_pSoundFile = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream->m_pBuffer) {
|
||||||
|
free(stream->m_pBuffer);
|
||||||
|
stream->m_pBuffer = nil;
|
||||||
|
}
|
||||||
|
delete stream;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lock(stream->m_mutex);
|
||||||
|
|
||||||
|
std::pair<ALuint, ALuint> buffers, *lastBufAddr;
|
||||||
|
bool insertBufsAfterCheck = false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (stream->m_nDeleteMe == 1) {
|
||||||
|
m_streamsToDelete.push(stream);
|
||||||
|
stream->m_nDeleteMe = 2;
|
||||||
|
break;
|
||||||
|
} else if (stream->m_nDeleteMe == 2) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stream->IsOpened())
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (stream->m_bReset)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// We gave up this idea for now
|
||||||
|
/*
|
||||||
|
stream->m_pSoundFile->FileOpen();
|
||||||
|
|
||||||
|
// Deffered allocation, do it now
|
||||||
|
if (stream->m_pBuffer == nil) {
|
||||||
|
stream->m_pBuffer = malloc(stream->m_pSoundFile->GetBufferSize());
|
||||||
|
ASSERT(stream->m_pBuffer != nil);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (stream->m_bDoSeek) {
|
||||||
|
stream->m_bDoSeek = false;
|
||||||
|
int pos = stream->m_SeekPos;
|
||||||
|
lock.unlock();
|
||||||
|
stream->m_pSoundFile->Seek(pos);
|
||||||
|
lock.lock();
|
||||||
|
|
||||||
|
continue; // let's do the checks again, make sure we didn't miss anything while Seeking
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insertBufsAfterCheck) {
|
||||||
|
stream->m_queueBuffers.push(buffers);
|
||||||
|
insertBufsAfterCheck = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stream->m_fillBuffers.empty()) {
|
||||||
|
lastBufAddr = &stream->m_fillBuffers.front();
|
||||||
|
buffers = *lastBufAddr;
|
||||||
|
lock.unlock();
|
||||||
|
|
||||||
|
ALuint alBuffers[2] = {buffers.first, buffers.second}; // left - right
|
||||||
|
bool filled = stream->FillBuffer(alBuffers);
|
||||||
|
|
||||||
|
lock.lock();
|
||||||
|
|
||||||
|
// Make sure queue isn't touched after we released mutex
|
||||||
|
if (!stream->m_fillBuffers.empty() && lastBufAddr == &stream->m_fillBuffers.front()) {
|
||||||
|
stream->m_fillBuffers.pop();
|
||||||
|
if (filled)
|
||||||
|
insertBufsAfterCheck = true; // Also make sure stream's properties aren't changed. So make one more pass, and push it to m_queueBuffers only if it pass checks again.
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
break;
|
||||||
|
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
} while(true);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void CStream::Initialise()
|
void CStream::Initialise()
|
||||||
{
|
{
|
||||||
#ifdef AUDIO_OAL_USE_MPG123
|
#ifdef AUDIO_OAL_USE_MPG123
|
||||||
mpg123_init();
|
mpg123_init();
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef MULTITHREADED_AUDIO
|
||||||
|
gAudioThread = std::thread(audioFileOpsThread);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStream::Terminate()
|
void CStream::Terminate()
|
||||||
@ -973,6 +1213,14 @@ void CStream::Terminate()
|
|||||||
#ifdef AUDIO_OAL_USE_MPG123
|
#ifdef AUDIO_OAL_USE_MPG123
|
||||||
mpg123_exit();
|
mpg123_exit();
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef MULTITHREADED_AUDIO
|
||||||
|
gAudioThreadQueueMutex.lock();
|
||||||
|
gAudioThreadTerm = true;
|
||||||
|
gAudioThreadQueueMutex.unlock();
|
||||||
|
|
||||||
|
gAudioThreadCv.notify_one();
|
||||||
|
gAudioThread.join();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
CStream::CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS], uint32 overrideSampleRate) :
|
CStream::CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS], uint32 overrideSampleRate) :
|
||||||
@ -981,6 +1229,11 @@ CStream::CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBU
|
|||||||
m_pBuffer(nil),
|
m_pBuffer(nil),
|
||||||
m_bPaused(false),
|
m_bPaused(false),
|
||||||
m_bActive(false),
|
m_bActive(false),
|
||||||
|
#ifdef MULTITHREADED_AUDIO
|
||||||
|
m_nDeleteMe(false),
|
||||||
|
m_bDoSeek(false),
|
||||||
|
m_SeekPos(0),
|
||||||
|
#endif
|
||||||
m_pSoundFile(nil),
|
m_pSoundFile(nil),
|
||||||
m_bReset(false),
|
m_bReset(false),
|
||||||
m_nVolume(0),
|
m_nVolume(0),
|
||||||
@ -1027,8 +1280,10 @@ CStream::CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBU
|
|||||||
|
|
||||||
if ( IsOpened() )
|
if ( IsOpened() )
|
||||||
{
|
{
|
||||||
m_pBuffer = malloc(m_pSoundFile->GetBufferSize());
|
uint32 bufSize = m_pSoundFile->GetBufferSize();
|
||||||
ASSERT(m_pBuffer!=nil);
|
if(bufSize != 0) { // Otherwise it's deferred
|
||||||
|
m_pBuffer = malloc(bufSize);
|
||||||
|
ASSERT(m_pBuffer != nil);
|
||||||
|
|
||||||
DEV("AvgSamplesPerSec: %d\n", m_pSoundFile->GetAvgSamplesPerSec());
|
DEV("AvgSamplesPerSec: %d\n", m_pSoundFile->GetAvgSamplesPerSec());
|
||||||
DEV("SampleCount: %d\n", m_pSoundFile->GetSampleCount());
|
DEV("SampleCount: %d\n", m_pSoundFile->GetSampleCount());
|
||||||
@ -1037,18 +1292,30 @@ CStream::CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBU
|
|||||||
DEV("Buffer Samples: %d\n", m_pSoundFile->GetBufferSamples());
|
DEV("Buffer Samples: %d\n", m_pSoundFile->GetBufferSamples());
|
||||||
DEV("Buffer sec: %f\n", (float(m_pSoundFile->GetBufferSamples()) / float(m_pSoundFile->GetChannels())/ float(m_pSoundFile->GetSampleRate())));
|
DEV("Buffer sec: %f\n", (float(m_pSoundFile->GetBufferSamples()) / float(m_pSoundFile->GetChannels())/ float(m_pSoundFile->GetSampleRate())));
|
||||||
DEV("Length MS: %02d:%02d\n", (m_pSoundFile->GetLength() / 1000) / 60, (m_pSoundFile->GetLength() / 1000) % 60);
|
DEV("Length MS: %02d:%02d\n", (m_pSoundFile->GetLength() / 1000) / 60, (m_pSoundFile->GetLength() / 1000) % 60);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CStream::~CStream()
|
CStream::~CStream()
|
||||||
{
|
{
|
||||||
Delete();
|
assert(!IsOpened());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStream::Delete()
|
void CStream::Close()
|
||||||
{
|
{
|
||||||
|
#ifdef MULTITHREADED_AUDIO
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
Stop();
|
||||||
|
ClearBuffers();
|
||||||
|
m_nDeleteMe = true;
|
||||||
|
// clearing buffer queues are not needed. after m_nDeleteMe set, this stream is ded
|
||||||
|
}
|
||||||
|
|
||||||
|
FlagAsToBeProcessed(true);
|
||||||
|
#else
|
||||||
Stop();
|
Stop();
|
||||||
ClearBuffers();
|
ClearBuffers();
|
||||||
|
|
||||||
@ -1063,6 +1330,7 @@ void CStream::Delete()
|
|||||||
free(m_pBuffer);
|
free(m_pBuffer);
|
||||||
m_pBuffer = nil;
|
m_pBuffer = nil;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CStream::HasSource()
|
bool CStream::HasSource()
|
||||||
@ -1086,6 +1354,14 @@ bool CStream::IsPlaying()
|
|||||||
alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState[1]);
|
alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState[1]);
|
||||||
if (sourceState[0] == AL_PLAYING || sourceState[1] == AL_PLAYING)
|
if (sourceState[0] == AL_PLAYING || sourceState[1] == AL_PLAYING)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
#ifdef MULTITHREADED_AUDIO
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
// Streams are designed in such a way that m_fillBuffers and m_queueBuffers will be *always* filled if audio is playing, and mutex is acquired
|
||||||
|
if (!m_fillBuffers.empty() || !m_queueBuffers.emptyNts())
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -1160,8 +1436,24 @@ void CStream::SetPan(uint8 nPan)
|
|||||||
void CStream::SetPosMS(uint32 nPos)
|
void CStream::SetPosMS(uint32 nPos)
|
||||||
{
|
{
|
||||||
if ( !IsOpened() ) return;
|
if ( !IsOpened() ) return;
|
||||||
|
|
||||||
|
#ifdef MULTITHREADED_AUDIO
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
std::queue<std::pair<ALuint, ALuint>>().swap(m_fillBuffers);
|
||||||
|
tsQueue<std::pair<ALuint, ALuint>>().swapNts(m_queueBuffers); // TSness not required, second thread always access it when stream mutex acquired
|
||||||
|
|
||||||
|
if (MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE) {
|
||||||
|
m_bDoSeek = true;
|
||||||
|
m_SeekPos = nPos;
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
m_pSoundFile->Seek(nPos);
|
m_pSoundFile->Seek(nPos);
|
||||||
|
}
|
||||||
ClearBuffers();
|
ClearBuffers();
|
||||||
|
|
||||||
|
// adding to gStreamsToProcess not needed, someone always calls Start() / BuffersShouldBeFilled() after SetPosMS
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 CStream::GetPosMS()
|
uint32 CStream::GetPosMS()
|
||||||
@ -1169,10 +1461,16 @@ uint32 CStream::GetPosMS()
|
|||||||
if ( !HasSource() ) return 0;
|
if ( !HasSource() ) return 0;
|
||||||
if ( !IsOpened() ) return 0;
|
if ( !IsOpened() ) return 0;
|
||||||
|
|
||||||
|
// Deferred init causes division by zero
|
||||||
|
if (m_pSoundFile->GetChannels() == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
ALint offset;
|
ALint offset;
|
||||||
//alGetSourcei(m_alSource, AL_SAMPLE_OFFSET, &offset);
|
//alGetSourcei(m_alSource, AL_SAMPLE_OFFSET, &offset);
|
||||||
alGetSourcei(m_pAlSources[0], AL_BYTE_OFFSET, &offset);
|
alGetSourcei(m_pAlSources[0], AL_BYTE_OFFSET, &offset);
|
||||||
|
|
||||||
|
//std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
return m_pSoundFile->Tell()
|
return m_pSoundFile->Tell()
|
||||||
- m_pSoundFile->samples2ms(m_pSoundFile->GetBufferSamples() * (NUM_STREAMBUFFERS/2-1)) / m_pSoundFile->GetChannels()
|
- m_pSoundFile->samples2ms(m_pSoundFile->GetBufferSamples() * (NUM_STREAMBUFFERS/2-1)) / m_pSoundFile->GetChannels()
|
||||||
+ m_pSoundFile->samples2ms(offset/m_pSoundFile->GetSampleSize()) / m_pSoundFile->GetChannels();
|
+ m_pSoundFile->samples2ms(offset/m_pSoundFile->GetSampleSize()) / m_pSoundFile->GetChannels();
|
||||||
@ -1186,6 +1484,7 @@ uint32 CStream::GetLengthMS()
|
|||||||
|
|
||||||
bool CStream::FillBuffer(ALuint *alBuffer)
|
bool CStream::FillBuffer(ALuint *alBuffer)
|
||||||
{
|
{
|
||||||
|
#ifndef MULTITHREADED_AUDIO
|
||||||
if ( !HasSource() )
|
if ( !HasSource() )
|
||||||
return false;
|
return false;
|
||||||
if ( !IsOpened() )
|
if ( !IsOpened() )
|
||||||
@ -1194,6 +1493,7 @@ bool CStream::FillBuffer(ALuint *alBuffer)
|
|||||||
return false;
|
return false;
|
||||||
if ( !(alBuffer[1] != AL_NONE && alIsBuffer(alBuffer[1])) )
|
if ( !(alBuffer[1] != AL_NONE && alIsBuffer(alBuffer[1])) )
|
||||||
return false;
|
return false;
|
||||||
|
#endif
|
||||||
|
|
||||||
uint32 size = m_pSoundFile->Decode(m_pBuffer);
|
uint32 size = m_pSoundFile->Decode(m_pBuffer);
|
||||||
if( size == 0 )
|
if( size == 0 )
|
||||||
@ -1210,6 +1510,26 @@ bool CStream::FillBuffer(ALuint *alBuffer)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MULTITHREADED_AUDIO
|
||||||
|
bool CStream::QueueBuffers()
|
||||||
|
{
|
||||||
|
bool buffersQueued = false;
|
||||||
|
std::pair<ALuint, ALuint> buffers;
|
||||||
|
while (m_queueBuffers.peekPop(&buffers)) // beware: m_queueBuffers is tsQueue
|
||||||
|
{
|
||||||
|
ALuint leftBuf = buffers.first;
|
||||||
|
ALuint rightBuf = buffers.second;
|
||||||
|
|
||||||
|
alSourceQueueBuffers(m_pAlSources[0], 1, &leftBuf);
|
||||||
|
alSourceQueueBuffers(m_pAlSources[1], 1, &rightBuf);
|
||||||
|
|
||||||
|
buffersQueued = true;
|
||||||
|
}
|
||||||
|
return buffersQueued;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Only used in single-threaded audio or cutscene audio
|
||||||
int32 CStream::FillBuffers()
|
int32 CStream::FillBuffers()
|
||||||
{
|
{
|
||||||
int32 i = 0;
|
int32 i = 0;
|
||||||
@ -1239,17 +1559,33 @@ void CStream::ClearBuffers()
|
|||||||
alSourceUnqueueBuffers(m_pAlSources[1], 1, &value);
|
alSourceUnqueueBuffers(m_pAlSources[1], 1, &value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CStream::Setup(bool imSureQueueIsEmpty)
|
bool CStream::Setup(bool imSureQueueIsEmpty, bool lock)
|
||||||
{
|
{
|
||||||
if ( IsOpened() )
|
if ( IsOpened() )
|
||||||
{
|
{
|
||||||
alSourcei(m_pAlSources[0], AL_LOOPING, AL_FALSE);
|
#ifdef MULTITHREADED_AUDIO
|
||||||
alSourcei(m_pAlSources[1], AL_LOOPING, AL_FALSE);
|
if (lock)
|
||||||
|
m_mutex.lock();
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!imSureQueueIsEmpty) {
|
if (!imSureQueueIsEmpty) {
|
||||||
SetPlay(false);
|
Stop();
|
||||||
ClearBuffers();
|
ClearBuffers();
|
||||||
}
|
}
|
||||||
|
#ifdef MULTITHREADED_AUDIO
|
||||||
|
if (MusicManager.m_nMusicMode == MUSICMODE_CUTSCENE) {
|
||||||
m_pSoundFile->Seek(0);
|
m_pSoundFile->Seek(0);
|
||||||
|
} else {
|
||||||
|
m_bDoSeek = true;
|
||||||
|
m_SeekPos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lock)
|
||||||
|
m_mutex.unlock();
|
||||||
|
#else
|
||||||
|
m_pSoundFile->Seek(0);
|
||||||
|
#endif
|
||||||
|
|
||||||
//SetPosition(0.0f, 0.0f, 0.0f);
|
//SetPosition(0.0f, 0.0f, 0.0f);
|
||||||
SetPitch(1.0f);
|
SetPitch(1.0f);
|
||||||
//SetPan(m_nPan);
|
//SetPan(m_nPan);
|
||||||
@ -1302,8 +1638,12 @@ void CStream::SetPlay(bool state)
|
|||||||
void CStream::Start()
|
void CStream::Start()
|
||||||
{
|
{
|
||||||
if ( !HasSource() ) return;
|
if ( !HasSource() ) return;
|
||||||
if ( FillBuffers() != 0 )
|
|
||||||
SetPlay(true);
|
#ifdef MULTITHREADED_AUDIO
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
tsQueue<std::pair<ALuint, ALuint>>().swapNts(m_queueBuffers); // TSness not required, second thread always access it when stream mutex acquired
|
||||||
|
#endif
|
||||||
|
BuffersShouldBeFilled();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStream::Stop()
|
void CStream::Stop()
|
||||||
@ -1325,8 +1665,22 @@ void CStream::Update()
|
|||||||
|
|
||||||
if ( !m_bPaused )
|
if ( !m_bPaused )
|
||||||
{
|
{
|
||||||
ALint totalBuffers[2] = { 0, 0 };
|
|
||||||
ALint buffersProcessed[2] = { 0, 0 };
|
bool buffersQueuedAndStarted = false;
|
||||||
|
bool buffersQueuedButNotStarted = false;
|
||||||
|
#ifdef MULTITHREADED_AUDIO
|
||||||
|
// Put it in here because we need totalBuffers after queueing to decide when to loop audio
|
||||||
|
if (m_bActive)
|
||||||
|
{
|
||||||
|
buffersQueuedAndStarted = QueueBuffers();
|
||||||
|
if(buffersQueuedAndStarted) {
|
||||||
|
SetPlay(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ALint totalBuffers[2] = {0, 0};
|
||||||
|
ALint buffersProcessed[2] = {0, 0};
|
||||||
|
|
||||||
// Relying a lot on left buffer states in here
|
// Relying a lot on left buffer states in here
|
||||||
|
|
||||||
@ -1347,36 +1701,58 @@ void CStream::Update()
|
|||||||
// AL_BUFFERS_PROCESSED = Index of the buffer being processing right now. Buffers coming after that(have greater index) are pending buffers.
|
// AL_BUFFERS_PROCESSED = Index of the buffer being processing right now. Buffers coming after that(have greater index) are pending buffers.
|
||||||
// which means: totalBuffers[0] - buffersProcessed[0] = pending buffers
|
// which means: totalBuffers[0] - buffersProcessed[0] = pending buffers
|
||||||
|
|
||||||
bool buffersRefilled = false;
|
|
||||||
|
|
||||||
// We should wait queue to be cleared to loop track, because position calculation relies on queue.
|
// We should wait queue to be cleared to loop track, because position calculation relies on queue.
|
||||||
if (m_nLoopCount != 1 && m_bActive && totalBuffers[0] == 0)
|
if (m_nLoopCount != 1 && m_bActive && totalBuffers[0] == 0)
|
||||||
{
|
{
|
||||||
Setup(true);
|
#ifdef MULTITHREADED_AUDIO
|
||||||
buffersRefilled = FillBuffers() != 0;
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
if (m_fillBuffers.empty() && m_queueBuffers.emptyNts()) // we already acquired stream mutex, which is enough for second thread. thus Nts variant
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
Setup(true, false);
|
||||||
|
BuffersShouldBeFilled(); // will also call SetPlay(true)
|
||||||
if (m_nLoopCount != 0)
|
if (m_nLoopCount != 0)
|
||||||
m_nLoopCount--;
|
m_nLoopCount--;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
while( buffersProcessed[0]-- )
|
static std::queue<std::pair<ALuint, ALuint>> tempFillBuffer;
|
||||||
|
|
||||||
|
while ( buffersProcessed[0]-- )
|
||||||
{
|
{
|
||||||
ALuint buffer[2];
|
ALuint buffer[2];
|
||||||
|
|
||||||
alSourceUnqueueBuffers(m_pAlSources[0], 1, &buffer[0]);
|
alSourceUnqueueBuffers(m_pAlSources[0], 1, &buffer[0]);
|
||||||
alSourceUnqueueBuffers(m_pAlSources[1], 1, &buffer[1]);
|
alSourceUnqueueBuffers(m_pAlSources[1], 1, &buffer[1]);
|
||||||
|
|
||||||
if (m_bActive && FillBuffer(buffer))
|
if (m_bActive)
|
||||||
{
|
{
|
||||||
buffersRefilled = true;
|
tempFillBuffer.push(std::pair<ALuint, ALuint>(buffer[0], buffer[1]));
|
||||||
alSourceQueueBuffers(m_pAlSources[0], 1, &buffer[0]);
|
|
||||||
alSourceQueueBuffers(m_pAlSources[1], 1, &buffer[1]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Two reasons: 1-Source may be starved to audio and stopped itself, 2- We're already waiting it to starve and die for looping track!
|
if (m_bActive && buffersProcessed[1])
|
||||||
if (m_bActive && (buffersRefilled || (totalBuffers[1] - buffersProcessed[1] != 0)))
|
{
|
||||||
|
#ifdef MULTITHREADED_AUDIO
|
||||||
|
m_mutex.lock();
|
||||||
|
#endif
|
||||||
|
while (!tempFillBuffer.empty()) {
|
||||||
|
auto elem = tempFillBuffer.front();
|
||||||
|
tempFillBuffer.pop();
|
||||||
|
buffersQueuedButNotStarted = BufferShouldBeFilledAndQueued(&elem);
|
||||||
|
}
|
||||||
|
#ifdef MULTITHREADED_AUDIO
|
||||||
|
m_mutex.unlock();
|
||||||
|
FlagAsToBeProcessed();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Source may be starved to audio and stopped itself
|
||||||
|
if (m_bActive && !buffersQueuedAndStarted && (buffersQueuedButNotStarted || (totalBuffers[1] - buffersProcessed[1] != 0)))
|
||||||
SetPlay(true);
|
SetPlay(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1385,28 +1761,45 @@ void CStream::ProviderInit()
|
|||||||
{
|
{
|
||||||
if ( m_bReset )
|
if ( m_bReset )
|
||||||
{
|
{
|
||||||
if ( Setup(true) )
|
if ( Setup(true, false) ) // lock not needed, thread can't process streams with m_bReset set
|
||||||
{
|
{
|
||||||
SetPan(m_nPan);
|
SetPan(m_nPan);
|
||||||
SetVolume(m_nVolume);
|
SetVolume(m_nVolume);
|
||||||
SetLoopCount(m_nLoopCount);
|
SetLoopCount(m_nLoopCount);
|
||||||
SetPosMS(m_nPosBeforeReset);
|
SetPosMS(m_nPosBeforeReset);
|
||||||
if (m_bActive)
|
#ifdef MULTITHREADED_AUDIO
|
||||||
FillBuffers();
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
SetPlay(m_bActive);
|
#endif
|
||||||
if ( m_bPaused )
|
if(m_bActive)
|
||||||
|
BuffersShouldBeFilled();
|
||||||
|
|
||||||
|
if (m_bPaused)
|
||||||
Pause();
|
Pause();
|
||||||
}
|
|
||||||
|
|
||||||
m_bReset = false;
|
m_bReset = false;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
#ifdef MULTITHREADED_AUDIO
|
||||||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
|
#endif
|
||||||
|
m_bReset = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStream::ProviderTerm()
|
void CStream::ProviderTerm()
|
||||||
{
|
{
|
||||||
|
#ifdef MULTITHREADED_AUDIO
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
// unlike Close() we will reuse this stream, so clearing queues are important.
|
||||||
|
std::queue<std::pair<ALuint, ALuint>>().swap(m_fillBuffers);
|
||||||
|
tsQueue<std::pair<ALuint, ALuint>>().swapNts(m_queueBuffers); // stream mutex is already acquired, thus Nts variant
|
||||||
|
#endif
|
||||||
m_bReset = true;
|
m_bReset = true;
|
||||||
m_nPosBeforeReset = GetPosMS();
|
m_nPosBeforeReset = GetPosMS();
|
||||||
|
|
||||||
|
Stop();
|
||||||
ClearBuffers();
|
ClearBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ public:
|
|||||||
virtual ~IDecoder() { }
|
virtual ~IDecoder() { }
|
||||||
|
|
||||||
virtual bool IsOpened() = 0;
|
virtual bool IsOpened() = 0;
|
||||||
|
virtual void FileOpen() = 0;
|
||||||
|
|
||||||
virtual uint32 GetSampleSize() = 0;
|
virtual uint32 GetSampleSize() = 0;
|
||||||
virtual uint32 GetSampleCount() = 0;
|
virtual uint32 GetSampleCount() = 0;
|
||||||
@ -48,12 +49,70 @@ public:
|
|||||||
|
|
||||||
uint32 GetLength()
|
uint32 GetLength()
|
||||||
{
|
{
|
||||||
|
FileOpen(); // abort deferred init, we need length now - game has to cache audio file sizes
|
||||||
return float(GetSampleCount()) * 1000.0f / float(GetSampleRate());
|
return float(GetSampleCount()) * 1000.0f / float(GetSampleRate());
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual uint32 Decode(void *buffer) = 0;
|
virtual uint32 Decode(void *buffer) = 0;
|
||||||
};
|
};
|
||||||
|
#ifdef MULTITHREADED_AUDIO
|
||||||
|
template <typename T> class tsQueue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
tsQueue() : count(0) { }
|
||||||
|
|
||||||
|
void push(const T &value)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
m_queue.push(value);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool peekPop(T *retVal)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
if (count == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*retVal = m_queue.front();
|
||||||
|
m_queue.pop();
|
||||||
|
count--;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void swapNts(tsQueue<T> &replaceWith)
|
||||||
|
{
|
||||||
|
m_queue.swap(replaceWith.m_queue);
|
||||||
|
replaceWith.count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
void swapTs(tsQueue<T> &replaceWith)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
std::lock_guard<std::mutex> lock2(replaceWith.m_mutex);
|
||||||
|
swapNts(replaceWith);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool emptyNts()
|
||||||
|
{
|
||||||
|
return count == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
bool emptyTs()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
return emptyNts();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
std::queue<T> m_queue;
|
||||||
|
int count;
|
||||||
|
mutable std::mutex m_mutex;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
class CStream
|
class CStream
|
||||||
{
|
{
|
||||||
char m_aFilename[128];
|
char m_aFilename[128];
|
||||||
@ -63,6 +122,16 @@ class CStream
|
|||||||
bool m_bPaused;
|
bool m_bPaused;
|
||||||
bool m_bActive;
|
bool m_bActive;
|
||||||
|
|
||||||
|
public:
|
||||||
|
#ifdef MULTITHREADED_AUDIO
|
||||||
|
std::mutex m_mutex;
|
||||||
|
std::queue<std::pair<ALuint, ALuint>> m_fillBuffers; // left and right buffer
|
||||||
|
tsQueue<std::pair<ALuint, ALuint>> m_queueBuffers;
|
||||||
|
bool m_bDoSeek;
|
||||||
|
uint32 m_SeekPos;
|
||||||
|
uint8 m_nDeleteMe; // 1: add to delete list 2: already on delete list
|
||||||
|
#endif
|
||||||
|
|
||||||
void *m_pBuffer;
|
void *m_pBuffer;
|
||||||
|
|
||||||
bool m_bReset;
|
bool m_bReset;
|
||||||
@ -73,6 +142,13 @@ class CStream
|
|||||||
|
|
||||||
IDecoder *m_pSoundFile;
|
IDecoder *m_pSoundFile;
|
||||||
|
|
||||||
|
void BuffersShouldBeFilled(); // all
|
||||||
|
bool BufferShouldBeFilledAndQueued(std::pair<ALuint, ALuint>*); // two (left-right)
|
||||||
|
#ifdef MULTITHREADED_AUDIO
|
||||||
|
void FlagAsToBeProcessed(bool close = false);
|
||||||
|
bool QueueBuffers();
|
||||||
|
#endif
|
||||||
|
|
||||||
bool HasSource();
|
bool HasSource();
|
||||||
void SetPosition(int i, float x, float y, float z);
|
void SetPosition(int i, float x, float y, float z);
|
||||||
void SetPitch(float pitch);
|
void SetPitch(float pitch);
|
||||||
@ -83,13 +159,13 @@ class CStream
|
|||||||
bool FillBuffer(ALuint *alBuffer);
|
bool FillBuffer(ALuint *alBuffer);
|
||||||
int32 FillBuffers();
|
int32 FillBuffers();
|
||||||
void ClearBuffers();
|
void ClearBuffers();
|
||||||
public:
|
//public:
|
||||||
static void Initialise();
|
static void Initialise();
|
||||||
static void Terminate();
|
static void Terminate();
|
||||||
|
|
||||||
CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS], uint32 overrideSampleRate = 32000);
|
CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS], uint32 overrideSampleRate = 32000);
|
||||||
~CStream();
|
~CStream();
|
||||||
void Delete();
|
void Close();
|
||||||
|
|
||||||
bool IsOpened();
|
bool IsOpened();
|
||||||
bool IsPlaying();
|
bool IsPlaying();
|
||||||
@ -100,13 +176,12 @@ public:
|
|||||||
uint32 GetPosMS();
|
uint32 GetPosMS();
|
||||||
uint32 GetLengthMS();
|
uint32 GetLengthMS();
|
||||||
|
|
||||||
bool Setup(bool imSureQueueIsEmpty = false);
|
bool Setup(bool imSureQueueIsEmpty = false, bool lock = true);
|
||||||
void Start();
|
void Start();
|
||||||
void Stop();
|
void Stop();
|
||||||
void Update(void);
|
void Update(void);
|
||||||
void SetLoopCount(int32);
|
void SetLoopCount(int32);
|
||||||
|
|
||||||
|
|
||||||
void ProviderInit();
|
void ProviderInit();
|
||||||
void ProviderTerm();
|
void ProviderTerm();
|
||||||
};
|
};
|
||||||
|
@ -34,6 +34,12 @@
|
|||||||
#include "oal/oal_utils.h"
|
#include "oal/oal_utils.h"
|
||||||
#include "oal/aldlist.h"
|
#include "oal/aldlist.h"
|
||||||
#include "oal/channel.h"
|
#include "oal/channel.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#ifdef MULTITHREADED_AUDIO
|
||||||
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
#endif
|
||||||
#include "oal/stream.h"
|
#include "oal/stream.h"
|
||||||
|
|
||||||
#include "AudioManager.h"
|
#include "AudioManager.h"
|
||||||
@ -521,7 +527,7 @@ _FindMP3s(void)
|
|||||||
if (aStream[0] && aStream[0]->IsOpened())
|
if (aStream[0] && aStream[0]->IsOpened())
|
||||||
{
|
{
|
||||||
total_ms = aStream[0]->GetLengthMS();
|
total_ms = aStream[0]->GetLengthMS();
|
||||||
delete aStream[0];
|
aStream[0]->Close();
|
||||||
aStream[0] = NULL;
|
aStream[0] = NULL;
|
||||||
|
|
||||||
OutputDebugString(fd.cFileName);
|
OutputDebugString(fd.cFileName);
|
||||||
@ -595,7 +601,7 @@ _FindMP3s(void)
|
|||||||
if (aStream[0] && aStream[0]->IsOpened())
|
if (aStream[0] && aStream[0]->IsOpened())
|
||||||
{
|
{
|
||||||
total_ms = aStream[0]->GetLengthMS();
|
total_ms = aStream[0]->GetLengthMS();
|
||||||
delete aStream[0];
|
aStream[0]->Close();
|
||||||
aStream[0] = NULL;
|
aStream[0] = NULL;
|
||||||
|
|
||||||
OutputDebugString(fd.cFileName);
|
OutputDebugString(fd.cFileName);
|
||||||
@ -655,7 +661,7 @@ _FindMP3s(void)
|
|||||||
if (aStream[0] && aStream[0]->IsOpened())
|
if (aStream[0] && aStream[0]->IsOpened())
|
||||||
{
|
{
|
||||||
total_ms = aStream[0]->GetLengthMS();
|
total_ms = aStream[0]->GetLengthMS();
|
||||||
delete aStream[0];
|
aStream[0]->Close();
|
||||||
aStream[0] = NULL;
|
aStream[0] = NULL;
|
||||||
|
|
||||||
OutputDebugString(fd.cFileName);
|
OutputDebugString(fd.cFileName);
|
||||||
@ -811,6 +817,7 @@ cSampleManager::Initialise(void)
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
EFXInit();
|
EFXInit();
|
||||||
|
|
||||||
CStream::Initialise();
|
CStream::Initialise();
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -971,7 +978,7 @@ cSampleManager::Initialise(void)
|
|||||||
if ( aStream[0] && aStream[0]->IsOpened() )
|
if ( aStream[0] && aStream[0]->IsOpened() )
|
||||||
{
|
{
|
||||||
uint32 tatalms = aStream[0]->GetLengthMS();
|
uint32 tatalms = aStream[0]->GetLengthMS();
|
||||||
delete aStream[0];
|
aStream[0]->Close();
|
||||||
aStream[0] = NULL;
|
aStream[0] = NULL;
|
||||||
|
|
||||||
nStreamLength[i] = tatalms;
|
nStreamLength[i] = tatalms;
|
||||||
@ -1107,7 +1114,7 @@ cSampleManager::Terminate(void)
|
|||||||
CStream *stream = aStream[i];
|
CStream *stream = aStream[i];
|
||||||
if (stream)
|
if (stream)
|
||||||
{
|
{
|
||||||
delete stream;
|
stream->Close();
|
||||||
aStream[i] = NULL;
|
aStream[i] = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1688,7 +1695,7 @@ cSampleManager::PreloadStreamedFile(uint32 nFile, uint8 nStream)
|
|||||||
{
|
{
|
||||||
if ( aStream[nStream] )
|
if ( aStream[nStream] )
|
||||||
{
|
{
|
||||||
delete aStream[nStream];
|
aStream[nStream]->Close();
|
||||||
aStream[nStream] = NULL;
|
aStream[nStream] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1700,7 +1707,7 @@ cSampleManager::PreloadStreamedFile(uint32 nFile, uint8 nStream)
|
|||||||
aStream[nStream] = stream;
|
aStream[nStream] = stream;
|
||||||
if ( !stream->Setup() )
|
if ( !stream->Setup() )
|
||||||
{
|
{
|
||||||
delete stream;
|
stream->Close();
|
||||||
aStream[nStream] = NULL;
|
aStream[nStream] = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1747,7 +1754,7 @@ cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream)
|
|||||||
|
|
||||||
if ( aStream[nStream] )
|
if ( aStream[nStream] )
|
||||||
{
|
{
|
||||||
delete aStream[nStream];
|
aStream[nStream]->Close();
|
||||||
aStream[nStream] = NULL;
|
aStream[nStream] = NULL;
|
||||||
}
|
}
|
||||||
if ( nFile == STREAMED_SOUND_RADIO_MP3_PLAYER )
|
if ( nFile == STREAMED_SOUND_RADIO_MP3_PLAYER )
|
||||||
@ -1780,7 +1787,7 @@ cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream)
|
|||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
} else {
|
} else {
|
||||||
delete stream;
|
stream->Close();
|
||||||
aStream[nStream] = NULL;
|
aStream[nStream] = NULL;
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -1805,7 +1812,7 @@ cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream)
|
|||||||
_bIsMp3Active = TRUE;
|
_bIsMp3Active = TRUE;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
} else {
|
} else {
|
||||||
delete aStream[nStream];
|
aStream[nStream]->Close();
|
||||||
aStream[nStream] = NULL;
|
aStream[nStream] = NULL;
|
||||||
}
|
}
|
||||||
// fall through, start playing from another song
|
// fall through, start playing from another song
|
||||||
@ -1839,7 +1846,7 @@ cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream)
|
|||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
} else {
|
} else {
|
||||||
delete stream;
|
stream->Close();
|
||||||
aStream[nStream] = NULL;
|
aStream[nStream] = NULL;
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -1861,7 +1868,7 @@ cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream)
|
|||||||
#endif
|
#endif
|
||||||
return TRUE;
|
return TRUE;
|
||||||
} else {
|
} else {
|
||||||
delete aStream[nStream];
|
aStream[nStream]->Close();
|
||||||
aStream[nStream] = NULL;
|
aStream[nStream] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1888,7 +1895,7 @@ cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream)
|
|||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
} else {
|
} else {
|
||||||
delete stream;
|
stream->Close();
|
||||||
aStream[nStream] = NULL;
|
aStream[nStream] = NULL;
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -1903,7 +1910,7 @@ cSampleManager::StopStreamedFile(uint8 nStream)
|
|||||||
|
|
||||||
if ( stream )
|
if ( stream )
|
||||||
{
|
{
|
||||||
delete stream;
|
stream->Close();
|
||||||
aStream[nStream] = NULL;
|
aStream[nStream] = NULL;
|
||||||
|
|
||||||
if ( nStream == 0 )
|
if ( nStream == 0 )
|
||||||
|
@ -398,6 +398,7 @@ static_assert(false, "SUPPORT_XBOX_SCRIPT and SUPPORT_MOBILE_SCRIPT are mutually
|
|||||||
//#define PS2_AUDIO_PATHS // changes audio paths for cutscenes and radio to PS2 paths (needs vbdec on MSS builds)
|
//#define PS2_AUDIO_PATHS // changes audio paths for cutscenes and radio to PS2 paths (needs vbdec on MSS builds)
|
||||||
//#define AUDIO_OAL_USE_SNDFILE // use libsndfile to decode WAVs instead of our internal decoder
|
//#define AUDIO_OAL_USE_SNDFILE // use libsndfile to decode WAVs instead of our internal decoder
|
||||||
#define AUDIO_OAL_USE_MPG123 // use mpg123 to support mp3 files
|
#define AUDIO_OAL_USE_MPG123 // use mpg123 to support mp3 files
|
||||||
|
#define MULTITHREADED_AUDIO
|
||||||
|
|
||||||
#ifdef AUDIO_OPUS
|
#ifdef AUDIO_OPUS
|
||||||
#define AUDIO_OAL_USE_OPUS // enable support of opus files
|
#define AUDIO_OAL_USE_OPUS // enable support of opus files
|
||||||
|
Loading…
Reference in New Issue
Block a user