2014-09-06 03:29:22 +02:00
/*****************************************************************************
* Copyright ( c ) 2014 Ted John
* OpenRCT2 , an open source clone of Roller Coaster Tycoon 2.
*
* This file is part of OpenRCT2 .
*
* OpenRCT2 is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
2015-10-20 20:16:30 +02:00
2014-09-06 03:29:22 +02:00
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
2015-10-20 20:16:30 +02:00
2014-09-06 03:29:22 +02:00
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
extern " C " {
2014-10-09 15:03:54 +02:00
# include "../config.h"
# include "../platform/platform.h"
2015-02-05 18:34:51 +01:00
# include "../localisation/localisation.h"
2015-08-04 00:16:30 +02:00
# include "audio.h"
2014-09-06 03:29:22 +02:00
}
# include "mixer.h"
2014-09-13 19:51:58 +02:00
Mixer gMixer ;
2015-04-25 16:55:12 +02:00
Source : : ~ Source ( )
{
}
unsigned long Source : : GetSome ( unsigned long offset , const uint8 * * data , unsigned long length )
{
if ( offset > = Length ( ) ) {
return 0 ;
}
unsigned long size = length ;
if ( offset + length > Length ( ) ) {
size = Length ( ) - offset ;
}
return Read ( offset , data , size ) ;
}
unsigned long Source : : Length ( )
{
return length ;
}
const AudioFormat & Source : : Format ( )
{
return format ;
}
Source_Null : : Source_Null ( )
{
length = 0 ;
}
unsigned long Source_Null : : Read ( unsigned long offset , const uint8 * * data , unsigned long length )
{
return 0 ;
}
Source_Sample : : Source_Sample ( )
2014-09-06 03:29:22 +02:00
{
data = 0 ;
length = 0 ;
issdlwav = false ;
}
2015-04-25 16:55:12 +02:00
Source_Sample : : ~ Source_Sample ( )
2014-09-06 03:29:22 +02:00
{
Unload ( ) ;
}
2015-04-25 16:55:12 +02:00
unsigned long Source_Sample : : Read ( unsigned long offset , const uint8 * * data , unsigned long length )
2014-09-06 03:29:22 +02:00
{
2015-04-25 16:55:12 +02:00
* data = & Source_Sample : : data [ offset ] ;
return length ;
}
bool Source_Sample : : LoadWAV ( const char * filename )
{
log_verbose ( " Source_Sample::LoadWAV(%s) " , filename ) ;
2015-02-04 20:11:41 +01:00
2014-09-06 03:29:22 +02:00
Unload ( ) ;
2015-08-29 14:12:52 +02:00
SDL_RWops * rw = SDL_RWFromFile ( filename , " rb " ) ;
2015-02-04 20:11:41 +01:00
if ( rw = = NULL ) {
log_verbose ( " Error loading %s " , filename ) ;
2014-09-06 03:29:22 +02:00
return false ;
}
2015-02-04 20:11:41 +01:00
2014-09-06 03:29:22 +02:00
SDL_AudioSpec audiospec ;
memset ( & audiospec , 0 , sizeof ( audiospec ) ) ;
2014-09-13 19:51:58 +02:00
SDL_AudioSpec * spec = SDL_LoadWAV_RW ( rw , false , & audiospec , & data , ( Uint32 * ) & length ) ;
2015-02-04 20:11:41 +01:00
SDL_RWclose ( rw ) ;
2014-09-06 03:29:22 +02:00
if ( spec ! = NULL ) {
format . freq = spec - > freq ;
format . format = spec - > format ;
format . channels = spec - > channels ;
issdlwav = true ;
2014-09-13 19:51:58 +02:00
} else {
2015-02-04 20:11:41 +01:00
log_verbose ( " Error loading %s, unsupported WAV format " , filename ) ;
2014-09-13 19:51:58 +02:00
return false ;
2014-09-06 03:29:22 +02:00
}
2015-02-04 20:11:41 +01:00
2014-09-06 03:29:22 +02:00
return true ;
}
2015-08-29 14:12:52 +02:00
bool Source_Sample : : LoadCSS1 ( const char * filename , unsigned int offset )
2014-09-06 03:29:22 +02:00
{
2015-04-25 16:55:12 +02:00
log_verbose ( " Source_Sample::LoadCSS1(%s, %d) " , filename , offset ) ;
2015-02-05 18:34:51 +01:00
2014-09-06 03:29:22 +02:00
Unload ( ) ;
2015-08-29 14:12:52 +02:00
SDL_RWops * rw = SDL_RWFromFile ( filename , " rb " ) ;
2015-02-04 20:11:41 +01:00
if ( rw = = NULL ) {
2015-02-05 18:34:51 +01:00
log_verbose ( " Unable to load %s " , filename ) ;
2014-09-06 03:29:22 +02:00
return false ;
}
2015-02-05 18:34:51 +01:00
2014-09-06 03:29:22 +02:00
Uint32 numsounds ;
SDL_RWread ( rw , & numsounds , sizeof ( numsounds ) , 1 ) ;
if ( offset > numsounds ) {
SDL_RWclose ( rw ) ;
return false ;
}
SDL_RWseek ( rw , offset * 4 , RW_SEEK_CUR ) ;
Uint32 soundoffset ;
SDL_RWread ( rw , & soundoffset , sizeof ( soundoffset ) , 1 ) ;
SDL_RWseek ( rw , soundoffset , RW_SEEK_SET ) ;
Uint32 soundsize ;
SDL_RWread ( rw , & soundsize , sizeof ( soundsize ) , 1 ) ;
length = soundsize ;
2015-09-23 10:39:45 +02:00
struct WaveFormatEx
{
Uint16 encoding ;
Uint16 channels ;
Uint32 frequency ;
Uint32 byterate ;
Uint16 blockalign ;
Uint16 bitspersample ;
Uint16 extrasize ;
} waveformat ;
2014-09-06 03:29:22 +02:00
SDL_RWread ( rw , & waveformat , sizeof ( waveformat ) , 1 ) ;
2015-09-23 10:39:45 +02:00
format . freq = waveformat . frequency ;
2014-09-13 19:51:58 +02:00
format . format = AUDIO_S16LSB ;
2015-09-23 10:39:45 +02:00
format . channels = waveformat . channels ;
2015-04-25 16:55:12 +02:00
data = new ( std : : nothrow ) uint8 [ length ] ;
if ( ! data ) {
log_verbose ( " Unable to allocate data " ) ;
SDL_RWclose ( rw ) ;
return false ;
}
2014-09-06 03:29:22 +02:00
SDL_RWread ( rw , data , length , 1 ) ;
SDL_RWclose ( rw ) ;
return true ;
}
2015-04-25 16:55:12 +02:00
void Source_Sample : : Unload ( )
2014-09-06 03:29:22 +02:00
{
if ( data ) {
if ( issdlwav ) {
SDL_FreeWAV ( data ) ;
} else {
delete [ ] data ;
}
data = 0 ;
}
issdlwav = false ;
length = 0 ;
}
2015-04-25 16:55:12 +02:00
bool Source_Sample : : Convert ( AudioFormat format )
2014-10-08 07:00:11 +02:00
{
2015-04-25 16:55:12 +02:00
if ( Source_Sample : : format . format ! = format . format | | Source_Sample : : format . channels ! = format . channels | | Source_Sample : : format . freq ! = format . freq ) {
2014-09-06 03:29:22 +02:00
SDL_AudioCVT cvt ;
2015-04-25 16:55:12 +02:00
if ( SDL_BuildAudioCVT ( & cvt , Source_Sample : : format . format , Source_Sample : : format . channels , Source_Sample : : format . freq , format . format , format . channels , format . freq ) < 0 ) {
2014-09-06 03:29:22 +02:00
return false ;
}
cvt . len = length ;
2014-09-13 19:51:58 +02:00
cvt . buf = ( Uint8 * ) new uint8 [ cvt . len * cvt . len_mult ] ;
2014-09-06 03:29:22 +02:00
memcpy ( cvt . buf , data , length ) ;
if ( SDL_ConvertAudio ( & cvt ) < 0 ) {
delete [ ] cvt . buf ;
return false ;
}
Unload ( ) ;
data = cvt . buf ;
length = cvt . len_cvt ;
2015-04-25 16:55:12 +02:00
Source_Sample : : format = format ;
2014-10-07 04:21:55 +02:00
return true ;
2014-09-06 03:29:22 +02:00
}
2014-10-07 04:21:55 +02:00
return false ;
2014-09-06 03:29:22 +02:00
}
2015-04-25 16:55:12 +02:00
Source_SampleStream : : Source_SampleStream ( )
2014-09-06 03:29:22 +02:00
{
2015-04-25 16:55:12 +02:00
length = 0 ;
rw = NULL ;
buffer = 0 ;
buffersize = 0 ;
2014-09-06 03:29:22 +02:00
}
2015-04-25 16:55:12 +02:00
Source_SampleStream : : ~ Source_SampleStream ( )
2014-09-06 03:29:22 +02:00
{
2015-04-25 16:55:12 +02:00
Unload ( ) ;
2014-09-06 03:29:22 +02:00
}
2015-04-25 16:55:12 +02:00
unsigned long Source_SampleStream : : Read ( unsigned long offset , const uint8 * * data , unsigned long length )
2014-09-06 03:29:22 +02:00
{
2015-04-25 16:55:12 +02:00
if ( length > buffersize ) {
if ( buffer ) {
delete [ ] buffer ;
}
buffer = new ( std : : nothrow ) uint8 [ length ] ;
if ( ! buffer ) {
return 0 ;
}
buffersize = length ;
}
Sint64 currentposition = SDL_RWtell ( rw ) ;
if ( currentposition = = - 1 ) {
return 0 ;
}
if ( currentposition - databegin ! = offset ) {
Sint64 newposition = SDL_RWseek ( rw , databegin + offset , SEEK_SET ) ;
if ( newposition = = - 1 ) {
return 0 ;
}
currentposition = newposition ;
}
* data = buffer ;
int read = SDL_RWread ( rw , buffer , 1 , length ) ;
if ( read = = - 1 ) {
return 0 ;
}
return read ;
2014-09-06 03:29:22 +02:00
}
2015-04-25 16:55:12 +02:00
bool Source_SampleStream : : LoadWAV ( SDL_RWops * rw )
2014-09-06 03:29:22 +02:00
{
2015-04-25 16:55:12 +02:00
Unload ( ) ;
if ( rw = = NULL ) {
return false ;
2014-09-06 03:29:22 +02:00
}
2015-04-25 16:55:12 +02:00
Source_SampleStream : : rw = rw ;
Uint32 chunk_id = SDL_ReadLE32 ( rw ) ;
const Uint32 RIFF = 0x46464952 ;
if ( chunk_id ! = RIFF ) {
log_verbose ( " Not a WAV file " ) ;
return false ;
}
Uint32 chunk_size = SDL_ReadLE32 ( rw ) ;
Uint32 chunk_format = SDL_ReadLE32 ( rw ) ;
const Uint32 WAVE = 0x45564157 ;
if ( chunk_format ! = WAVE ) {
log_verbose ( " Not in WAVE format " ) ;
return false ;
}
const Uint32 FMT = 0x20746D66 ;
Uint32 fmtchunk_size = FindChunk ( rw , FMT ) ;
if ( ! fmtchunk_size ) {
log_verbose ( " Could not find FMT chunk " ) ;
return false ;
}
2015-05-07 03:14:41 +02:00
Uint64 chunkstart = SDL_RWtell ( rw ) ;
2015-09-23 10:39:45 +02:00
struct WaveFormat
{
Uint16 encoding ;
Uint16 channels ;
Uint32 frequency ;
Uint32 byterate ;
Uint16 blockalign ;
Uint16 bitspersample ;
} waveformat ;
2015-04-25 16:55:12 +02:00
SDL_RWread ( rw , & waveformat , sizeof ( waveformat ) , 1 ) ;
2015-05-07 03:14:41 +02:00
SDL_RWseek ( rw , chunkstart + fmtchunk_size , RW_SEEK_SET ) ;
2015-09-23 10:39:45 +02:00
const Uint16 pcmformat = 0x0001 ;
if ( waveformat . encoding ! = pcmformat ) {
2015-04-25 16:55:12 +02:00
log_verbose ( " Not in proper format " ) ;
return false ;
}
2015-09-23 10:39:45 +02:00
format . freq = waveformat . frequency ;
switch ( waveformat . bitspersample ) {
2015-04-25 16:55:12 +02:00
case 8 :
format . format = AUDIO_U8 ;
break ;
case 16 :
format . format = AUDIO_S16LSB ;
break ;
default :
log_verbose ( " Invalid bits per sample " ) ;
return false ;
break ;
}
2015-09-23 10:39:45 +02:00
format . channels = waveformat . channels ;
2015-04-25 16:55:12 +02:00
const Uint32 DATA = 0x61746164 ;
Uint32 datachunk_size = FindChunk ( rw , DATA ) ;
if ( ! datachunk_size ) {
log_verbose ( " Could not find DATA chunk " ) ;
return false ;
}
length = datachunk_size ;
databegin = SDL_RWtell ( rw ) ;
return true ;
2014-09-06 03:29:22 +02:00
}
2015-04-25 16:55:12 +02:00
Uint32 Source_SampleStream : : FindChunk ( SDL_RWops * rw , Uint32 wanted_id )
2014-09-06 03:29:22 +02:00
{
2015-04-25 16:55:12 +02:00
Uint32 subchunk_id = SDL_ReadLE32 ( rw ) ;
Uint32 subchunk_size = SDL_ReadLE32 ( rw ) ;
if ( subchunk_id = = wanted_id ) {
return subchunk_size ;
}
const Uint32 FACT = 0x74636166 ;
const Uint32 LIST = 0x5453494c ;
const Uint32 BEXT = 0x74786562 ;
const Uint32 JUNK = 0x4B4E554A ;
while ( subchunk_id = = FACT | | subchunk_id = = LIST | | subchunk_id = = BEXT | | subchunk_id = = JUNK ) {
SDL_RWseek ( rw , subchunk_size , RW_SEEK_CUR ) ;
subchunk_id = SDL_ReadLE32 ( rw ) ;
subchunk_size = SDL_ReadLE32 ( rw ) ;
if ( subchunk_id = = wanted_id ) {
return subchunk_size ;
}
2014-09-06 03:29:22 +02:00
}
return 0 ;
}
2015-04-25 16:55:12 +02:00
void Source_SampleStream : : Unload ( )
2014-09-06 03:29:22 +02:00
{
2015-04-25 16:55:12 +02:00
if ( rw ) {
SDL_RWclose ( rw ) ;
rw = NULL ;
2014-09-06 03:29:22 +02:00
}
2015-04-25 16:55:12 +02:00
length = 0 ;
if ( buffer ) {
delete [ ] buffer ;
buffer = 0 ;
}
buffersize = 0 ;
2014-09-06 03:29:22 +02:00
}
Channel : : Channel ( )
{
resampler = 0 ;
2014-10-08 07:00:11 +02:00
SetRate ( 1 ) ;
2014-09-06 03:29:22 +02:00
SetVolume ( SDL_MIX_MAXVOLUME ) ;
2014-10-13 02:29:43 +02:00
oldvolume = 0 ;
2015-04-30 01:29:37 +02:00
oldvolume_l = 0 ;
oldvolume_r = 0 ;
2014-10-08 07:00:11 +02:00
SetPan ( 0.5f ) ;
done = true ;
2014-10-13 02:29:43 +02:00
stopping = false ;
2015-04-25 16:55:12 +02:00
source = 0 ;
deletesourceondone = false ;
2015-06-04 22:29:21 +02:00
group = MIXER_GROUP_NONE ;
2014-09-06 03:29:22 +02:00
}
Channel : : ~ Channel ( )
{
if ( resampler ) {
speex_resampler_destroy ( resampler ) ;
resampler = 0 ;
}
2015-04-25 16:55:12 +02:00
if ( deletesourceondone ) {
delete source ;
}
2014-09-06 03:29:22 +02:00
}
2015-04-25 16:55:12 +02:00
void Channel : : Play ( Source & source , int loop = MIXER_LOOP_NONE )
2014-09-06 03:29:22 +02:00
{
2015-04-25 16:55:12 +02:00
Channel : : source = & source ;
2014-09-06 03:29:22 +02:00
Channel : : loop = loop ;
offset = 0 ;
2014-10-08 07:00:11 +02:00
done = false ;
2014-09-06 03:29:22 +02:00
}
void Channel : : SetRate ( double rate )
{
Channel : : rate = rate ;
if ( Channel : : rate < 0.001 ) {
Channel : : rate = 0.001 ;
}
}
void Channel : : SetVolume ( int volume )
{
Channel : : volume = volume ;
2014-09-13 19:51:58 +02:00
if ( volume > SDL_MIX_MAXVOLUME ) {
2014-09-06 03:29:22 +02:00
Channel : : volume = SDL_MIX_MAXVOLUME ;
}
2014-09-13 19:51:58 +02:00
if ( volume < 0 ) {
2014-09-06 03:29:22 +02:00
Channel : : volume = 0 ;
}
}
2014-09-13 19:51:58 +02:00
void Channel : : SetPan ( float pan )
{
Channel : : pan = pan ;
if ( pan > 1 ) {
Channel : : pan = 1 ;
}
if ( pan < 0 ) {
Channel : : pan = 0 ;
}
2015-04-30 01:29:37 +02:00
double decibels = ( abs ( Channel : : pan - 0.5 ) * 2.0 ) * 100.0 ;
double attenuation = pow ( 10 , decibels / 20.0 ) ;
if ( Channel : : pan < = 0.5 ) {
volume_l = 1.0 ;
volume_r = float ( 1.0 / attenuation ) ;
} else {
volume_r = 1.0 ;
volume_l = float ( 1.0 / attenuation ) ;
}
2014-09-13 19:51:58 +02:00
}
2014-10-08 07:00:11 +02:00
bool Channel : : IsPlaying ( )
{
return ! done ;
}
2014-11-11 20:07:21 +01:00
unsigned long Channel : : GetOffset ( )
{
return offset ;
}
bool Channel : : SetOffset ( unsigned long offset )
{
2015-04-25 16:55:12 +02:00
if ( source & & offset < source - > Length ( ) ) {
int samplesize = source - > Format ( ) . channels * source - > Format ( ) . BytesPerSample ( ) ;
2014-11-11 20:07:21 +01:00
Channel : : offset = ( offset / samplesize ) * samplesize ;
return true ;
}
return false ;
}
2015-06-04 22:29:21 +02:00
void Channel : : SetGroup ( int group )
{
Channel : : group = group ;
}
2015-04-25 16:55:12 +02:00
Mixer : : Mixer ( )
{
effectbuffer = 0 ;
2015-10-17 19:46:40 +02:00
volume = 1 ;
2015-04-25 16:55:12 +02:00
for ( int i = 0 ; i < countof ( css1sources ) ; i + + ) {
css1sources [ i ] = 0 ;
}
for ( int i = 0 ; i < countof ( musicsources ) ; i + + ) {
musicsources [ i ] = 0 ;
}
}
2014-09-13 19:51:58 +02:00
void Mixer : : Init ( const char * device )
2014-09-06 03:29:22 +02:00
{
Close ( ) ;
SDL_AudioSpec want , have ;
SDL_zero ( want ) ;
2014-09-13 19:51:58 +02:00
want . freq = 44100 ;
want . format = AUDIO_S16SYS ;
2014-09-06 03:29:22 +02:00
want . channels = 2 ;
want . samples = 1024 ;
want . callback = Callback ;
want . userdata = this ;
deviceid = SDL_OpenAudioDevice ( device , 0 , & want , & have , 0 ) ;
format . format = have . format ;
format . channels = have . channels ;
format . freq = have . freq ;
2014-09-13 19:51:58 +02:00
const char * filename = get_file_path ( PATH_ID_CSS1 ) ;
2015-04-25 16:55:12 +02:00
for ( int i = 0 ; i < countof ( css1sources ) ; i + + ) {
Source_Sample * source_sample = new Source_Sample ;
if ( source_sample - > LoadCSS1 ( filename , i ) ) {
source_sample - > Convert ( format ) ; // convert to audio output format, saves some cpu usage but requires a bit more memory, optional
css1sources [ i ] = source_sample ;
} else {
css1sources [ i ] = & source_null ;
delete source_sample ;
}
2014-09-06 03:29:22 +02:00
}
2014-11-11 20:07:21 +01:00
effectbuffer = new uint8 [ ( have . samples * format . BytesPerSample ( ) * format . channels ) ] ;
2014-09-06 03:29:22 +02:00
SDL_PauseAudioDevice ( deviceid , 0 ) ;
}
void Mixer : : Close ( )
{
2014-10-07 20:16:17 +02:00
Lock ( ) ;
while ( channels . begin ( ) ! = channels . end ( ) ) {
2014-10-13 02:29:43 +02:00
delete * ( channels . begin ( ) ) ;
channels . erase ( channels . begin ( ) ) ;
2014-10-07 20:16:17 +02:00
}
Unlock ( ) ;
2014-09-06 03:29:22 +02:00
SDL_CloseAudioDevice ( deviceid ) ;
2015-04-25 16:55:12 +02:00
for ( int i = 0 ; i < countof ( css1sources ) ; i + + ) {
if ( css1sources [ i ] & & css1sources [ i ] ! = & source_null ) {
delete css1sources [ i ] ;
css1sources [ i ] = 0 ;
}
}
for ( int i = 0 ; i < countof ( musicsources ) ; i + + ) {
if ( musicsources [ i ] & & musicsources [ i ] ! = & source_null ) {
delete musicsources [ i ] ;
musicsources [ i ] = 0 ;
}
}
if ( effectbuffer ) {
delete [ ] effectbuffer ;
effectbuffer = 0 ;
}
2014-09-06 03:29:22 +02:00
}
2014-10-07 04:21:55 +02:00
void Mixer : : Lock ( )
{
SDL_LockAudioDevice ( deviceid ) ;
}
void Mixer : : Unlock ( )
{
SDL_UnlockAudioDevice ( deviceid ) ;
}
2015-04-25 16:55:12 +02:00
Channel * Mixer : : Play ( Source & source , int loop , bool deleteondone , bool deletesourceondone )
2014-10-07 04:21:55 +02:00
{
Lock ( ) ;
2015-04-25 16:55:12 +02:00
Channel * newchannel = new ( std : : nothrow ) Channel ;
2014-10-07 04:21:55 +02:00
if ( newchannel ) {
2015-04-25 16:55:12 +02:00
newchannel - > Play ( source , loop ) ;
2014-10-08 07:00:11 +02:00
newchannel - > deleteondone = deleteondone ;
2015-04-25 16:55:12 +02:00
newchannel - > deletesourceondone = deletesourceondone ;
2014-10-07 04:21:55 +02:00
channels . push_back ( newchannel ) ;
}
Unlock ( ) ;
return newchannel ;
}
void Mixer : : Stop ( Channel & channel )
{
Lock ( ) ;
2014-10-13 02:29:43 +02:00
channel . stopping = true ;
2014-10-07 04:21:55 +02:00
Unlock ( ) ;
}
2014-10-08 07:00:11 +02:00
bool Mixer : : LoadMusic ( int pathid )
{
2015-04-25 16:55:12 +02:00
if ( pathid > = countof ( musicsources ) ) {
2014-10-08 07:00:11 +02:00
return false ;
}
2015-04-25 16:55:12 +02:00
if ( ! musicsources [ pathid ] ) {
2014-10-08 07:00:11 +02:00
const char * filename = get_file_path ( pathid ) ;
2015-04-25 16:55:12 +02:00
Source_Sample * source_sample = new Source_Sample ;
if ( source_sample - > LoadWAV ( filename ) ) {
musicsources [ pathid ] = source_sample ;
return true ;
} else {
delete source_sample ;
musicsources [ pathid ] = & source_null ;
return false ;
}
2014-10-08 07:00:11 +02:00
} else {
return true ;
}
}
2015-10-17 19:46:40 +02:00
void Mixer : : SetVolume ( float volume )
{
Mixer : : volume = volume ;
}
2014-09-13 19:51:58 +02:00
void SDLCALL Mixer : : Callback ( void * arg , uint8 * stream , int length )
2014-09-06 03:29:22 +02:00
{
Mixer * mixer = ( Mixer * ) arg ;
memset ( stream , 0 , length ) ;
2014-10-08 07:00:11 +02:00
std : : list < Channel * > : : iterator i = mixer - > channels . begin ( ) ;
while ( i ! = mixer - > channels . end ( ) ) {
2014-10-07 04:21:55 +02:00
mixer - > MixChannel ( * ( * i ) , stream , length ) ;
2014-10-13 02:29:43 +02:00
if ( ( ( * i ) - > done & & ( * i ) - > deleteondone ) | | ( * i ) - > stopping ) {
2014-10-08 07:00:11 +02:00
delete ( * i ) ;
i = mixer - > channels . erase ( i ) ;
} else {
i + + ;
}
2014-09-06 03:29:22 +02:00
}
}
2014-09-13 19:51:58 +02:00
void Mixer : : MixChannel ( Channel & channel , uint8 * data , int length )
2014-09-06 03:29:22 +02:00
{
2015-07-15 00:38:56 +02:00
if ( channel . source & & channel . source - > Length ( ) > 0 & & ! channel . done & & gConfigSound . sound ) {
2015-04-25 16:55:12 +02:00
AudioFormat streamformat = channel . source - > Format ( ) ;
2014-09-06 03:29:22 +02:00
int loaded = 0 ;
SDL_AudioCVT cvt ;
cvt . len_ratio = 1 ;
do {
int samplesize = format . channels * format . BytesPerSample ( ) ;
int samples = length / samplesize ;
int samplesloaded = loaded / samplesize ;
2014-10-07 20:16:17 +02:00
double rate = 1 ;
if ( format . format = = AUDIO_S16SYS ) {
rate = channel . rate ;
}
2014-11-11 20:07:21 +01:00
int samplestoread = ( int ) ( ( samples - samplesloaded ) * rate ) ;
2014-09-06 03:29:22 +02:00
int lengthloaded = 0 ;
2015-04-25 16:55:12 +02:00
if ( channel . offset < channel . source - > Length ( ) ) {
2014-09-06 03:29:22 +02:00
bool mustconvert = false ;
2015-04-25 16:55:12 +02:00
if ( MustConvert ( * channel . source ) ) {
2014-11-11 20:07:21 +01:00
if ( SDL_BuildAudioCVT ( & cvt , streamformat . format , streamformat . channels , streamformat . freq , Mixer : : format . format , Mixer : : format . channels , Mixer : : format . freq ) = = - 1 ) {
2014-09-06 03:29:22 +02:00
break ;
}
mustconvert = true ;
}
2014-09-13 19:51:58 +02:00
const uint8 * datastream = 0 ;
2014-11-11 20:07:21 +01:00
int toread = ( int ) ( samplestoread / cvt . len_ratio ) * samplesize ;
2015-04-25 16:55:12 +02:00
int readfromstream = ( channel . source - > GetSome ( channel . offset , & datastream , toread ) ) ;
2014-09-06 03:29:22 +02:00
if ( readfromstream = = 0 ) {
break ;
}
2014-09-13 19:51:58 +02:00
uint8 * dataconverted = 0 ;
const uint8 * tomix = 0 ;
2014-09-06 03:29:22 +02:00
if ( mustconvert ) {
2014-11-11 20:07:21 +01:00
// tofix: there seems to be an issue with converting audio using SDL_ConvertAudio in the callback vs preconverted, can cause pops and static depending on sample rate and channels
2014-09-06 03:29:22 +02:00
if ( Convert ( cvt , datastream , readfromstream , & dataconverted ) ) {
2014-09-13 19:51:58 +02:00
tomix = dataconverted ;
2014-11-11 20:07:21 +01:00
lengthloaded = cvt . len_cvt ;
2014-09-13 19:51:58 +02:00
} else {
break ;
2014-09-06 03:29:22 +02:00
}
} else {
2014-09-13 19:51:58 +02:00
tomix = datastream ;
lengthloaded = readfromstream ;
}
bool effectbufferloaded = false ;
2014-10-07 20:16:17 +02:00
if ( rate ! = 1 & & format . format = = AUDIO_S16SYS ) {
2014-11-11 20:07:21 +01:00
int in_len = ( int ) ( ( double ) lengthloaded / samplesize ) ;
int out_len = samples ;
2014-10-13 02:29:43 +02:00
if ( ! channel . resampler ) {
channel . resampler = speex_resampler_init ( format . channels , format . freq , format . freq , 0 , 0 ) ;
}
2014-11-11 20:07:21 +01:00
if ( readfromstream = = toread ) {
// use buffer lengths for conversion ratio so that it fits exactly
speex_resampler_set_rate ( channel . resampler , in_len , samples - samplesloaded ) ;
} else {
// reached end of stream so we cant use buffer length as resampling ratio
speex_resampler_set_rate ( channel . resampler , format . freq , ( int ) ( format . freq * ( 1 / rate ) ) ) ;
}
2014-09-13 19:51:58 +02:00
speex_resampler_process_interleaved_int ( channel . resampler , ( const spx_int16_t * ) tomix , ( spx_uint32_t * ) & in_len , ( spx_int16_t * ) effectbuffer , ( spx_uint32_t * ) & out_len ) ;
effectbufferloaded = true ;
tomix = effectbuffer ;
lengthloaded = ( out_len * samplesize ) ;
}
if ( channel . pan ! = 0.5f & & format . channels = = 2 ) {
if ( ! effectbufferloaded ) {
memcpy ( effectbuffer , tomix , lengthloaded ) ;
effectbufferloaded = true ;
tomix = effectbuffer ;
2014-09-06 03:29:22 +02:00
}
2014-09-13 19:51:58 +02:00
switch ( format . format ) {
case AUDIO_S16SYS :
EffectPanS16 ( channel , ( sint16 * ) effectbuffer , lengthloaded / samplesize ) ;
break ;
case AUDIO_U8 :
EffectPanU8 ( channel , ( uint8 * ) effectbuffer , lengthloaded / samplesize ) ;
2014-10-07 20:16:17 +02:00
break ;
2014-09-13 19:51:58 +02:00
}
}
int mixlength = lengthloaded ;
if ( loaded + mixlength > length ) {
mixlength = length - loaded ;
}
2015-10-17 19:46:40 +02:00
float volumeadjust = volume ;
volumeadjust * = ( gConfigSound . master_volume / 100.0f ) ;
2015-06-04 22:29:21 +02:00
if ( channel . group = = MIXER_GROUP_MUSIC ) {
volumeadjust * = ( gConfigSound . music_volume / 100.0f ) ;
}
int startvolume = ( int ) ( channel . oldvolume * volumeadjust ) ;
int endvolume = ( int ) ( channel . volume * volumeadjust ) ;
2014-10-13 02:29:43 +02:00
if ( channel . stopping ) {
endvolume = 0 ;
}
2015-06-04 22:29:21 +02:00
int mixvolume = ( int ) ( channel . volume * volumeadjust ) ;
2014-10-13 02:29:43 +02:00
if ( startvolume ! = endvolume ) {
// fade between volume levels to smooth out sound and minimize clicks from sudden volume changes
if ( ! effectbufferloaded ) {
memcpy ( effectbuffer , tomix , lengthloaded ) ;
effectbufferloaded = true ;
tomix = effectbuffer ;
}
2014-10-16 04:02:56 +02:00
mixvolume = SDL_MIX_MAXVOLUME ; // set to max since we are adjusting the volume ourselves
2014-10-13 02:29:43 +02:00
int fadelength = mixlength / format . BytesPerSample ( ) ;
switch ( format . format ) {
case AUDIO_S16SYS :
EffectFadeS16 ( ( sint16 * ) effectbuffer , fadelength , startvolume , endvolume ) ;
break ;
case AUDIO_U8 :
EffectFadeU8 ( ( uint8 * ) effectbuffer , fadelength , startvolume , endvolume ) ;
break ;
}
}
2014-10-16 04:02:56 +02:00
SDL_MixAudioFormat ( & data [ loaded ] , tomix , format . format , mixlength , mixvolume ) ;
2014-09-13 19:51:58 +02:00
if ( dataconverted ) {
delete [ ] dataconverted ;
2014-09-06 03:29:22 +02:00
}
channel . offset + = readfromstream ;
}
loaded + = lengthloaded ;
2015-04-25 16:55:12 +02:00
if ( channel . loop ! = 0 & & channel . offset > = channel . source - > Length ( ) ) {
2014-09-06 03:29:22 +02:00
if ( channel . loop ! = - 1 ) {
channel . loop - - ;
}
channel . offset = 0 ;
}
2014-10-13 02:29:43 +02:00
} while ( loaded < length & & channel . loop ! = 0 & & ! channel . stopping ) ;
channel . oldvolume = channel . volume ;
2015-04-30 01:29:37 +02:00
channel . oldvolume_l = channel . volume_l ;
channel . oldvolume_r = channel . volume_r ;
2015-04-25 16:55:12 +02:00
if ( channel . loop = = 0 & & channel . offset > = channel . source - > Length ( ) ) {
2014-10-08 07:00:11 +02:00
channel . done = true ;
}
2014-09-06 03:29:22 +02:00
}
}
2014-09-13 19:51:58 +02:00
void Mixer : : EffectPanS16 ( Channel & channel , sint16 * data , int length )
{
for ( int i = 0 ; i < length * 2 ; i + = 2 ) {
2015-04-30 01:29:37 +02:00
float t = ( float ) i / ( length * 2 ) ;
data [ i ] = ( sint16 ) ( data [ i ] * ( ( 1.0 - t ) * channel . oldvolume_l + t * channel . volume_l ) ) ;
data [ i + 1 ] = ( sint16 ) ( data [ i + 1 ] * ( ( 1.0 - t ) * channel . oldvolume_r + t * channel . volume_r ) ) ;
2014-09-13 19:51:58 +02:00
}
}
void Mixer : : EffectPanU8 ( Channel & channel , uint8 * data , int length )
{
for ( int i = 0 ; i < length * 2 ; i + = 2 ) {
2015-04-30 01:29:37 +02:00
float t = ( float ) i / ( length * 2 ) ;
data [ i ] = ( uint8 ) ( data [ i ] * ( ( 1.0 - t ) * channel . oldvolume_l + t * channel . volume_l ) ) ;
data [ i + 1 ] = ( uint8 ) ( data [ i + 1 ] * ( ( 1.0 - t ) * channel . oldvolume_r + t * channel . volume_r ) ) ;
2014-09-13 19:51:58 +02:00
}
}
2014-10-13 02:29:43 +02:00
void Mixer : : EffectFadeS16 ( sint16 * data , int length , int startvolume , int endvolume )
{
float startvolume_f = ( float ) startvolume / SDL_MIX_MAXVOLUME ;
float endvolume_f = ( float ) endvolume / SDL_MIX_MAXVOLUME ;
for ( int i = 0 ; i < length ; i + + ) {
float t = ( float ) i / length ;
data [ i ] = ( sint16 ) ( data [ i ] * ( ( 1 - t ) * startvolume_f + t * endvolume_f ) ) ;
}
}
void Mixer : : EffectFadeU8 ( uint8 * data , int length , int startvolume , int endvolume )
{
float startvolume_f = ( float ) startvolume / SDL_MIX_MAXVOLUME ;
float endvolume_f = ( float ) endvolume / SDL_MIX_MAXVOLUME ;
for ( int i = 0 ; i < length ; i + + ) {
float t = ( float ) i / length ;
data [ i ] = ( uint8 ) ( data [ i ] * ( ( 1 - t ) * startvolume_f + t * endvolume_f ) ) ;
}
}
2015-04-25 16:55:12 +02:00
bool Mixer : : MustConvert ( Source & source )
2014-09-06 03:29:22 +02:00
{
2015-04-25 16:55:12 +02:00
const AudioFormat sourceformat = source . Format ( ) ;
if ( sourceformat . format ! = format . format | | sourceformat . channels ! = format . channels | | sourceformat . freq ! = format . freq ) {
2014-09-06 03:29:22 +02:00
return true ;
}
return false ;
}
2014-09-13 19:51:58 +02:00
bool Mixer : : Convert ( SDL_AudioCVT & cvt , const uint8 * data , unsigned long length , uint8 * * dataout )
2014-09-06 03:29:22 +02:00
{
if ( length = = 0 | | cvt . len_mult = = 0 ) {
return false ;
}
cvt . len = length ;
2014-09-13 19:51:58 +02:00
cvt . buf = ( Uint8 * ) new uint8 [ cvt . len * cvt . len_mult ] ;
2014-09-06 03:29:22 +02:00
memcpy ( cvt . buf , data , length ) ;
if ( SDL_ConvertAudio ( & cvt ) < 0 ) {
delete [ ] cvt . buf ;
return false ;
}
* dataout = cvt . buf ;
return true ;
}
2014-09-13 19:51:58 +02:00
void Mixer_Init ( const char * device )
2014-09-06 03:29:22 +02:00
{
2014-09-13 19:51:58 +02:00
gMixer . Init ( device ) ;
2014-10-07 04:21:55 +02:00
}
2014-10-08 07:00:11 +02:00
void * Mixer_Play_Effect ( int id , int loop , int volume , float pan , double rate , int deleteondone )
2014-10-07 04:21:55 +02:00
{
2015-07-19 01:57:42 +02:00
if ( ! gConfigSound . sound ) {
return 0 ;
}
2015-04-25 16:55:12 +02:00
if ( id > = countof ( gMixer . css1sources ) ) {
2014-10-07 04:21:55 +02:00
return 0 ;
}
gMixer . Lock ( ) ;
2015-04-25 16:55:12 +02:00
Channel * channel = gMixer . Play ( * gMixer . css1sources [ id ] , loop , deleteondone ! = 0 , false ) ;
2014-10-07 04:21:55 +02:00
if ( channel ) {
channel - > SetVolume ( volume ) ;
channel - > SetPan ( pan ) ;
channel - > SetRate ( rate ) ;
}
gMixer . Unlock ( ) ;
return channel ;
}
void Mixer_Stop_Channel ( void * channel )
{
gMixer . Stop ( * ( Channel * ) channel ) ;
}
void Mixer_Channel_Volume ( void * channel , int volume )
{
2014-10-07 20:16:17 +02:00
gMixer . Lock ( ) ;
2014-10-07 04:21:55 +02:00
( ( Channel * ) channel ) - > SetVolume ( volume ) ;
2014-10-07 20:16:17 +02:00
gMixer . Unlock ( ) ;
2014-10-07 04:21:55 +02:00
}
void Mixer_Channel_Pan ( void * channel , float pan )
{
2014-10-07 20:16:17 +02:00
gMixer . Lock ( ) ;
2014-10-07 04:21:55 +02:00
( ( Channel * ) channel ) - > SetPan ( pan ) ;
2014-10-07 20:16:17 +02:00
gMixer . Unlock ( ) ;
2014-10-07 04:21:55 +02:00
}
void Mixer_Channel_Rate ( void * channel , double rate )
{
2014-10-07 20:16:17 +02:00
gMixer . Lock ( ) ;
2014-10-07 04:21:55 +02:00
( ( Channel * ) channel ) - > SetRate ( rate ) ;
2014-10-07 20:16:17 +02:00
gMixer . Unlock ( ) ;
2014-10-08 07:00:11 +02:00
}
int Mixer_Channel_IsPlaying ( void * channel )
{
return ( ( Channel * ) channel ) - > IsPlaying ( ) ;
}
2014-11-11 20:07:21 +01:00
unsigned long Mixer_Channel_GetOffset ( void * channel )
{
return ( ( Channel * ) channel ) - > GetOffset ( ) ;
}
int Mixer_Channel_SetOffset ( void * channel , unsigned long offset )
{
return ( ( Channel * ) channel ) - > SetOffset ( offset ) ;
}
2015-06-04 22:29:21 +02:00
void Mixer_Channel_SetGroup ( void * channel , int group )
{
( ( Channel * ) channel ) - > SetGroup ( group ) ;
}
2015-05-21 01:52:35 +02:00
void * Mixer_Play_Music ( int pathid , int loop , int streaming )
2014-10-08 07:00:11 +02:00
{
2015-07-19 01:57:42 +02:00
if ( ! gConfigSound . sound ) {
return 0 ;
}
2015-04-25 16:55:12 +02:00
if ( streaming ) {
2015-08-29 14:12:52 +02:00
const utf8 * filename = get_file_path ( pathid ) ;
2015-04-25 16:55:12 +02:00
2015-08-29 14:12:52 +02:00
SDL_RWops * rw = SDL_RWFromFile ( filename , " rb " ) ;
2015-04-25 16:55:12 +02:00
if ( rw = = NULL ) {
return 0 ;
}
Source_SampleStream * source_samplestream = new Source_SampleStream ;
if ( source_samplestream - > LoadWAV ( rw ) ) {
2015-05-21 01:52:35 +02:00
Channel * channel = gMixer . Play ( * source_samplestream , loop , false , true ) ;
2015-04-25 16:55:12 +02:00
if ( ! channel ) {
delete source_samplestream ;
2015-06-05 09:00:10 +02:00
} else {
channel - > SetGroup ( MIXER_GROUP_MUSIC ) ;
2015-04-25 16:55:12 +02:00
}
return channel ;
} else {
delete source_samplestream ;
return 0 ;
}
} else {
if ( gMixer . LoadMusic ( pathid ) ) {
2015-06-04 22:29:21 +02:00
Channel * channel = gMixer . Play ( * gMixer . musicsources [ pathid ] , MIXER_LOOP_INFINITE , false , false ) ;
if ( channel ) {
channel - > SetGroup ( MIXER_GROUP_MUSIC ) ;
}
return channel ;
2015-04-25 16:55:12 +02:00
}
2014-10-08 07:00:11 +02:00
}
2014-10-08 20:15:16 +02:00
return 0 ;
2015-08-04 01:08:42 +02:00
}
2015-10-17 19:46:40 +02:00
void Mixer_SetVolume ( float volume )
{
gMixer . SetVolume ( volume ) ;
}