296 lines
6.0 KiB
C++
296 lines
6.0 KiB
C++
/* FCE Ultra - NES/Famicom Emulator
|
|
*
|
|
* Copyright notice for this file:
|
|
* Copyright (C) 2002 Xodnizel
|
|
*
|
|
* This program 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 2 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, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/// \file
|
|
/// \brief Handles sound emulation using the SDL.
|
|
|
|
#include "sdl.h"
|
|
|
|
#include "../common/configSys.h"
|
|
#include "../../utils/memory.h"
|
|
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <cstdlib>
|
|
|
|
extern Config *g_config;
|
|
|
|
static volatile int *s_Buffer = 0;
|
|
static unsigned int s_BufferSize;
|
|
static unsigned int s_BufferRead;
|
|
static unsigned int s_BufferWrite;
|
|
static volatile unsigned int s_BufferIn;
|
|
|
|
static int s_mute = 0;
|
|
|
|
|
|
/**
|
|
* Callback from the SDL to get and play audio data.
|
|
*/
|
|
static void
|
|
fillaudio(void *udata,
|
|
uint8 *stream,
|
|
int len)
|
|
{
|
|
static int16_t sample = 0;
|
|
int16 *tmps = (int16*)stream;
|
|
len >>= 1;
|
|
while (len)
|
|
{
|
|
if (s_BufferIn)
|
|
{
|
|
sample = s_Buffer[s_BufferRead];
|
|
s_BufferRead = (s_BufferRead + 1) % s_BufferSize;
|
|
s_BufferIn--;
|
|
} else {
|
|
// Retain last known sample value, helps avoid clicking
|
|
// noise when sound system is starved of audio data.
|
|
//sample = 0;
|
|
//bufStarveDetected = 1;
|
|
}
|
|
|
|
*tmps = sample;
|
|
tmps++;
|
|
len--;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize the audio subsystem.
|
|
*/
|
|
int
|
|
InitSound()
|
|
{
|
|
int sound, soundrate, soundbufsize, soundvolume, soundtrianglevolume, soundsquare1volume, soundsquare2volume, soundnoisevolume, soundpcmvolume, soundq;
|
|
SDL_AudioSpec spec;
|
|
const char *driverName;
|
|
|
|
g_config->getOption("SDL.Sound", &sound);
|
|
if (!sound)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
memset(&spec, 0, sizeof(spec));
|
|
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
|
|
{
|
|
puts(SDL_GetError());
|
|
KillSound();
|
|
return 0;
|
|
}
|
|
|
|
// load configuration variables
|
|
g_config->getOption("SDL.Sound.Rate", &soundrate);
|
|
g_config->getOption("SDL.Sound.BufSize", &soundbufsize);
|
|
g_config->getOption("SDL.Sound.Volume", &soundvolume);
|
|
g_config->getOption("SDL.Sound.Quality", &soundq);
|
|
g_config->getOption("SDL.Sound.TriangleVolume", &soundtrianglevolume);
|
|
g_config->getOption("SDL.Sound.Square1Volume", &soundsquare1volume);
|
|
g_config->getOption("SDL.Sound.Square2Volume", &soundsquare2volume);
|
|
g_config->getOption("SDL.Sound.NoiseVolume", &soundnoisevolume);
|
|
g_config->getOption("SDL.Sound.PCMVolume", &soundpcmvolume);
|
|
|
|
spec.freq = soundrate;
|
|
spec.format = AUDIO_S16SYS;
|
|
spec.channels = 1;
|
|
spec.samples = 512;
|
|
spec.callback = fillaudio;
|
|
spec.userdata = 0;
|
|
|
|
s_BufferSize = soundbufsize * soundrate / 1000;
|
|
|
|
// For safety, set a bare minimum:
|
|
if (s_BufferSize < spec.samples * 2)
|
|
{
|
|
s_BufferSize = spec.samples * 2;
|
|
}
|
|
|
|
s_Buffer = (int *)FCEU_dmalloc(sizeof(int) * s_BufferSize);
|
|
|
|
if (!s_Buffer)
|
|
{
|
|
return 0;
|
|
}
|
|
s_BufferRead = s_BufferWrite = s_BufferIn = 0;
|
|
|
|
if (SDL_OpenAudio(&spec, 0) < 0)
|
|
{
|
|
puts(SDL_GetError());
|
|
KillSound();
|
|
return 0;
|
|
}
|
|
SDL_PauseAudio(0);
|
|
|
|
driverName = SDL_GetCurrentAudioDriver();
|
|
|
|
if ( driverName )
|
|
{
|
|
fprintf(stderr, "Loading SDL sound with %s driver...\n", driverName);
|
|
}
|
|
|
|
FCEUI_SetSoundVolume(soundvolume);
|
|
FCEUI_SetSoundQuality(soundq);
|
|
FCEUI_Sound(soundrate);
|
|
FCEUI_SetTriangleVolume(soundtrianglevolume);
|
|
FCEUI_SetSquare1Volume(soundsquare1volume);
|
|
FCEUI_SetSquare2Volume(soundsquare2volume);
|
|
FCEUI_SetNoiseVolume(soundnoisevolume);
|
|
FCEUI_SetPCMVolume(soundpcmvolume);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the size of the audio buffer.
|
|
*/
|
|
uint32
|
|
GetMaxSound(void)
|
|
{
|
|
return(s_BufferSize);
|
|
}
|
|
|
|
/**
|
|
* Returns the amount of free space in the audio buffer.
|
|
*/
|
|
uint32
|
|
GetWriteSound(void)
|
|
{
|
|
return(s_BufferSize - s_BufferIn);
|
|
}
|
|
|
|
/**
|
|
* Send a sound clip to the audio subsystem.
|
|
*/
|
|
void
|
|
WriteSound(int32 *buf,
|
|
int Count)
|
|
{
|
|
extern int EmulationPaused;
|
|
if (EmulationPaused == 0)
|
|
{
|
|
int waitCount = 0;
|
|
|
|
while(Count)
|
|
{
|
|
while(s_BufferIn == s_BufferSize)
|
|
{
|
|
SDL_Delay(1); waitCount++;
|
|
|
|
if ( waitCount > 1000 )
|
|
{
|
|
printf("Error: Sound sink is not draining... Breaking out of audio loop to prevent lockup.\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
s_Buffer[s_BufferWrite] = *buf;
|
|
Count--;
|
|
s_BufferWrite = (s_BufferWrite + 1) % s_BufferSize;
|
|
|
|
SDL_LockAudio();
|
|
s_BufferIn++;
|
|
SDL_UnlockAudio();
|
|
|
|
buf++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Pause (1) or unpause (0) the audio output.
|
|
*/
|
|
void
|
|
SilenceSound(int n)
|
|
{
|
|
SDL_PauseAudio(n);
|
|
}
|
|
|
|
/**
|
|
* Shut down the audio subsystem.
|
|
*/
|
|
int
|
|
KillSound(void)
|
|
{
|
|
FCEUI_Sound(0);
|
|
SDL_CloseAudio();
|
|
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
|
if(s_Buffer) {
|
|
free((void *)s_Buffer);
|
|
s_Buffer = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Adjust the volume either down (-1), up (1), or to the default (0).
|
|
* Unmutes if mute was active before.
|
|
*/
|
|
void
|
|
FCEUD_SoundVolumeAdjust(int n)
|
|
{
|
|
int soundvolume;
|
|
g_config->getOption("SDL.Sound.Volume", &soundvolume);
|
|
|
|
switch(n) {
|
|
case -1:
|
|
soundvolume -= 10;
|
|
if(soundvolume < 0) {
|
|
soundvolume = 0;
|
|
}
|
|
break;
|
|
case 0:
|
|
soundvolume = 100;
|
|
break;
|
|
case 1:
|
|
soundvolume += 10;
|
|
if(soundvolume > 150) {
|
|
soundvolume = 150;
|
|
}
|
|
break;
|
|
}
|
|
|
|
s_mute = 0;
|
|
FCEUI_SetSoundVolume(soundvolume);
|
|
g_config->setOption("SDL.Sound.Volume", soundvolume);
|
|
|
|
FCEU_DispMessage("Sound volume %d.",0, soundvolume);
|
|
}
|
|
|
|
/**
|
|
* Toggles the sound on or off.
|
|
*/
|
|
void
|
|
FCEUD_SoundToggle(void)
|
|
{
|
|
if(s_mute) {
|
|
int soundvolume;
|
|
g_config->getOption("SDL.SoundVolume", &soundvolume);
|
|
|
|
s_mute = 0;
|
|
FCEUI_SetSoundVolume(soundvolume);
|
|
FCEU_DispMessage("Sound mute off.",0);
|
|
} else {
|
|
s_mute = 1;
|
|
FCEUI_SetSoundVolume(0);
|
|
FCEU_DispMessage("Sound mute on.",0);
|
|
}
|
|
}
|