| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604 | /* -*- mode: c; tab-width: 4; c-basic-offset: 3; c-file-style: "linux" -*- *///// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.// All rights reserved.//// This file is part of SDLPAL.//// SDLPAL 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.//// 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.//// You should have received a copy of the GNU General Public License// along with this program.  If not, see <http://www.gnu.org/licenses/>.//#include "palcommon.h"#include "global.h"#include "palcfg.h"#include "audio.h"#include "players.h"#include "util.h"#include "resampler.h"#include "midi.h"#include <math.h>#if PAL_HAS_OGG#include <vorbis/codec.h>#endif#define PAL_CDTRACK_BASE    10000typedef void(*ResampleMixFunction)(void *, const void *, int, void *, int, int, uint8_t);typedef struct tagAUDIODEVICE{   SDL_AudioSpec             spec;		/* Actual-used sound specification */   SDL_AudioCVT              cvt;		/* Audio format conversion parameter */   AUDIOPLAYER              *pMusPlayer;   AUDIOPLAYER              *pCDPlayer;#if PAL_HAS_SDLCD   SDL_CD                   *pCD;#endif   AUDIOPLAYER              *pSoundPlayer;   void                     *pSoundBuffer;	/* The output buffer for sound */   INT                       iMusicVolume;	/* The BGM volume ranged in [0, 128] for better performance */   INT                       iSoundVolume;	/* The sound effect volume ranged in [0, 128] for better performance */   BOOL                      fMusicEnabled; /* Is BGM enabled? */   BOOL                      fSoundEnabled; /* Is sound effect enabled? */   BOOL                      fOpened;       /* Is the audio device opened? */} AUDIODEVICE;static AUDIODEVICE gAudioDevice;PAL_FORCE_INLINEvoidAUDIO_MixNative(	short     *dst,	short     *src,	int        samples){	while (samples > 0)	{		int val = *src++ + *dst;		if (val > SHRT_MAX)			*dst++ = SHRT_MAX;		else if (val < SHRT_MIN)			*dst++ = SHRT_MIN;		else			*dst++ = (short)val;		samples--;	}}PAL_FORCE_INLINEvoidAUDIO_AdjustVolume(	short     *srcdst,	int        iVolume,	int        samples){	if (iVolume == SDL_MIX_MAXVOLUME) return;	if (iVolume == 0) { memset(srcdst, 0, samples << 1); return; }	while (samples > 0)	{		*srcdst = *srcdst * iVolume / SDL_MIX_MAXVOLUME;		samples--; srcdst++;	}}static VOID SDLCALLAUDIO_FillBuffer(   LPVOID          udata,   LPBYTE          stream,   INT             len)/*++  Purpose:    SDL sound callback function.  Parameters:    [IN]  udata - pointer to user-defined parameters (Not used).    [OUT] stream - pointer to the stream buffer.    [IN]  len - Length of the buffer.  Return value:    None.--*/{#if SDL_VERSION_ATLEAST(2,0,0)   memset(stream, 0, len);#endif   gAudioDevice.cvt.buf = stream;   gAudioDevice.cvt.len = len;   //   // Play music   //   if (gAudioDevice.fMusicEnabled && gAudioDevice.iMusicVolume > 0)   {      if (gAudioDevice.pMusPlayer)      {         gAudioDevice.pMusPlayer->FillBuffer(gAudioDevice.pMusPlayer, stream, len);      }      if (gAudioDevice.pCDPlayer)      {         gAudioDevice.pCDPlayer->FillBuffer(gAudioDevice.pCDPlayer, stream, len);      }      //      // Adjust volume for music      //      AUDIO_AdjustVolume((short *)stream, gAudioDevice.iMusicVolume, len >> 1);   }   //   // Play sound   //   if (gAudioDevice.fSoundEnabled && gAudioDevice.pSoundPlayer && gAudioDevice.iSoundVolume > 0)   {	   memset(gAudioDevice.pSoundBuffer, 0, len);	   gAudioDevice.pSoundPlayer->FillBuffer(gAudioDevice.pSoundPlayer, gAudioDevice.pSoundBuffer, len);	   //	   // Adjust volume for sound	   //	   AUDIO_AdjustVolume((short *)gAudioDevice.pSoundBuffer, gAudioDevice.iSoundVolume, len >> 1);	   //	   // Mix sound & music	   //	   AUDIO_MixNative((short *)stream, gAudioDevice.pSoundBuffer, len >> 1);   }   //   // Convert audio from native byte-order to actual byte-order   //   SDL_ConvertAudio(&gAudioDevice.cvt);}INTAUDIO_OpenDevice(   VOID)/*++  Purpose:    Initialize the audio subsystem.  Parameters:    None.  Return value:    0 if succeed, others if failed.--*/{   SDL_AudioSpec spec;   if (gAudioDevice.fOpened)   {      //      // Already opened      //      return -1;   }#ifdef __EMSCRIPTEN__ // Now either music/sound enabled will makes whole app crash in emscripten. Disabled until a solution is found.   return -1;#endif   gAudioDevice.fOpened = FALSE;   gAudioDevice.fMusicEnabled = TRUE;   gAudioDevice.fSoundEnabled = TRUE;   gAudioDevice.iMusicVolume = gConfig.iMusicVolume * SDL_MIX_MAXVOLUME / PAL_MAX_VOLUME;   gAudioDevice.iSoundVolume = gConfig.iSoundVolume * SDL_MIX_MAXVOLUME / PAL_MAX_VOLUME;   //   // Initialize the resampler module   //   resampler_init();   //   // Open the audio device.   //   gAudioDevice.spec.freq = gConfig.iSampleRate;   gAudioDevice.spec.format = AUDIO_S16;   gAudioDevice.spec.channels = gConfig.iAudioChannels;   gAudioDevice.spec.samples = gConfig.wAudioBufferSize;   gAudioDevice.spec.callback = AUDIO_FillBuffer;   if (SDL_OpenAudio(&gAudioDevice.spec, &spec) < 0)   {      //      // Failed      //      return -3;   }   else   {      gAudioDevice.spec = spec;      gAudioDevice.pSoundBuffer = malloc(spec.size);   }   SDL_BuildAudioCVT(&gAudioDevice.cvt, AUDIO_S16SYS, spec.channels, spec.freq, spec.format, spec.channels, spec.freq);   gAudioDevice.fOpened = TRUE;   //   // Initialize the sound subsystem.   //   gAudioDevice.pSoundPlayer = SOUND_Init();   //   // Initialize the music subsystem.   //   switch (gConfig.eMusicType)   {   case MUSIC_RIX:	   if (!(gAudioDevice.pMusPlayer = RIX_Init(va("%s%s", gConfig.pszGamePath, "mus.mkf"))))	   {		   gAudioDevice.pMusPlayer = RIX_Init(va("%s%s", gConfig.pszGamePath, "MUS.MKF"));	   }	   break;   case MUSIC_MP3:#if PAL_HAS_MP3	   gAudioDevice.pMusPlayer = MP3_Init();#else	   gAudioDevice.pMusPlayer = NULL;#endif	   break;   case MUSIC_OGG:#if PAL_HAS_OGG	   gAudioDevice.pMusPlayer = OGG_Init();#else	   gAudioDevice.pMusPlayer = NULL;#endif	   break;   case MUSIC_MIDI:	   gAudioDevice.pMusPlayer = NULL;	   break;   }   //   // Initialize the CD audio.   //   switch (gConfig.eCDType)   {   case MUSIC_SDLCD:   {#if PAL_HAS_SDLCD	   int i;	   gAudioDevice.pCD = NULL;	   for (i = 0; i < SDL_CDNumDrives(); i++)	   {		   gAudioDevice.pCD = SDL_CDOpen(i);		   if (gAudioDevice.pCD != NULL)		   {			   if (!CD_INDRIVE(SDL_CDStatus(gAudioDevice.pCD)))			   {				   SDL_CDClose(gAudioDevice.pCD);				   gAudioDevice.pCD = NULL;			   }			   else			   {				   break;			   }		   }	   }#endif	   gAudioDevice.pCDPlayer = NULL;	   break;   }   case MUSIC_MP3:#if PAL_HAS_MP3	   gAudioDevice.pCDPlayer = MP3_Init();#else	   gAudioDevice.pCDPlayer = NULL;#endif	   break;   case MUSIC_OGG:#if PAL_HAS_OGG	   gAudioDevice.pCDPlayer = OGG_Init();#else	   gAudioDevice.pCDPlayer = NULL;#endif	   break;   }   //   // Let the callback function run so that musics will be played.   //   SDL_PauseAudio(0);   return 0;}VOIDAUDIO_CloseDevice(   VOID)/*++  Purpose:    Close the audio subsystem.  Parameters:    None.  Return value:    None.--*/{   SDL_CloseAudio();   if (gAudioDevice.pSoundPlayer != NULL)   {      gAudioDevice.pSoundPlayer->Shutdown(gAudioDevice.pSoundPlayer);      gAudioDevice.pSoundPlayer = NULL;   }   if (gAudioDevice.pMusPlayer)   {	   gAudioDevice.pMusPlayer->Shutdown(gAudioDevice.pMusPlayer);	   gAudioDevice.pMusPlayer = NULL;   }   if (gAudioDevice.pCDPlayer)   {	   gAudioDevice.pCDPlayer->Shutdown(gAudioDevice.pCDPlayer);	   gAudioDevice.pCDPlayer = NULL;   }#if PAL_HAS_SDLCD   if (gAudioDevice.pCD != NULL)   {      AUDIO_PlayCDTrack(-1);      SDL_CDClose(gAudioDevice.pCD);   }#endif   if (gAudioDevice.pSoundBuffer != NULL)   {      free(gAudioDevice.pSoundBuffer);	  gAudioDevice.pSoundBuffer = NULL;   }#if PAL_HAS_NATIVEMIDI   if (gConfig.eMusicType == MUSIC_MIDI) MIDI_Play(0, FALSE);#endif}SDL_AudioSpec*AUDIO_GetDeviceSpec(	VOID){	return &gAudioDevice.spec;}static INTAUDIO_ChangeVolumeByValue(   INT   *iVolume,   INT    iValue){   *iVolume += iValue;   if (*iVolume > PAL_MAX_VOLUME)      *iVolume = PAL_MAX_VOLUME;   else if (*iVolume < 0)      *iVolume = 0;   return *iVolume;}VOIDAUDIO_IncreaseVolume(   VOID)/*++  Purpose:    Increase global volume by 3%.  Parameters:    None.  Return value:    None.--*/{   AUDIO_ChangeVolumeByValue(&gConfig.iMusicVolume, 3);   AUDIO_ChangeVolumeByValue(&gConfig.iSoundVolume, 3);   gAudioDevice.iMusicVolume = gConfig.iMusicVolume * SDL_MIX_MAXVOLUME / PAL_MAX_VOLUME;   gAudioDevice.iSoundVolume = gConfig.iSoundVolume * SDL_MIX_MAXVOLUME / PAL_MAX_VOLUME;}VOIDAUDIO_DecreaseVolume(   VOID)/*++  Purpose:    Decrease global volume by 3%.  Parameters:    None.  Return value:    None.--*/{   AUDIO_ChangeVolumeByValue(&gConfig.iMusicVolume, -3);   AUDIO_ChangeVolumeByValue(&gConfig.iSoundVolume, -3);   gAudioDevice.iMusicVolume = gConfig.iMusicVolume * SDL_MIX_MAXVOLUME / PAL_MAX_VOLUME;   gAudioDevice.iSoundVolume = gConfig.iSoundVolume * SDL_MIX_MAXVOLUME / PAL_MAX_VOLUME;}VOIDAUDIO_PlaySound(   INT    iSoundNum)/*++  Purpose:    Play a sound in voc.mkf/sounds.mkf file.  Parameters:    [IN]  iSoundNum - number of the sound; the absolute value is used.  Return value:    None.--*/{   // Unlike musics that use the 'load as required' strategy, sound player   // load the entire sound file at once, which may cause about 0.5s or longer   // latency for large sound files. To prevent this latency affects audio playing,   // the mutex lock is obtained inside the SOUND_Play function rather than here.   if (gAudioDevice.pSoundPlayer)   {      gAudioDevice.pSoundPlayer->Play(gAudioDevice.pSoundPlayer, abs(iSoundNum), FALSE, 0.0f);   }}VOIDAUDIO_PlayMusic(   INT       iNumRIX,   BOOL      fLoop,   FLOAT     flFadeTime){#if PAL_HAS_NATIVEMIDI   if (gConfig.eMusicType == MUSIC_MIDI)   {      MIDI_Play(iNumRIX, fLoop);      return;   }#endif   SDL_LockAudio();   if (gAudioDevice.pMusPlayer)   {      gAudioDevice.pMusPlayer->Play(gAudioDevice.pMusPlayer, iNumRIX, fLoop, flFadeTime);   }   SDL_UnlockAudio();}BOOLAUDIO_PlayCDTrack(   INT    iNumTrack)/*++  Purpose:    Play a CD Audio Track.  Parameters:    [IN]  iNumTrack - number of the CD Audio Track.  Return value:    TRUE if the track can be played, FALSE if not.--*/{	BOOL ret = FALSE;#if PAL_HAS_SDLCD   if (gAudioDevice.pCD != NULL)   {      if (CD_INDRIVE(SDL_CDStatus(gAudioDevice.pCD)))      {         SDL_CDStop(gAudioDevice.pCD);         if (iNumTrack != -1)         {            AUDIO_PlayMusic(-1, FALSE, 0);            if (SDL_CDPlayTracks(gAudioDevice.pCD, iNumTrack - 1, 0, 1, 0) == 0)            {               return TRUE;            }         }      }   }#endif   SDL_LockAudio();   if (gAudioDevice.pCDPlayer)   {	   if (iNumTrack != -1)	   {		   AUDIO_PlayMusic(-1, FALSE, 0);		   ret = gAudioDevice.pCDPlayer->Play(gAudioDevice.pCDPlayer, PAL_CDTRACK_BASE + iNumTrack, TRUE, 0);	   }	   else	   {		   ret = gAudioDevice.pCDPlayer->Play(gAudioDevice.pCDPlayer, -1, FALSE, 0);	   }   }   SDL_UnlockAudio();   return ret;}VOIDAUDIO_EnableMusic(   BOOL   fEnable){   gAudioDevice.fMusicEnabled = fEnable;}BOOLAUDIO_MusicEnabled(   VOID){   return gAudioDevice.fMusicEnabled;}VOIDAUDIO_EnableSound(   BOOL   fEnable){	gAudioDevice.fSoundEnabled = fEnable;}BOOLAUDIO_SoundEnabled(   VOID){   return gAudioDevice.fSoundEnabled;}
 |