2020-05-22 17:00:18 +02:00
# include "common.h"
2020-05-04 19:33:48 +02:00
# ifdef AUDIO_OAL
2020-05-07 08:26:16 +02:00
2021-01-14 22:15:36 +01:00
# if defined _MSC_VER && !defined CMAKE_NO_AUTOLINK
2021-01-06 14:46:59 +01:00
# ifdef AUDIO_OAL_USE_SNDFILE
2020-05-07 08:26:16 +02:00
# pragma comment( lib, "libsndfile-1.lib" )
2021-01-06 14:46:59 +01:00
# endif
2021-01-07 21:01:44 +01:00
# ifdef AUDIO_OAL_USE_MPG123
2020-08-03 16:04:00 +02:00
# pragma comment( lib, "libmpg123-0.lib" )
2020-05-11 04:55:57 +02:00
# endif
2021-01-07 21:01:44 +01:00
# endif
2021-01-06 14:46:59 +01:00
# ifdef AUDIO_OAL_USE_SNDFILE
2020-05-22 17:00:18 +02:00
# include <sndfile.h>
2021-01-06 14:46:59 +01:00
# endif
2021-01-07 21:01:44 +01:00
# ifdef AUDIO_OAL_USE_MPG123
2020-05-22 17:00:18 +02:00
# include <mpg123.h>
2020-05-22 16:40:19 +02:00
# endif
2021-01-07 21:01:44 +01:00
# ifdef AUDIO_OAL_USE_OPUS
# include <opusfile.h>
# endif
2020-05-07 08:26:16 +02:00
2021-06-25 04:06:38 +02:00
# 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
2021-06-27 13:42:52 +02:00
std : : queue < std : : pair < IDecoder * , void * > > gStreamsToClose ;
2021-06-25 04:06:38 +02:00
# else
# include "stream.h"
# endif
# include "sampman.h"
2020-12-06 20:26:40 +01:00
# ifndef _WIN32
# include "crossplatform.h"
# endif
2021-01-04 19:11:04 +01:00
/*
As we ran onto an issue of having different volume levels for mono streams
and stereo streams we are now handling all the stereo panning ourselves .
Each stream now has two sources - one panned to the left and one to the right ,
and uses two separate buffers to store data for each individual channel .
For that we also have to reshuffle all decoded PCM stereo data from LRLRLRLR to
LLLLRRRR ( handled by CSortStereoBuffer ) .
*/
class CSortStereoBuffer
{
uint16 * PcmBuf ;
size_t BufSize ;
2021-06-25 04:06:38 +02:00
//#ifdef MULTITHREADED_AUDIO
// std::mutex Mutex;
//#endif
2021-01-04 19:11:04 +01:00
public :
CSortStereoBuffer ( ) : PcmBuf ( nil ) , BufSize ( 0 ) { }
~ CSortStereoBuffer ( )
{
if ( PcmBuf )
free ( PcmBuf ) ;
}
uint16 * GetBuffer ( size_t size )
{
if ( size = = 0 ) return nil ;
if ( ! PcmBuf )
{
BufSize = size ;
PcmBuf = ( uint16 * ) malloc ( BufSize ) ;
}
else if ( BufSize < size )
{
BufSize = size ;
PcmBuf = ( uint16 * ) realloc ( PcmBuf , size ) ;
}
return PcmBuf ;
}
void SortStereo ( void * buf , size_t size )
{
2021-06-25 04:06:38 +02:00
//#ifdef MULTITHREADED_AUDIO
// std::lock_guard<std::mutex> lock(Mutex);
//#endif
2021-01-04 19:11:04 +01:00
uint16 * InBuf = ( uint16 * ) buf ;
uint16 * OutBuf = GetBuffer ( size ) ;
if ( ! OutBuf ) return ;
size_t rightStart = size / 4 ;
for ( size_t i = 0 ; i < size / 4 ; i + + )
{
OutBuf [ i ] = InBuf [ i * 2 ] ;
OutBuf [ i + rightStart ] = InBuf [ i * 2 + 1 ] ;
}
memcpy ( InBuf , OutBuf , size ) ;
}
} ;
CSortStereoBuffer SortStereoBuffer ;
2021-01-06 14:46:59 +01:00
class CImaADPCMDecoder
{
const uint16 StepTable [ 89 ] = {
7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 ,
16 , 17 , 19 , 21 , 23 , 25 , 28 , 31 ,
34 , 37 , 41 , 45 , 50 , 55 , 60 , 66 ,
73 , 80 , 88 , 97 , 107 , 118 , 130 , 143 ,
157 , 173 , 190 , 209 , 230 , 253 , 279 , 307 ,
337 , 371 , 408 , 449 , 494 , 544 , 598 , 658 ,
724 , 796 , 876 , 963 , 1060 , 1166 , 1282 , 1411 ,
1552 , 1707 , 1878 , 2066 , 2272 , 2499 , 2749 , 3024 ,
3327 , 3660 , 4026 , 4428 , 4871 , 5358 , 5894 , 6484 ,
7132 , 7845 , 8630 , 9493 , 10442 , 11487 , 12635 , 13899 ,
15289 , 16818 , 18500 , 20350 , 22385 , 24623 , 27086 , 29794 ,
32767
} ;
int16 Sample , StepIndex ;
public :
CImaADPCMDecoder ( )
{
Init ( 0 , 0 ) ;
}
void Init ( int16 _Sample , int16 _StepIndex )
{
Sample = _Sample ;
StepIndex = _StepIndex ;
}
void Decode ( uint8 * inbuf , int16 * _outbuf , size_t size )
{
int16 * outbuf = _outbuf ;
for ( size_t i = 0 ; i < size ; i + + )
{
* ( outbuf + + ) = DecodeSample ( inbuf [ i ] & 0xF ) ;
* ( outbuf + + ) = DecodeSample ( inbuf [ i ] > > 4 ) ;
}
}
int16 DecodeSample ( uint8 adpcm )
{
uint16 step = StepTable [ StepIndex ] ;
if ( adpcm & 4 )
StepIndex + = ( ( adpcm & 3 ) + 1 ) * 2 ;
else
StepIndex - - ;
2021-06-28 13:57:05 +02:00
StepIndex = Clamp ( StepIndex , 0 , 88 ) ;
2021-01-06 14:46:59 +01:00
int delta = step > > 3 ;
if ( adpcm & 1 ) delta + = step > > 2 ;
if ( adpcm & 2 ) delta + = step > > 1 ;
if ( adpcm & 4 ) delta + = step ;
if ( adpcm & 8 ) delta = - delta ;
int newSample = Sample + delta ;
2021-06-28 13:57:05 +02:00
Sample = Clamp ( newSample , - 32768 , 32767 ) ;
2021-01-06 14:46:59 +01:00
return Sample ;
}
} ;
class CWavFile : public IDecoder
{
enum
{
WAVEFMT_PCM = 1 ,
WAVEFMT_IMA_ADPCM = 0x11 ,
WAVEFMT_XBOX_ADPCM = 0x69 ,
} ;
struct tDataHeader
{
uint32 ID ;
uint32 Size ;
} ;
struct tFormatHeader
{
uint16 AudioFormat ;
uint16 NumChannels ;
uint32 SampleRate ;
uint32 ByteRate ;
uint16 BlockAlign ;
uint16 BitsPerSample ;
uint16 extra [ 2 ] ; // adpcm only
tFormatHeader ( ) { memset ( this , 0 , sizeof ( * this ) ) ; }
} ;
2021-01-06 19:22:09 +01:00
FILE * m_pFile ;
bool m_bIsOpen ;
2021-01-06 14:46:59 +01:00
2021-01-06 19:22:09 +01:00
tFormatHeader m_FormatHeader ;
uint32 m_DataStartOffset ; // TODO: 64 bit?
uint32 m_nSampleCount ;
uint32 m_nSamplesPerBlock ;
2021-01-06 14:46:59 +01:00
// ADPCM things
2021-01-06 19:22:09 +01:00
uint8 * m_pAdpcmBuffer ;
int16 * * m_ppPcmBuffers ;
CImaADPCMDecoder * m_pAdpcmDecoders ;
2021-01-06 14:46:59 +01:00
void Close ( )
{
2021-01-06 19:22:09 +01:00
if ( m_pFile ) {
fclose ( m_pFile ) ;
m_pFile = nil ;
2021-01-06 14:46:59 +01:00
}
2021-01-06 19:22:09 +01:00
delete [ ] m_pAdpcmBuffer ;
delete [ ] m_ppPcmBuffers ;
delete [ ] m_pAdpcmDecoders ;
}
uint32 GetCurrentSample ( ) const
{
// TODO: 64 bit?
uint32 FilePos = ftell ( m_pFile ) ;
if ( FilePos < = m_DataStartOffset )
return 0 ;
return ( FilePos - m_DataStartOffset ) / m_FormatHeader . BlockAlign * m_nSamplesPerBlock ;
2021-01-06 14:46:59 +01:00
}
public :
2021-01-06 19:22:09 +01:00
CWavFile ( const char * path ) : m_bIsOpen ( false ) , m_DataStartOffset ( 0 ) , m_nSampleCount ( 0 ) , m_nSamplesPerBlock ( 0 ) , m_pAdpcmBuffer ( nil ) , m_ppPcmBuffers ( nil ) , m_pAdpcmDecoders ( nil )
2021-01-06 14:46:59 +01:00
{
2021-01-06 19:22:09 +01:00
m_pFile = fopen ( path , " rb " ) ;
if ( ! m_pFile ) return ;
2021-01-06 14:46:59 +01:00
# define CLOSE_ON_ERROR(op)\
if ( op ) { \
Close ( ) ; \
return ; \
}
tDataHeader DataHeader ;
2021-01-06 19:22:09 +01:00
CLOSE_ON_ERROR ( fread ( & DataHeader , sizeof ( DataHeader ) , 1 , m_pFile ) = = 0 ) ;
2021-01-06 14:46:59 +01:00
CLOSE_ON_ERROR ( DataHeader . ID ! = ' FFIR ' ) ;
2021-01-06 19:22:09 +01:00
// TODO? validate filesizes
2021-01-06 14:46:59 +01:00
int WAVE ;
2021-01-06 19:22:09 +01:00
CLOSE_ON_ERROR ( fread ( & WAVE , 4 , 1 , m_pFile ) = = 0 ) ;
2021-01-06 14:46:59 +01:00
CLOSE_ON_ERROR ( WAVE ! = ' EVAW ' )
2021-01-06 19:22:09 +01:00
CLOSE_ON_ERROR ( fread ( & DataHeader , sizeof ( DataHeader ) , 1 , m_pFile ) = = 0 ) ;
2021-01-06 14:46:59 +01:00
CLOSE_ON_ERROR ( DataHeader . ID ! = ' tmf ' ) ;
2021-01-06 19:22:09 +01:00
CLOSE_ON_ERROR ( fread ( & m_FormatHeader , Min ( DataHeader . Size , sizeof ( tFormatHeader ) ) , 1 , m_pFile ) = = 0 ) ;
2021-01-06 14:46:59 +01:00
CLOSE_ON_ERROR ( DataHeader . Size > sizeof ( tFormatHeader ) ) ;
2021-01-06 19:22:09 +01:00
switch ( m_FormatHeader . AudioFormat )
2021-01-06 14:46:59 +01:00
{
case WAVEFMT_XBOX_ADPCM :
2021-01-06 19:22:09 +01:00
m_FormatHeader . AudioFormat = WAVEFMT_IMA_ADPCM ;
2021-01-06 14:46:59 +01:00
case WAVEFMT_IMA_ADPCM :
2021-01-06 19:22:09 +01:00
m_nSamplesPerBlock = ( m_FormatHeader . BlockAlign / m_FormatHeader . NumChannels - 4 ) * 2 + 1 ;
m_pAdpcmBuffer = new uint8 [ m_FormatHeader . BlockAlign ] ;
m_ppPcmBuffers = new int16 * [ m_FormatHeader . NumChannels ] ;
m_pAdpcmDecoders = new CImaADPCMDecoder [ m_FormatHeader . NumChannels ] ;
2021-01-06 14:46:59 +01:00
break ;
case WAVEFMT_PCM :
2021-01-06 19:22:09 +01:00
m_nSamplesPerBlock = 1 ;
if ( m_FormatHeader . BitsPerSample ! = 16 )
2021-01-06 14:46:59 +01:00
{
2021-01-06 19:22:09 +01:00
debug ( " Unsupported PCM (%d bits), only signed 16-bit is supported (%s) \n " , m_FormatHeader . BitsPerSample , path ) ;
Close ( ) ;
2021-01-06 14:46:59 +01:00
return ;
}
break ;
default :
2021-01-06 19:22:09 +01:00
debug ( " Unsupported wav format 0x%x (%s) \n " , m_FormatHeader . AudioFormat , path ) ;
Close ( ) ;
2021-01-06 14:46:59 +01:00
return ;
}
while ( true ) {
2021-01-06 19:22:09 +01:00
CLOSE_ON_ERROR ( fread ( & DataHeader , sizeof ( DataHeader ) , 1 , m_pFile ) = = 0 ) ;
2021-01-06 14:46:59 +01:00
if ( DataHeader . ID = = ' atad ' )
break ;
2021-01-06 19:22:09 +01:00
fseek ( m_pFile , DataHeader . Size , SEEK_CUR ) ;
// TODO? validate data size
// maybe check if there no extreme custom headers that might break this
2021-01-06 14:46:59 +01:00
}
2021-01-06 19:22:09 +01:00
m_DataStartOffset = ftell ( m_pFile ) ;
m_nSampleCount = DataHeader . Size / m_FormatHeader . BlockAlign * m_nSamplesPerBlock ;
2021-01-06 14:46:59 +01:00
2021-01-06 19:22:09 +01:00
m_bIsOpen = true ;
2021-01-06 14:46:59 +01:00
# undef CLOSE_ON_ERROR
}
2021-06-25 04:06:38 +02:00
void FileOpen ( )
{
}
2021-01-06 14:46:59 +01:00
~ CWavFile ( )
{
Close ( ) ;
}
bool IsOpened ( )
{
2021-01-06 19:22:09 +01:00
return m_bIsOpen ;
2021-01-06 14:46:59 +01:00
}
2021-06-25 04:06:38 +02:00
2021-01-06 14:46:59 +01:00
uint32 GetSampleSize ( )
{
return sizeof ( uint16 ) ;
}
uint32 GetSampleCount ( )
{
2021-01-06 19:22:09 +01:00
return m_nSampleCount ;
2021-01-06 14:46:59 +01:00
}
uint32 GetSampleRate ( )
{
2021-01-06 19:22:09 +01:00
return m_FormatHeader . SampleRate ;
2021-01-06 14:46:59 +01:00
}
uint32 GetChannels ( )
{
2021-01-06 19:22:09 +01:00
return m_FormatHeader . NumChannels ;
2021-01-06 14:46:59 +01:00
}
void Seek ( uint32 milliseconds )
{
if ( ! IsOpened ( ) ) return ;
2021-01-06 19:22:09 +01:00
fseek ( m_pFile , m_DataStartOffset + ms2samples ( milliseconds ) / m_nSamplesPerBlock * m_FormatHeader . BlockAlign , SEEK_SET ) ;
2021-01-06 14:46:59 +01:00
}
uint32 Tell ( )
{
if ( ! IsOpened ( ) ) return 0 ;
2021-01-06 19:22:09 +01:00
return samples2ms ( GetCurrentSample ( ) ) ;
2021-01-06 14:46:59 +01:00
}
# define SAMPLES_IN_LINE (8)
uint32 Decode ( void * buffer )
{
if ( ! IsOpened ( ) ) return 0 ;
2021-01-06 19:22:09 +01:00
if ( m_FormatHeader . AudioFormat = = WAVEFMT_PCM )
2021-01-06 14:46:59 +01:00
{
2021-01-06 19:22:09 +01:00
// just read the file and sort the samples
uint32 size = fread ( buffer , 1 , GetBufferSize ( ) , m_pFile ) ;
if ( m_FormatHeader . NumChannels = = 2 )
2021-01-06 14:46:59 +01:00
SortStereoBuffer . SortStereo ( buffer , size ) ;
return size ;
}
2021-01-06 19:22:09 +01:00
else if ( m_FormatHeader . AudioFormat = = WAVEFMT_IMA_ADPCM )
2021-01-06 14:46:59 +01:00
{
2021-01-06 19:22:09 +01:00
// trim the buffer size if we're at the end of our file
uint32 nMaxSamples = GetBufferSamples ( ) / m_FormatHeader . NumChannels ;
uint32 nSamplesLeft = m_nSampleCount - GetCurrentSample ( ) ;
nMaxSamples = Min ( nMaxSamples , nSamplesLeft ) ;
// align sample count to our block
nMaxSamples = nMaxSamples / m_nSamplesPerBlock * m_nSamplesPerBlock ;
// count the size of output buffer
uint32 OutBufSizePerChannel = nMaxSamples * GetSampleSize ( ) ;
uint32 OutBufSize = OutBufSizePerChannel * m_FormatHeader . NumChannels ;
// calculate the pointers to individual channel buffers
for ( uint32 i = 0 ; i < m_FormatHeader . NumChannels ; i + + )
m_ppPcmBuffers [ i ] = ( int16 * ) ( ( int8 * ) buffer + OutBufSizePerChannel * i ) ;
2021-01-06 14:46:59 +01:00
uint32 samplesRead = 0 ;
2021-01-06 19:22:09 +01:00
while ( samplesRead < nMaxSamples )
2021-01-06 14:46:59 +01:00
{
2021-01-06 19:22:09 +01:00
// read the file
uint8 * pAdpcmBuf = m_pAdpcmBuffer ;
if ( fread ( m_pAdpcmBuffer , 1 , m_FormatHeader . BlockAlign , m_pFile ) = = 0 )
2021-01-06 14:46:59 +01:00
return 0 ;
2021-01-06 19:22:09 +01:00
// get the first sample in adpcm block and initialise the decoder(s)
for ( uint32 i = 0 ; i < m_FormatHeader . NumChannels ; i + + )
2021-01-06 14:46:59 +01:00
{
2021-01-06 19:22:09 +01:00
int16 Sample = * ( int16 * ) pAdpcmBuf ;
pAdpcmBuf + = sizeof ( int16 ) ;
int16 Step = * ( int16 * ) pAdpcmBuf ;
pAdpcmBuf + = sizeof ( int16 ) ;
m_pAdpcmDecoders [ i ] . Init ( Sample , Step ) ;
* ( m_ppPcmBuffers [ i ] ) = Sample ;
m_ppPcmBuffers [ i ] + + ;
2021-01-06 14:46:59 +01:00
}
samplesRead + + ;
2021-01-06 19:22:09 +01:00
// decode the rest of the block
for ( uint32 s = 1 ; s < m_nSamplesPerBlock ; s + = SAMPLES_IN_LINE )
2021-01-06 14:46:59 +01:00
{
2021-01-06 19:22:09 +01:00
for ( uint32 i = 0 ; i < m_FormatHeader . NumChannels ; i + + )
2021-01-06 14:46:59 +01:00
{
2021-01-06 19:22:09 +01:00
m_pAdpcmDecoders [ i ] . Decode ( pAdpcmBuf , m_ppPcmBuffers [ i ] , SAMPLES_IN_LINE / 2 ) ;
pAdpcmBuf + = SAMPLES_IN_LINE / 2 ;
m_ppPcmBuffers [ i ] + = SAMPLES_IN_LINE ;
2021-01-06 14:46:59 +01:00
}
samplesRead + = SAMPLES_IN_LINE ;
}
}
return OutBufSize ;
}
return 0 ;
}
} ;
# ifdef AUDIO_OAL_USE_SNDFILE
2020-05-07 08:26:16 +02:00
class CSndFile : public IDecoder
{
SNDFILE * m_pfSound ;
SF_INFO m_soundInfo ;
public :
CSndFile ( const char * path ) :
m_pfSound ( nil )
{
memset ( & m_soundInfo , 0 , sizeof ( m_soundInfo ) ) ;
m_pfSound = sf_open ( path , SFM_READ , & m_soundInfo ) ;
}
2021-06-25 04:06:38 +02:00
void FileOpen ( )
{
}
2020-05-07 08:26:16 +02:00
~ CSndFile ( )
{
if ( m_pfSound )
{
sf_close ( m_pfSound ) ;
m_pfSound = nil ;
}
}
bool IsOpened ( )
{
return m_pfSound ! = nil ;
}
uint32 GetSampleSize ( )
{
return sizeof ( uint16 ) ;
}
uint32 GetSampleCount ( )
{
return m_soundInfo . frames ;
}
uint32 GetSampleRate ( )
{
return m_soundInfo . samplerate ;
}
uint32 GetChannels ( )
{
return m_soundInfo . channels ;
}
void Seek ( uint32 milliseconds )
{
if ( ! IsOpened ( ) ) return ;
sf_seek ( m_pfSound , ms2samples ( milliseconds ) , SF_SEEK_SET ) ;
}
uint32 Tell ( )
{
if ( ! IsOpened ( ) ) return 0 ;
return samples2ms ( sf_seek ( m_pfSound , 0 , SF_SEEK_CUR ) ) ;
}
uint32 Decode ( void * buffer )
{
if ( ! IsOpened ( ) ) return 0 ;
2021-01-04 19:11:04 +01:00
size_t size = sf_read_short ( m_pfSound , ( short * ) buffer , GetBufferSamples ( ) ) * GetSampleSize ( ) ;
if ( GetChannels ( ) = = 2 )
SortStereoBuffer . SortStereo ( buffer , size ) ;
return size ;
2020-05-07 08:26:16 +02:00
}
} ;
2021-01-06 14:46:59 +01:00
# endif
2020-05-07 08:26:16 +02:00
2021-01-07 21:01:44 +01:00
# ifdef AUDIO_OAL_USE_MPG123
2021-01-05 20:06:17 +01:00
2020-05-07 08:26:16 +02:00
class CMP3File : public IDecoder
{
2020-05-27 06:52:45 +02:00
protected :
2020-05-07 08:26:16 +02:00
mpg123_handle * m_pMH ;
bool m_bOpened ;
uint32 m_nRate ;
uint32 m_nChannels ;
2021-06-25 04:06:38 +02:00
const char * m_pPath ;
bool m_bFileNotOpenedYet ;
2020-05-27 06:52:45 +02:00
CMP3File ( ) :
m_pMH ( nil ) ,
m_bOpened ( false ) ,
m_nRate ( 0 ) ,
2021-06-25 04:06:38 +02:00
m_bFileNotOpenedYet ( false ) ,
2020-05-27 06:52:45 +02:00
m_nChannels ( 0 ) { }
2020-05-07 08:26:16 +02:00
public :
CMP3File ( const char * path ) :
m_pMH ( nil ) ,
m_bOpened ( false ) ,
m_nRate ( 0 ) ,
2021-06-25 04:06:38 +02:00
m_nChannels ( 0 ) ,
m_pPath ( path ) ,
m_bFileNotOpenedYet ( false )
2020-05-07 08:26:16 +02:00
{
m_pMH = mpg123_new ( nil , nil ) ;
if ( m_pMH )
{
2021-02-17 13:59:05 +01:00
mpg123_param ( m_pMH , MPG123_FLAGS , MPG123_SEEKBUFFER | MPG123_GAPLESS , 0.0 ) ;
2021-02-02 14:39:08 +01:00
2021-06-25 04:06:38 +02:00
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
2020-05-07 08:26:16 +02:00
}
}
2021-06-25 04:06:38 +02:00
void FileOpen ( )
{
if ( ! m_bFileNotOpenedYet ) return ;
long rate = 0 ;
int channels = 0 ;
int encoding = 0 ;
m_bOpened = mpg123_open ( m_pMH , m_pPath ) = = MPG123_OK
& & mpg123_getformat ( m_pMH , & rate , & channels , & encoding ) = = MPG123_OK ;
m_nRate = rate ;
m_nChannels = channels ;
if ( IsOpened ( ) ) {
mpg123_format_none ( m_pMH ) ;
mpg123_format ( m_pMH , rate , channels , encoding ) ;
}
m_bFileNotOpenedYet = false ;
}
2020-05-07 08:26:16 +02:00
~ CMP3File ( )
{
if ( m_pMH )
{
mpg123_close ( m_pMH ) ;
mpg123_delete ( m_pMH ) ;
m_pMH = nil ;
}
}
bool IsOpened ( )
{
return m_bOpened ;
}
uint32 GetSampleSize ( )
{
return sizeof ( uint16 ) ;
}
uint32 GetSampleCount ( )
{
2021-06-25 04:06:38 +02:00
if ( ! IsOpened ( ) | | m_bFileNotOpenedYet ) return 0 ;
2020-05-07 08:26:16 +02:00
return mpg123_length ( m_pMH ) ;
}
uint32 GetSampleRate ( )
{
return m_nRate ;
}
uint32 GetChannels ( )
{
return m_nChannels ;
}
void Seek ( uint32 milliseconds )
{
2021-06-25 04:06:38 +02:00
if ( ! IsOpened ( ) | | m_bFileNotOpenedYet ) return ;
2020-10-05 12:59:40 +02:00
mpg123_seek ( m_pMH , ms2samples ( milliseconds ) , SEEK_SET ) ;
2020-05-07 08:26:16 +02:00
}
uint32 Tell ( )
{
2021-06-25 04:06:38 +02:00
if ( ! IsOpened ( ) | | m_bFileNotOpenedYet ) return 0 ;
2020-10-05 12:59:40 +02:00
return samples2ms ( mpg123_tell ( m_pMH ) ) ;
2020-05-07 08:26:16 +02:00
}
uint32 Decode ( void * buffer )
{
2021-06-25 04:06:38 +02:00
if ( ! IsOpened ( ) | | m_bFileNotOpenedYet ) return 0 ;
2020-05-07 08:26:16 +02:00
size_t size ;
int err = mpg123_read ( m_pMH , ( unsigned char * ) buffer , GetBufferSize ( ) , & size ) ;
2020-07-22 13:56:28 +02:00
# if defined(__LP64__) || defined(_WIN64)
assert ( " We can't handle audio files more then 2 GB yet :shrug: " & & ( size < UINT32_MAX ) ) ;
# endif
2020-05-07 08:26:16 +02:00
if ( err ! = MPG123_OK & & err ! = MPG123_DONE ) return 0 ;
2021-01-04 19:11:04 +01:00
if ( GetChannels ( ) = = 2 )
SortStereoBuffer . SortStereo ( buffer , size ) ;
2020-07-22 13:56:28 +02:00
return ( uint32 ) size ;
2020-05-07 08:26:16 +02:00
}
} ;
2021-01-05 20:06:17 +01:00
2021-01-07 21:01:44 +01:00
class CADFFile : public CMP3File
{
static ssize_t r_read ( void * fh , void * buf , size_t size )
{
size_t bytesRead = fread ( buf , 1 , size , ( FILE * ) fh ) ;
uint8 * _buf = ( uint8 * ) buf ;
for ( size_t i = 0 ; i < size ; i + + )
_buf [ i ] ^ = 0x22 ;
return bytesRead ;
}
static off_t r_seek ( void * fh , off_t pos , int seekType )
{
fseek ( ( FILE * ) fh , pos , seekType ) ;
return ftell ( ( FILE * ) fh ) ;
}
static void r_close ( void * fh )
{
fclose ( ( FILE * ) fh ) ;
}
public :
CADFFile ( const char * path )
{
m_pMH = mpg123_new ( nil , nil ) ;
if ( m_pMH )
{
2021-02-17 13:59:05 +01:00
mpg123_param ( m_pMH , MPG123_FLAGS , MPG123_SEEKBUFFER | MPG123_GAPLESS , 0.0 ) ;
2021-06-25 04:06:38 +02:00
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 ( ) ;
2021-01-07 21:01:44 +01:00
# endif
2021-06-25 04:06:38 +02:00
}
}
2021-01-07 21:01:44 +01:00
2021-06-25 04:06:38 +02:00
void FileOpen ( )
{
if ( ! m_bFileNotOpenedYet ) return ;
2021-01-07 21:01:44 +01:00
2021-06-25 04:06:38 +02:00
long rate = 0 ;
int channels = 0 ;
int encoding = 0 ;
FILE * f = fopen ( m_pPath , " rb " ) ;
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 ;
m_nRate = rate ;
m_nChannels = channels ;
if ( IsOpened ( ) ) {
mpg123_format_none ( m_pMH ) ;
mpg123_format ( m_pMH , rate , channels , encoding ) ;
2021-01-07 21:01:44 +01:00
}
2021-06-25 04:06:38 +02:00
m_bFileNotOpenedYet = false ;
2021-01-07 21:01:44 +01:00
}
} ;
# endif
2021-01-05 20:06:17 +01:00
# define VAG_LINE_SIZE (0x10)
# define VAG_SAMPLES_IN_LINE (28)
class CVagDecoder
{
const double f [ 5 ] [ 2 ] = { { 0.0 , 0.0 } ,
{ 60.0 / 64.0 , 0.0 } ,
{ 115.0 / 64.0 , - 52.0 / 64.0 } ,
{ 98.0 / 64.0 , - 55.0 / 64.0 } ,
{ 122.0 / 64.0 , - 60.0 / 64.0 } } ;
double s_1 ;
double s_2 ;
public :
CVagDecoder ( )
{
ResetState ( ) ;
}
void ResetState ( )
{
s_1 = s_2 = 0.0 ;
}
static short quantize ( double sample )
{
int a = int ( sample + 0.5 ) ;
2021-06-28 13:57:05 +02:00
return short ( Clamp ( a , - 32768 , 32767 ) ) ;
2021-01-05 20:06:17 +01:00
}
void Decode ( void * _inbuf , int16 * _outbuf , size_t size )
{
uint8 * inbuf = ( uint8 * ) _inbuf ;
int16 * outbuf = _outbuf ;
size & = ~ ( VAG_LINE_SIZE - 1 ) ;
while ( size > 0 ) {
double samples [ VAG_SAMPLES_IN_LINE ] ;
int predict_nr , shift_factor , flags ;
predict_nr = * ( inbuf + + ) ;
shift_factor = predict_nr & 0xf ;
predict_nr > > = 4 ;
flags = * ( inbuf + + ) ;
if ( flags = = 7 ) // TODO: ignore?
break ;
for ( int i = 0 ; i < VAG_SAMPLES_IN_LINE ; i + = 2 ) {
int d = * ( inbuf + + ) ;
int16 s = int16 ( ( d & 0xf ) < < 12 ) ;
samples [ i ] = ( double ) ( s > > shift_factor ) ;
s = int16 ( ( d & 0xf0 ) < < 8 ) ;
samples [ i + 1 ] = ( double ) ( s > > shift_factor ) ;
}
for ( int i = 0 ; i < VAG_SAMPLES_IN_LINE ; i + + ) {
samples [ i ] = samples [ i ] + s_1 * f [ predict_nr ] [ 0 ] + s_2 * f [ predict_nr ] [ 1 ] ;
s_2 = s_1 ;
s_1 = samples [ i ] ;
* ( outbuf + + ) = quantize ( samples [ i ] + 0.5 ) ;
}
size - = VAG_LINE_SIZE ;
}
}
} ;
# define VB_BLOCK_SIZE (0x2000)
# define NUM_VAG_LINES_IN_BLOCK (VB_BLOCK_SIZE / VAG_LINE_SIZE)
# define NUM_VAG_SAMPLES_IN_BLOCK (NUM_VAG_LINES_IN_BLOCK * VAG_SAMPLES_IN_LINE)
class CVbFile : public IDecoder
{
2021-01-06 19:22:09 +01:00
FILE * m_pFile ;
CVagDecoder * m_pVagDecoders ;
size_t m_FileSize ;
size_t m_nNumberOfBlocks ;
2021-01-05 20:06:17 +01:00
2021-01-06 19:22:09 +01:00
uint32 m_nSampleRate ;
uint8 m_nChannels ;
bool m_bBlockRead ;
uint16 m_LineInBlock ;
size_t m_CurrentBlock ;
2021-01-05 20:06:17 +01:00
2021-01-06 19:22:09 +01:00
uint8 * * m_ppVagBuffers ; // buffers that cache actual ADPCM file data
int16 * * m_ppPcmBuffers ;
2021-01-05 20:06:17 +01:00
void ReadBlock ( int32 block = - 1 )
{
// just read next block if -1
if ( block ! = - 1 )
2021-01-06 19:22:09 +01:00
fseek ( m_pFile , block * m_nChannels * VB_BLOCK_SIZE , SEEK_SET ) ;
2021-01-05 20:06:17 +01:00
for ( int i = 0 ; i < m_nChannels ; i + + )
2021-01-06 19:22:09 +01:00
fread ( m_ppVagBuffers [ i ] , VB_BLOCK_SIZE , 1 , m_pFile ) ;
2021-01-05 20:06:17 +01:00
m_bBlockRead = true ;
}
public :
2021-01-06 19:22:09 +01:00
CVbFile ( const char * path , uint32 nSampleRate = 32000 , uint8 nChannels = 2 ) : m_nSampleRate ( nSampleRate ) , m_nChannels ( nChannels ) , m_pVagDecoders ( nil ) , m_ppVagBuffers ( nil ) , m_ppPcmBuffers ( nil ) ,
2021-01-06 14:46:59 +01:00
m_FileSize ( 0 ) , m_nNumberOfBlocks ( 0 ) , m_bBlockRead ( false ) , m_LineInBlock ( 0 ) , m_CurrentBlock ( 0 )
2021-01-05 20:06:17 +01:00
{
2021-01-06 19:22:09 +01:00
m_pFile = fopen ( path , " rb " ) ;
if ( ! m_pFile ) return ;
fseek ( m_pFile , 0 , SEEK_END ) ;
m_FileSize = ftell ( m_pFile ) ;
fseek ( m_pFile , 0 , SEEK_SET ) ;
2021-01-06 14:46:59 +01:00
m_nNumberOfBlocks = m_FileSize / ( nChannels * VB_BLOCK_SIZE ) ;
2021-01-06 19:22:09 +01:00
m_pVagDecoders = new CVagDecoder [ nChannels ] ;
m_ppVagBuffers = new uint8 * [ nChannels ] ;
m_ppPcmBuffers = new int16 * [ nChannels ] ;
2021-01-06 14:46:59 +01:00
for ( uint8 i = 0 ; i < nChannels ; i + + )
2021-01-06 19:22:09 +01:00
m_ppVagBuffers [ i ] = new uint8 [ VB_BLOCK_SIZE ] ;
2021-01-05 20:06:17 +01:00
}
2021-06-25 04:06:38 +02:00
void FileOpen ( )
{
}
2021-01-05 20:06:17 +01:00
~ CVbFile ( )
{
2021-01-06 19:22:09 +01:00
if ( m_pFile )
2021-01-05 20:06:17 +01:00
{
2021-01-06 19:22:09 +01:00
fclose ( m_pFile ) ;
delete [ ] m_pVagDecoders ;
2021-01-05 20:06:17 +01:00
for ( int i = 0 ; i < m_nChannels ; i + + )
2021-01-06 19:22:09 +01:00
delete [ ] m_ppVagBuffers [ i ] ;
delete [ ] m_ppVagBuffers ;
delete [ ] m_ppPcmBuffers ;
2021-01-05 20:06:17 +01:00
}
}
bool IsOpened ( )
{
2021-01-06 19:22:09 +01:00
return m_pFile ! = nil ;
2021-01-05 20:06:17 +01:00
}
uint32 GetSampleSize ( )
{
return sizeof ( uint16 ) ;
}
uint32 GetSampleCount ( )
{
if ( ! IsOpened ( ) ) return 0 ;
return m_nNumberOfBlocks * NUM_VAG_LINES_IN_BLOCK * VAG_SAMPLES_IN_LINE ;
}
uint32 GetSampleRate ( )
{
return m_nSampleRate ;
}
uint32 GetChannels ( )
{
return m_nChannels ;
}
void Seek ( uint32 milliseconds )
{
if ( ! IsOpened ( ) ) return ;
uint32 samples = ms2samples ( milliseconds ) ;
2021-01-06 19:22:09 +01:00
// find the block of our sample
2021-01-06 14:46:59 +01:00
uint32 block = samples / NUM_VAG_SAMPLES_IN_BLOCK ;
2021-01-05 20:06:17 +01:00
if ( block > m_nNumberOfBlocks )
{
samples = 0 ;
block = 0 ;
}
if ( block ! = m_CurrentBlock )
2021-01-06 14:46:59 +01:00
m_bBlockRead = false ;
2021-01-05 20:06:17 +01:00
2021-01-06 19:22:09 +01:00
// find a line of our sample within our block
2021-01-05 20:06:17 +01:00
uint32 remainingSamples = samples - block * NUM_VAG_SAMPLES_IN_BLOCK ;
uint32 newLine = remainingSamples / VAG_SAMPLES_IN_LINE / VAG_LINE_SIZE ;
if ( m_CurrentBlock ! = block | | m_LineInBlock ! = newLine )
{
m_CurrentBlock = block ;
m_LineInBlock = newLine ;
2021-01-06 14:46:59 +01:00
for ( uint32 i = 0 ; i < GetChannels ( ) ; i + + )
2021-01-06 19:22:09 +01:00
m_pVagDecoders [ i ] . ResetState ( ) ;
2021-01-05 20:06:17 +01:00
}
}
uint32 Tell ( )
{
if ( ! IsOpened ( ) ) return 0 ;
uint32 pos = ( m_CurrentBlock * NUM_VAG_LINES_IN_BLOCK + m_LineInBlock ) * VAG_SAMPLES_IN_LINE ;
return samples2ms ( pos ) ;
}
uint32 Decode ( void * buffer )
{
if ( ! IsOpened ( ) ) return 0 ;
2021-01-06 19:22:09 +01:00
if ( m_CurrentBlock > = m_nNumberOfBlocks ) return 0 ;
// cache current ADPCM block
2021-01-05 20:06:17 +01:00
if ( ! m_bBlockRead )
ReadBlock ( m_CurrentBlock ) ;
2021-01-06 19:22:09 +01:00
// trim the buffer size if we're at the end of our file
2021-01-06 14:46:59 +01:00
int numberOfRequiredLines = GetBufferSamples ( ) / m_nChannels / VAG_SAMPLES_IN_LINE ;
2021-01-05 20:06:17 +01:00
int numberOfRemainingLines = ( m_nNumberOfBlocks - m_CurrentBlock ) * NUM_VAG_LINES_IN_BLOCK - m_LineInBlock ;
int bufSizePerChannel = Min ( numberOfRequiredLines , numberOfRemainingLines ) * VAG_SAMPLES_IN_LINE * GetSampleSize ( ) ;
2021-01-06 19:22:09 +01:00
// calculate the pointers to individual channel buffers
2021-01-06 14:46:59 +01:00
for ( uint32 i = 0 ; i < m_nChannels ; i + + )
2021-01-06 19:22:09 +01:00
m_ppPcmBuffers [ i ] = ( int16 * ) ( ( int8 * ) buffer + bufSizePerChannel * i ) ;
2021-01-05 20:06:17 +01:00
2021-01-06 19:22:09 +01:00
int size = 0 ;
2021-01-05 20:06:17 +01:00
while ( size < bufSizePerChannel )
{
2021-01-06 19:22:09 +01:00
// decode the VAG lines
2021-01-06 14:46:59 +01:00
for ( uint32 i = 0 ; i < m_nChannels ; i + + )
2021-01-05 20:06:17 +01:00
{
2021-01-06 19:22:09 +01:00
m_pVagDecoders [ i ] . Decode ( m_ppVagBuffers [ i ] + m_LineInBlock * VAG_LINE_SIZE , m_ppPcmBuffers [ i ] , VAG_LINE_SIZE ) ;
m_ppPcmBuffers [ i ] + = VAG_SAMPLES_IN_LINE ;
2021-01-05 20:06:17 +01:00
}
size + = VAG_SAMPLES_IN_LINE * GetSampleSize ( ) ;
m_LineInBlock + + ;
2021-01-06 19:22:09 +01:00
// block is over, read the next block
2021-01-05 20:06:17 +01:00
if ( m_LineInBlock > = NUM_VAG_LINES_IN_BLOCK )
{
m_CurrentBlock + + ;
2021-01-06 19:22:09 +01:00
if ( m_CurrentBlock > = m_nNumberOfBlocks ) // end of file
2021-01-05 20:06:17 +01:00
break ;
m_LineInBlock = 0 ;
ReadBlock ( ) ;
}
}
2021-01-06 14:46:59 +01:00
return bufSizePerChannel * m_nChannels ;
2021-01-05 20:06:17 +01:00
}
} ;
2021-01-07 21:01:44 +01:00
# ifdef AUDIO_OAL_USE_OPUS
2020-05-22 16:40:19 +02:00
class COpusFile : public IDecoder
{
OggOpusFile * m_FileH ;
bool m_bOpened ;
uint32 m_nRate ;
uint32 m_nChannels ;
public :
2020-05-24 17:43:50 +02:00
COpusFile ( const char * path ) : m_FileH ( nil ) ,
2020-05-22 16:40:19 +02:00
m_bOpened ( false ) ,
m_nRate ( 0 ) ,
m_nChannels ( 0 )
{
int ret ;
m_FileH = op_open_file ( path , & ret ) ;
if ( m_FileH ) {
m_nChannels = op_head ( m_FileH , 0 ) - > channel_count ;
2020-10-24 15:37:14 +02:00
m_nRate = 48000 ;
2020-05-24 17:43:50 +02:00
const OpusTags * tags = op_tags ( m_FileH , 0 ) ;
2020-05-22 16:40:19 +02:00
for ( int i = 0 ; i < tags - > comments ; i + + ) {
if ( strncmp ( tags - > user_comments [ i ] , " SAMPLERATE " , sizeof ( " SAMPLERATE " ) - 1 ) = = 0 )
{
sscanf ( tags - > user_comments [ i ] , " SAMPLERATE=%i " , & m_nRate ) ;
break ;
}
}
m_bOpened = true ;
}
}
2021-06-25 04:06:38 +02:00
void FileOpen ( )
{
}
2020-05-22 16:40:19 +02:00
~ COpusFile ( )
{
if ( m_FileH )
{
op_free ( m_FileH ) ;
m_FileH = nil ;
}
}
bool IsOpened ( )
{
return m_bOpened ;
}
uint32 GetSampleSize ( )
{
return sizeof ( uint16 ) ;
}
uint32 GetSampleCount ( )
{
if ( ! IsOpened ( ) ) return 0 ;
return op_pcm_total ( m_FileH , 0 ) ;
}
uint32 GetSampleRate ( )
{
return m_nRate ;
}
uint32 GetChannels ( )
{
return m_nChannels ;
}
void Seek ( uint32 milliseconds )
{
if ( ! IsOpened ( ) ) return ;
2020-10-05 12:59:40 +02:00
op_pcm_seek ( m_FileH , ms2samples ( milliseconds ) / GetChannels ( ) ) ;
2020-05-22 16:40:19 +02:00
}
uint32 Tell ( )
{
if ( ! IsOpened ( ) ) return 0 ;
2020-10-05 12:59:40 +02:00
return samples2ms ( op_pcm_tell ( m_FileH ) * GetChannels ( ) ) ;
2020-05-22 16:40:19 +02:00
}
uint32 Decode ( void * buffer )
{
if ( ! IsOpened ( ) ) return 0 ;
int size = op_read ( m_FileH , ( opus_int16 * ) buffer , GetBufferSamples ( ) , NULL ) ;
if ( size < 0 )
return 0 ;
2021-01-04 19:11:04 +01:00
if ( GetChannels ( ) = = 2 )
SortStereoBuffer . SortStereo ( buffer , size * m_nChannels * GetSampleSize ( ) ) ;
2020-05-22 16:40:19 +02:00
return size * m_nChannels * GetSampleSize ( ) ;
}
} ;
# endif
2020-05-04 19:33:48 +02:00
2021-06-25 04:06:38 +02:00
// 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 ( ) ;
2021-06-27 13:42:52 +02:00
if ( close )
gStreamsToClose . push ( std : : pair < IDecoder * , void * > ( m_pSoundFile ? m_pSoundFile : nil , m_pBuffer ? m_pBuffer : nil ) ) ;
else
gStreamsToProcess . push ( this ) ;
2021-06-25 04:06:38 +02:00
gAudioThreadQueueMutex . unlock ( ) ;
gAudioThreadCv . notify_one ( ) ;
}
void audioFileOpsThread ( )
{
do
{
CStream * stream ;
{
// Just a semaphore
std : : unique_lock < std : : mutex > queueMutex ( gAudioThreadQueueMutex ) ;
2021-06-27 13:42:52 +02:00
gAudioThreadCv . wait ( queueMutex , [ ] { return gStreamsToProcess . size ( ) > 0 | | gStreamsToClose . size ( ) > 0 | | gAudioThreadTerm ; } ) ;
2021-06-25 04:06:38 +02:00
if ( gAudioThreadTerm )
return ;
2021-06-27 13:42:52 +02:00
if ( ! gStreamsToClose . empty ( ) ) {
auto streamToClose = gStreamsToClose . front ( ) ;
gStreamsToClose . pop ( ) ;
if ( streamToClose . first ) { // pSoundFile
delete streamToClose . first ;
}
if ( streamToClose . second ) { // pBuffer
free ( streamToClose . second ) ;
}
}
2021-06-25 04:06:38 +02:00
if ( ! gStreamsToProcess . empty ( ) ) {
stream = gStreamsToProcess . front ( ) ;
gStreamsToProcess . pop ( ) ;
2021-06-26 22:59:40 +02:00
} else
2021-06-25 04:06:38 +02:00
continue ;
}
std : : unique_lock < std : : mutex > lock ( stream - > m_mutex ) ;
std : : pair < ALuint , ALuint > buffers , * lastBufAddr ;
bool insertBufsAfterCheck = false ;
do {
2021-06-26 22:59:40 +02:00
if ( ! stream - > IsOpened ( ) ) {
2021-06-25 04:06:38 +02:00
break ;
2021-06-26 22:59:40 +02:00
}
2021-06-25 04:06:38 +02:00
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
2020-05-04 19:33:48 +02:00
void CStream : : Initialise ( )
{
2021-01-07 21:01:44 +01:00
# ifdef AUDIO_OAL_USE_MPG123
2020-05-07 08:26:16 +02:00
mpg123_init ( ) ;
2020-05-22 16:40:19 +02:00
# endif
2021-06-25 04:06:38 +02:00
# ifdef MULTITHREADED_AUDIO
gAudioThread = std : : thread ( audioFileOpsThread ) ;
# endif
2020-05-04 19:33:48 +02:00
}
void CStream : : Terminate ( )
{
2021-01-07 21:01:44 +01:00
# ifdef AUDIO_OAL_USE_MPG123
2020-05-07 08:26:16 +02:00
mpg123_exit ( ) ;
2020-05-22 16:40:19 +02:00
# endif
2021-06-25 04:06:38 +02:00
# ifdef MULTITHREADED_AUDIO
gAudioThreadQueueMutex . lock ( ) ;
gAudioThreadTerm = true ;
gAudioThreadQueueMutex . unlock ( ) ;
gAudioThreadCv . notify_one ( ) ;
gAudioThread . join ( ) ;
# endif
2020-05-04 19:33:48 +02:00
}
2021-06-26 22:59:40 +02:00
CStream : : CStream ( ALuint * sources , ALuint ( & buffers ) [ NUM_STREAMBUFFERS ] ) :
2021-01-04 19:11:04 +01:00
m_pAlSources ( sources ) ,
2020-05-04 19:33:48 +02:00
m_alBuffers ( buffers ) ,
2020-05-07 08:26:16 +02:00
m_pBuffer ( nil ) ,
m_bPaused ( false ) ,
m_bActive ( false ) ,
2021-06-25 04:06:38 +02:00
# ifdef MULTITHREADED_AUDIO
2021-06-26 22:59:40 +02:00
m_bIExist ( false ) ,
2021-06-25 04:06:38 +02:00
m_bDoSeek ( false ) ,
m_SeekPos ( 0 ) ,
# endif
2020-05-07 08:26:16 +02:00
m_pSoundFile ( nil ) ,
m_bReset ( false ) ,
m_nVolume ( 0 ) ,
m_nPan ( 0 ) ,
2021-02-02 14:39:08 +01:00
m_nPosBeforeReset ( 0 ) ,
m_nLoopCount ( 1 )
2020-05-04 19:33:48 +02:00
{
2021-06-26 22:59:40 +02:00
}
bool CStream : : Open ( const char * filename , uint32 overrideSampleRate )
{
if ( IsOpened ( ) ) return false ;
# ifdef MULTITHREADED_AUDIO
std : : unique_lock < std : : mutex > lock ( m_mutex ) ;
m_bDoSeek = false ;
m_SeekPos = 0 ;
# endif
m_bPaused = false ;
m_bActive = false ;
m_bReset = false ;
m_nVolume = 0 ;
m_nPan = 0 ;
m_nPosBeforeReset = 0 ;
m_nLoopCount = 1 ;
2020-05-11 04:55:57 +02:00
// Be case-insensitive on linux (from https://github.com/OneSadCookie/fcaseopen/)
# if !defined(_WIN32)
2020-07-26 19:59:58 +02:00
char * real = casepath ( filename ) ;
if ( real ) {
strcpy ( m_aFilename , real ) ;
free ( real ) ;
2020-05-11 04:55:57 +02:00
} else {
# else
{
# endif
strcpy ( m_aFilename , filename ) ;
}
2020-05-04 19:33:48 +02:00
2020-05-07 08:26:16 +02:00
DEV ( " Stream %s \n " , m_aFilename ) ;
2021-01-07 21:01:44 +01:00
if ( ! strcasecmp ( & m_aFilename [ strlen ( m_aFilename ) - strlen ( " .wav " ) ] , " .wav " ) )
2021-01-06 14:46:59 +01:00
# ifdef AUDIO_OAL_USE_SNDFILE
2020-05-07 08:26:16 +02:00
m_pSoundFile = new CSndFile ( m_aFilename ) ;
2021-01-06 14:46:59 +01:00
# else
m_pSoundFile = new CWavFile ( m_aFilename ) ;
# endif
2021-01-07 21:01:44 +01:00
# ifdef AUDIO_OAL_USE_MPG123
else if ( ! strcasecmp ( & m_aFilename [ strlen ( m_aFilename ) - strlen ( " .mp3 " ) ] , " .mp3 " ) )
m_pSoundFile = new CMP3File ( m_aFilename ) ;
2020-05-27 06:52:45 +02:00
else if ( ! strcasecmp ( & m_aFilename [ strlen ( m_aFilename ) - strlen ( " .adf " ) ] , " .adf " ) )
m_pSoundFile = new CADFFile ( m_aFilename ) ;
2021-01-07 21:01:44 +01:00
# endif
2021-01-05 20:06:17 +01:00
else if ( ! strcasecmp ( & m_aFilename [ strlen ( m_aFilename ) - strlen ( " .vb " ) ] , " .VB " ) )
m_pSoundFile = new CVbFile ( m_aFilename , overrideSampleRate ) ;
2021-01-07 21:01:44 +01:00
# ifdef AUDIO_OAL_USE_OPUS
else if ( ! strcasecmp ( & m_aFilename [ strlen ( m_aFilename ) - strlen ( " .opus " ) ] , " .opus " ) )
2020-05-22 16:40:19 +02:00
m_pSoundFile = new COpusFile ( m_aFilename ) ;
# endif
2020-05-07 08:26:16 +02:00
else
m_pSoundFile = nil ;
2020-10-11 04:25:15 +02:00
2021-06-26 22:59:40 +02:00
if ( m_pSoundFile & & m_pSoundFile - > IsOpened ( ) )
2020-05-07 08:26:16 +02:00
{
2021-06-25 04:06:38 +02:00
uint32 bufSize = m_pSoundFile - > GetBufferSize ( ) ;
if ( bufSize ! = 0 ) { // Otherwise it's deferred
m_pBuffer = malloc ( bufSize ) ;
ASSERT ( m_pBuffer ! = nil ) ;
DEV ( " AvgSamplesPerSec: %d \n " , m_pSoundFile - > GetAvgSamplesPerSec ( ) ) ;
DEV ( " SampleCount: %d \n " , m_pSoundFile - > GetSampleCount ( ) ) ;
DEV ( " SampleRate: %d \n " , m_pSoundFile - > GetSampleRate ( ) ) ;
DEV ( " Channels: %d \n " , m_pSoundFile - > GetChannels ( ) ) ;
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 ( " Length MS: %02d:%02d \n " , ( m_pSoundFile - > GetLength ( ) / 1000 ) / 60 , ( m_pSoundFile - > GetLength ( ) / 1000 ) % 60 ) ;
}
2021-06-26 22:59:40 +02:00
# ifdef MULTITHREADED_AUDIO
m_bIExist = true ;
# endif
return true ;
2020-05-07 08:26:16 +02:00
}
2021-06-26 22:59:40 +02:00
return false ;
2020-05-04 19:33:48 +02:00
}
CStream : : ~ CStream ( )
{
2021-06-25 04:06:38 +02:00
assert ( ! IsOpened ( ) ) ;
2020-05-04 19:33:48 +02:00
}
2021-06-25 04:06:38 +02:00
void CStream : : Close ( )
2020-05-07 08:26:16 +02:00
{
2021-06-26 22:59:40 +02:00
if ( ! IsOpened ( ) ) return ;
2021-06-25 04:06:38 +02:00
# ifdef MULTITHREADED_AUDIO
{
std : : lock_guard < std : : mutex > lock ( m_mutex ) ;
Stop ( ) ;
ClearBuffers ( ) ;
2021-06-26 22:59:40 +02:00
m_bIExist = false ;
2021-06-27 13:42:52 +02:00
std : : queue < std : : pair < ALuint , ALuint > > ( ) . swap ( m_fillBuffers ) ;
tsQueue < std : : pair < ALuint , ALuint > > ( ) . swapNts ( m_queueBuffers ) ; // TSness not required, mutex is acquired
2021-06-25 04:06:38 +02:00
}
FlagAsToBeProcessed ( true ) ;
# else
2021-06-26 22:59:40 +02:00
2020-05-07 08:26:16 +02:00
Stop ( ) ;
ClearBuffers ( ) ;
2021-06-25 04:06:38 +02:00
2020-05-07 08:26:16 +02:00
if ( m_pSoundFile )
{
delete m_pSoundFile ;
m_pSoundFile = nil ;
}
2021-06-25 04:06:38 +02:00
2020-05-04 19:33:48 +02:00
if ( m_pBuffer )
{
free ( m_pBuffer ) ;
2020-05-07 08:26:16 +02:00
m_pBuffer = nil ;
2020-05-04 19:33:48 +02:00
}
2021-06-25 04:06:38 +02:00
# endif
2020-05-04 19:33:48 +02:00
}
2020-05-07 08:26:16 +02:00
bool CStream : : HasSource ( )
{
2021-01-04 19:11:04 +01:00
return ( m_pAlSources [ 0 ] ! = AL_NONE ) & & ( m_pAlSources [ 1 ] ! = AL_NONE ) ;
2020-05-07 08:26:16 +02:00
}
2021-06-26 22:59:40 +02:00
// m_bIExist only written in main thread, thus mutex is not needed on main thread
2020-05-04 19:33:48 +02:00
bool CStream : : IsOpened ( )
{
2021-06-26 22:59:40 +02:00
# ifdef MULTITHREADED_AUDIO
return m_bIExist ;
# else
2020-10-11 04:25:15 +02:00
return m_pSoundFile & & m_pSoundFile - > IsOpened ( ) ;
2021-06-26 22:59:40 +02:00
# endif
2020-05-04 19:33:48 +02:00
}
bool CStream : : IsPlaying ( )
{
2020-05-07 08:26:16 +02:00
if ( ! HasSource ( ) | | ! IsOpened ( ) ) return false ;
2020-10-11 04:25:15 +02:00
if ( ! m_bPaused )
2020-05-07 08:26:16 +02:00
{
2021-01-04 19:11:04 +01:00
ALint sourceState [ 2 ] ;
alGetSourcei ( m_pAlSources [ 0 ] , AL_SOURCE_STATE , & sourceState [ 0 ] ) ;
alGetSourcei ( m_pAlSources [ 1 ] , AL_SOURCE_STATE , & sourceState [ 1 ] ) ;
2021-02-02 14:39:08 +01:00
if ( sourceState [ 0 ] = = AL_PLAYING | | sourceState [ 1 ] = = AL_PLAYING )
2020-05-07 08:26:16 +02:00
return true ;
2021-06-25 04:06:38 +02:00
# 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
2020-05-07 08:26:16 +02:00
}
2020-05-04 19:33:48 +02:00
return false ;
}
2020-05-07 08:26:16 +02:00
void CStream : : Pause ( )
{
if ( ! HasSource ( ) ) return ;
ALint sourceState = AL_PAUSED ;
2021-01-04 19:11:04 +01:00
alGetSourcei ( m_pAlSources [ 0 ] , AL_SOURCE_STATE , & sourceState ) ;
2021-01-04 23:31:31 +01:00
if ( sourceState ! = AL_PAUSED )
2021-01-04 19:11:04 +01:00
alSourcePause ( m_pAlSources [ 0 ] ) ;
alGetSourcei ( m_pAlSources [ 1 ] , AL_SOURCE_STATE , & sourceState ) ;
if ( sourceState ! = AL_PAUSED )
alSourcePause ( m_pAlSources [ 1 ] ) ;
2020-05-07 08:26:16 +02:00
}
2020-05-04 19:33:48 +02:00
void CStream : : SetPause ( bool bPause )
{
2020-05-07 08:26:16 +02:00
if ( ! HasSource ( ) ) return ;
if ( bPause )
{
Pause ( ) ;
m_bPaused = true ;
}
else
{
if ( m_bPaused )
SetPlay ( true ) ;
m_bPaused = false ;
}
}
void CStream : : SetPitch ( float pitch )
{
if ( ! HasSource ( ) ) return ;
2021-01-04 19:11:04 +01:00
alSourcef ( m_pAlSources [ 0 ] , AL_PITCH , pitch ) ;
alSourcef ( m_pAlSources [ 1 ] , AL_PITCH , pitch ) ;
2020-05-07 08:26:16 +02:00
}
void CStream : : SetGain ( float gain )
{
if ( ! HasSource ( ) ) return ;
2021-01-04 19:11:04 +01:00
alSourcef ( m_pAlSources [ 0 ] , AL_GAIN , gain ) ;
alSourcef ( m_pAlSources [ 1 ] , AL_GAIN , gain ) ;
2020-05-07 08:26:16 +02:00
}
2021-01-04 19:11:04 +01:00
void CStream : : SetPosition ( int i , float x , float y , float z )
2020-05-07 08:26:16 +02:00
{
if ( ! HasSource ( ) ) return ;
2021-01-04 19:11:04 +01:00
alSource3f ( m_pAlSources [ i ] , AL_POSITION , x , y , z ) ;
2020-05-04 19:33:48 +02:00
}
void CStream : : SetVolume ( uint32 nVol )
{
2020-05-07 08:26:16 +02:00
m_nVolume = nVol ;
SetGain ( ALfloat ( nVol ) / MAX_VOLUME ) ;
2020-05-04 19:33:48 +02:00
}
void CStream : : SetPan ( uint8 nPan )
{
2021-06-28 13:57:05 +02:00
m_nPan = Clamp ( ( int8 ) nPan - 63 , 0 , 63 ) ;
2021-01-04 19:11:04 +01:00
SetPosition ( 0 , ( m_nPan - 63 ) / 64.0f , 0.0f , Sqrt ( 1.0f - SQR ( ( m_nPan - 63 ) / 64.0f ) ) ) ;
2021-06-28 13:57:05 +02:00
m_nPan = Clamp ( ( int8 ) nPan + 64 , 64 , 127 ) ;
2021-01-04 19:11:04 +01:00
SetPosition ( 1 , ( m_nPan - 63 ) / 64.0f , 0.0f , Sqrt ( 1.0f - SQR ( ( m_nPan - 63 ) / 64.0f ) ) ) ;
2020-05-07 08:26:16 +02:00
m_nPan = nPan ;
2020-05-04 19:33:48 +02:00
}
2021-02-14 16:07:21 +01:00
// Should only be called if source is stopped
2020-05-07 08:26:16 +02:00
void CStream : : SetPosMS ( uint32 nPos )
2020-05-04 19:33:48 +02:00
{
2020-10-11 04:25:15 +02:00
if ( ! IsOpened ( ) ) return ;
2021-06-25 04:06:38 +02:00
# 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 ) ;
}
2020-05-07 08:26:16 +02:00
ClearBuffers ( ) ;
2021-06-25 04:06:38 +02:00
// adding to gStreamsToProcess not needed, someone always calls Start() / BuffersShouldBeFilled() after SetPosMS
2020-05-04 19:33:48 +02:00
}
2020-05-07 08:26:16 +02:00
uint32 CStream : : GetPosMS ( )
2020-05-04 19:33:48 +02:00
{
2020-05-07 08:26:16 +02:00
if ( ! HasSource ( ) ) return 0 ;
2020-10-11 04:25:15 +02:00
if ( ! IsOpened ( ) ) return 0 ;
2020-05-07 08:26:16 +02:00
2021-06-25 04:06:38 +02:00
// Deferred init causes division by zero
if ( m_pSoundFile - > GetChannels ( ) = = 0 )
return 0 ;
2020-05-07 08:26:16 +02:00
ALint offset ;
//alGetSourcei(m_alSource, AL_SAMPLE_OFFSET, &offset);
2021-01-04 19:11:04 +01:00
alGetSourcei ( m_pAlSources [ 0 ] , AL_BYTE_OFFSET , & offset ) ;
2020-05-07 08:26:16 +02:00
2021-06-25 04:06:38 +02:00
//std::lock_guard<std::mutex> lock(m_mutex);
2020-05-07 08:26:16 +02:00
return m_pSoundFile - > Tell ( )
2021-01-04 19:11:04 +01:00
- m_pSoundFile - > samples2ms ( m_pSoundFile - > GetBufferSamples ( ) * ( NUM_STREAMBUFFERS / 2 - 1 ) ) / m_pSoundFile - > GetChannels ( )
2020-10-05 12:59:40 +02:00
+ m_pSoundFile - > samples2ms ( offset / m_pSoundFile - > GetSampleSize ( ) ) / m_pSoundFile - > GetChannels ( ) ;
2020-05-04 19:33:48 +02:00
}
2020-05-07 08:26:16 +02:00
uint32 CStream : : GetLengthMS ( )
2020-05-04 19:33:48 +02:00
{
2020-10-11 04:25:15 +02:00
if ( ! IsOpened ( ) ) return 0 ;
2020-05-07 08:26:16 +02:00
return m_pSoundFile - > GetLength ( ) ;
2020-05-04 19:33:48 +02:00
}
2021-01-04 19:11:04 +01:00
bool CStream : : FillBuffer ( ALuint * alBuffer )
2020-05-04 19:33:48 +02:00
{
2021-06-25 04:06:38 +02:00
# ifndef MULTITHREADED_AUDIO
2020-05-07 08:26:16 +02:00
if ( ! HasSource ( ) )
2020-05-04 19:33:48 +02:00
return false ;
2020-10-11 04:25:15 +02:00
if ( ! IsOpened ( ) )
2020-05-07 08:26:16 +02:00
return false ;
2021-01-04 19:11:04 +01:00
if ( ! ( alBuffer [ 0 ] ! = AL_NONE & & alIsBuffer ( alBuffer [ 0 ] ) ) )
return false ;
if ( ! ( alBuffer [ 1 ] ! = AL_NONE & & alIsBuffer ( alBuffer [ 1 ] ) ) )
2020-05-07 08:26:16 +02:00
return false ;
2021-06-25 04:06:38 +02:00
# endif
2020-05-07 08:26:16 +02:00
uint32 size = m_pSoundFile - > Decode ( m_pBuffer ) ;
if ( size = = 0 )
return false ;
2021-01-04 19:11:04 +01:00
2021-06-25 04:06:38 +02:00
uint32 channelSize = size / m_pSoundFile - > GetChannels ( ) ;
2021-01-04 19:11:04 +01:00
alBufferData ( alBuffer [ 0 ] , AL_FORMAT_MONO16 , m_pBuffer , channelSize , m_pSoundFile - > GetSampleRate ( ) ) ;
// TODO: use just one buffer if we play mono
if ( m_pSoundFile - > GetChannels ( ) = = 1 )
alBufferData ( alBuffer [ 1 ] , AL_FORMAT_MONO16 , m_pBuffer , channelSize , m_pSoundFile - > GetSampleRate ( ) ) ;
else
alBufferData ( alBuffer [ 1 ] , AL_FORMAT_MONO16 , ( uint8 * ) m_pBuffer + channelSize , channelSize , m_pSoundFile - > GetSampleRate ( ) ) ;
2020-05-07 08:26:16 +02:00
return true ;
}
2021-06-25 04:06:38 +02:00
# 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
2020-05-07 08:26:16 +02:00
int32 CStream : : FillBuffers ( )
{
int32 i = 0 ;
2021-01-04 19:11:04 +01:00
for ( i = 0 ; i < NUM_STREAMBUFFERS / 2 ; i + + )
2020-05-07 08:26:16 +02:00
{
2021-01-04 19:11:04 +01:00
if ( ! FillBuffer ( & m_alBuffers [ i * 2 ] ) )
2020-05-07 08:26:16 +02:00
break ;
2021-01-04 19:11:04 +01:00
alSourceQueueBuffers ( m_pAlSources [ 0 ] , 1 , & m_alBuffers [ i * 2 ] ) ;
alSourceQueueBuffers ( m_pAlSources [ 1 ] , 1 , & m_alBuffers [ i * 2 + 1 ] ) ;
2020-05-07 08:26:16 +02:00
}
return i ;
}
void CStream : : ClearBuffers ( )
{
if ( ! HasSource ( ) ) return ;
2021-01-04 19:11:04 +01:00
ALint buffersQueued [ 2 ] ;
alGetSourcei ( m_pAlSources [ 0 ] , AL_BUFFERS_QUEUED , & buffersQueued [ 0 ] ) ;
alGetSourcei ( m_pAlSources [ 1 ] , AL_BUFFERS_QUEUED , & buffersQueued [ 1 ] ) ;
2020-05-07 08:26:16 +02:00
ALuint value ;
2021-01-04 19:11:04 +01:00
while ( buffersQueued [ 0 ] - - )
alSourceUnqueueBuffers ( m_pAlSources [ 0 ] , 1 , & value ) ;
while ( buffersQueued [ 1 ] - - )
alSourceUnqueueBuffers ( m_pAlSources [ 1 ] , 1 , & value ) ;
2020-05-07 08:26:16 +02:00
}
2021-06-25 04:06:38 +02:00
bool CStream : : Setup ( bool imSureQueueIsEmpty , bool lock )
2020-05-07 08:26:16 +02:00
{
2020-10-11 04:25:15 +02:00
if ( IsOpened ( ) )
2020-05-07 08:26:16 +02:00
{
2021-06-25 04:06:38 +02:00
# ifdef MULTITHREADED_AUDIO
if ( lock )
m_mutex . lock ( ) ;
# endif
2021-02-14 16:07:21 +01:00
if ( ! imSureQueueIsEmpty ) {
2021-06-25 04:06:38 +02:00
Stop ( ) ;
2021-02-14 16:07:21 +01:00
ClearBuffers ( ) ;
}
2021-06-25 04:06:38 +02:00
# ifdef MULTITHREADED_AUDIO
if ( MusicManager . m_nMusicMode = = MUSICMODE_CUTSCENE ) {
m_pSoundFile - > Seek ( 0 ) ;
} else {
m_bDoSeek = true ;
m_SeekPos = 0 ;
}
if ( lock )
m_mutex . unlock ( ) ;
# else
2020-05-07 08:26:16 +02:00
m_pSoundFile - > Seek ( 0 ) ;
2021-06-25 04:06:38 +02:00
# endif
2020-05-07 08:26:16 +02:00
//SetPosition(0.0f, 0.0f, 0.0f);
SetPitch ( 1.0f ) ;
//SetPan(m_nPan);
//SetVolume(100);
}
2020-05-04 19:33:48 +02:00
return IsOpened ( ) ;
}
2021-02-02 14:39:08 +01:00
void CStream : : SetLoopCount ( int32 count )
{
if ( ! HasSource ( ) ) return ;
m_nLoopCount = count ;
}
2020-05-07 08:26:16 +02:00
void CStream : : SetPlay ( bool state )
{
if ( ! HasSource ( ) ) return ;
if ( state )
{
ALint sourceState = AL_PLAYING ;
2021-01-04 19:11:04 +01:00
alGetSourcei ( m_pAlSources [ 0 ] , AL_SOURCE_STATE , & sourceState ) ;
2020-05-07 08:26:16 +02:00
if ( sourceState ! = AL_PLAYING )
2021-01-04 19:11:04 +01:00
alSourcePlay ( m_pAlSources [ 0 ] ) ;
sourceState = AL_PLAYING ;
alGetSourcei ( m_pAlSources [ 1 ] , AL_SOURCE_STATE , & sourceState ) ;
if ( sourceState ! = AL_PLAYING )
alSourcePlay ( m_pAlSources [ 1 ] ) ;
2020-05-07 08:26:16 +02:00
m_bActive = true ;
}
else
{
ALint sourceState = AL_STOPPED ;
2021-01-04 19:11:04 +01:00
alGetSourcei ( m_pAlSources [ 0 ] , AL_SOURCE_STATE , & sourceState ) ;
2021-07-11 03:26:59 +02:00
if ( sourceState ! = AL_STOPPED )
2021-01-04 19:11:04 +01:00
alSourceStop ( m_pAlSources [ 0 ] ) ;
sourceState = AL_STOPPED ;
alGetSourcei ( m_pAlSources [ 1 ] , AL_SOURCE_STATE , & sourceState ) ;
if ( sourceState ! = AL_STOPPED )
alSourceStop ( m_pAlSources [ 1 ] ) ;
2020-05-07 08:26:16 +02:00
m_bActive = false ;
}
}
2020-05-04 19:33:48 +02:00
void CStream : : Start ( )
{
2020-05-07 08:26:16 +02:00
if ( ! HasSource ( ) ) return ;
2021-06-25 04:06:38 +02:00
# 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 ( ) ;
2020-05-07 08:26:16 +02:00
}
2020-05-04 19:33:48 +02:00
2020-05-07 08:26:16 +02:00
void CStream : : Stop ( )
{
if ( ! HasSource ( ) ) return ;
SetPlay ( false ) ;
2020-05-04 19:33:48 +02:00
}
void CStream : : Update ( )
{
if ( ! IsOpened ( ) )
return ;
2020-05-07 08:26:16 +02:00
if ( ! HasSource ( ) )
return ;
if ( m_bReset )
return ;
if ( ! m_bPaused )
{
2021-06-25 04:06:38 +02:00
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 } ;
2021-01-04 19:11:04 +01:00
// Relying a lot on left buffer states in here
2021-01-06 14:46:59 +01:00
do
{
//alSourcef(m_pAlSources[0], AL_ROLLOFF_FACTOR, 0.0f);
2021-02-02 14:39:08 +01:00
alGetSourcei ( m_pAlSources [ 0 ] , AL_BUFFERS_QUEUED , & totalBuffers [ 0 ] ) ;
2021-01-06 14:46:59 +01:00
alGetSourcei ( m_pAlSources [ 0 ] , AL_BUFFERS_PROCESSED , & buffersProcessed [ 0 ] ) ;
//alSourcef(m_pAlSources[1], AL_ROLLOFF_FACTOR, 0.0f);
2021-02-02 14:39:08 +01:00
alGetSourcei ( m_pAlSources [ 1 ] , AL_BUFFERS_QUEUED , & totalBuffers [ 1 ] ) ;
2021-01-06 14:46:59 +01:00
alGetSourcei ( m_pAlSources [ 1 ] , AL_BUFFERS_PROCESSED , & buffersProcessed [ 1 ] ) ;
} while ( buffersProcessed [ 0 ] ! = buffersProcessed [ 1 ] ) ;
2021-06-25 04:06:38 +02:00
2021-01-04 19:11:04 +01:00
assert ( buffersProcessed [ 0 ] = = buffersProcessed [ 1 ] ) ;
2021-02-02 14:39:08 +01:00
// Correcting OpenAL concepts here:
// AL_BUFFERS_QUEUED = Number of *all* buffers in queue, including processed, processing and pending
// 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
2021-06-25 04:06:38 +02:00
2021-02-02 14:39:08 +01:00
// 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 )
2020-05-07 08:26:16 +02:00
{
2021-06-25 04:06:38 +02:00
# ifdef MULTITHREADED_AUDIO
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 )
m_nLoopCount - - ;
}
2020-05-07 08:26:16 +02:00
}
2021-02-02 14:39:08 +01:00
else
2020-05-07 08:26:16 +02:00
{
2021-06-25 04:06:38 +02:00
static std : : queue < std : : pair < ALuint , ALuint > > tempFillBuffer ;
while ( buffersProcessed [ 0 ] - - )
2021-02-02 14:39:08 +01:00
{
ALuint buffer [ 2 ] ;
alSourceUnqueueBuffers ( m_pAlSources [ 0 ] , 1 , & buffer [ 0 ] ) ;
alSourceUnqueueBuffers ( m_pAlSources [ 1 ] , 1 , & buffer [ 1 ] ) ;
2021-06-25 04:06:38 +02:00
if ( m_bActive )
2021-02-02 14:39:08 +01:00
{
2021-06-25 04:06:38 +02:00
tempFillBuffer . push ( std : : pair < ALuint , ALuint > ( buffer [ 0 ] , buffer [ 1 ] ) ) ;
2021-02-02 14:39:08 +01:00
}
}
2021-06-25 04:06:38 +02:00
if ( m_bActive & & buffersProcessed [ 1 ] )
{
# 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
}
2020-05-07 08:26:16 +02:00
}
2021-02-02 14:39:08 +01:00
2021-06-25 04:06:38 +02:00
// Source may be starved to audio and stopped itself
if ( m_bActive & & ! buffersQueuedAndStarted & & ( buffersQueuedButNotStarted | | ( totalBuffers [ 1 ] - buffersProcessed [ 1 ] ! = 0 ) ) )
2021-02-02 14:39:08 +01:00
SetPlay ( true ) ;
2020-05-07 08:26:16 +02:00
}
2020-05-04 19:33:48 +02:00
}
2020-05-07 08:26:16 +02:00
void CStream : : ProviderInit ( )
{
if ( m_bReset )
{
2021-06-25 04:06:38 +02:00
if ( Setup ( true , false ) ) // lock not needed, thread can't process streams with m_bReset set
2020-05-07 08:26:16 +02:00
{
SetPan ( m_nPan ) ;
SetVolume ( m_nVolume ) ;
2021-02-02 14:39:08 +01:00
SetLoopCount ( m_nLoopCount ) ;
2020-05-07 08:26:16 +02:00
SetPosMS ( m_nPosBeforeReset ) ;
2021-06-25 04:06:38 +02:00
# ifdef MULTITHREADED_AUDIO
std : : unique_lock < std : : mutex > lock ( m_mutex ) ;
# endif
if ( m_bActive )
BuffersShouldBeFilled ( ) ;
if ( m_bPaused )
2020-05-07 08:26:16 +02:00
Pause ( ) ;
2021-06-25 04:06:38 +02:00
m_bReset = false ;
} else {
# ifdef MULTITHREADED_AUDIO
std : : unique_lock < std : : mutex > lock ( m_mutex ) ;
# endif
m_bReset = false ;
2020-05-07 08:26:16 +02:00
}
}
}
void CStream : : ProviderTerm ( )
{
2021-06-25 04:06:38 +02:00
# 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
2020-05-07 08:26:16 +02:00
m_bReset = true ;
m_nPosBeforeReset = GetPosMS ( ) ;
2021-06-25 04:06:38 +02:00
Stop ( ) ;
2020-05-07 08:26:16 +02:00
ClearBuffers ( ) ;
}
2020-05-11 04:55:57 +02:00
# endif