Added support for OpenAL software audio mixing
Updated GBA sound engine to support new audiosavestate format Several improvements to audio cores Updated sample buffer length in DirectSound git-svn-id: https://svn.code.sf.net/p/vbam/code/trunk@167 a31d4220-a93d-0410-bf67-fe4944624d44
This commit is contained in:
parent
e42aaa9c96
commit
faf058870c
|
@ -31,7 +31,8 @@
|
||||||
#define SAVE_GAME_VERSION_7 7
|
#define SAVE_GAME_VERSION_7 7
|
||||||
#define SAVE_GAME_VERSION_8 8
|
#define SAVE_GAME_VERSION_8 8
|
||||||
#define SAVE_GAME_VERSION_9 9
|
#define SAVE_GAME_VERSION_9 9
|
||||||
#define SAVE_GAME_VERSION SAVE_GAME_VERSION_9
|
#define SAVE_GAME_VERSION_10 10
|
||||||
|
#define SAVE_GAME_VERSION SAVE_GAME_VERSION_10
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u8 *address;
|
u8 *address;
|
||||||
|
|
355
src/Sound.cpp
355
src/Sound.cpp
|
@ -27,40 +27,42 @@
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
#include "Port.h"
|
#include "Port.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
//#include <float.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "gb/gb_apu/Gb_Apu.h"
|
#include "gb/gb_apu/Gb_Apu.h"
|
||||||
#include "gb/gb_apu/Multi_Buffer.h"
|
#include "gb/gb_apu/Multi_Buffer.h"
|
||||||
|
|
||||||
#define USE_TICKS_AS 380
|
|
||||||
|
|
||||||
extern bool stopState; // TODO: silence sound when true
|
extern bool stopState; // TODO: silence sound when true
|
||||||
|
|
||||||
int soundQuality = 2; // sample rate = 44100 / soundQuality
|
int soundQuality = 2;
|
||||||
int soundInterpolation = 0; // 1 if PCM should have low-pass filtering
|
int soundInterpolation = 0;
|
||||||
int soundVolume = 0; // emulator volume code (not linear)
|
float soundFiltering = 1;
|
||||||
|
static float soundFiltering_;
|
||||||
|
int soundVolume = 0;
|
||||||
static int soundVolume_;
|
static int soundVolume_;
|
||||||
bool soundEcho = false;
|
bool soundEcho = false;
|
||||||
bool soundLowPass = false;
|
|
||||||
bool soundReverse = false;
|
|
||||||
int soundEnableFlag = 0x3ff; // emulator channels enabled
|
int soundEnableFlag = 0x3ff; // emulator channels enabled
|
||||||
|
|
||||||
// Number of 16.8 MHz clocks until soundTick() will be called
|
int const SOUND_CLOCK_TICKS_ = 167772;
|
||||||
int soundTicks = soundQuality * USE_TICKS_AS;
|
int SOUND_CLOCK_TICKS = SOUND_CLOCK_TICKS_;
|
||||||
|
int soundTicks = SOUND_CLOCK_TICKS_;
|
||||||
|
|
||||||
// Number of 16.8 MHz clocks between calls to soundTick()
|
u16 soundFinalWave [1470];
|
||||||
int SOUND_CLOCK_TICKS = soundQuality * USE_TICKS_AS;
|
int soundBufferLen = 1470;
|
||||||
|
|
||||||
u16 soundFinalWave [1470]; // 16-bit SIGNED stereo sample buffer
|
|
||||||
int soundBufferLen = 1470; // size of sound buffer in BYTES
|
|
||||||
int soundBufferTotalLen = 14700;
|
int soundBufferTotalLen = 14700;
|
||||||
|
|
||||||
void interp_rate() { /* empty for now */ }
|
|
||||||
|
|
||||||
// Unknown purpose
|
|
||||||
int soundDebug = 0;
|
int soundDebug = 0;
|
||||||
u32 soundNextPosition = 0;
|
u32 soundNextPosition = 0;
|
||||||
bool soundOffFlag = false;
|
bool soundOffFlag = false;
|
||||||
bool soundPaused = true;
|
bool soundPaused = true;
|
||||||
|
|
||||||
|
bool soundLowPass = false;
|
||||||
|
bool soundReverse = false;
|
||||||
|
|
||||||
|
void interp_rate() { /* empty for now */ }
|
||||||
|
|
||||||
class Gba_Pcm {
|
class Gba_Pcm {
|
||||||
public:
|
public:
|
||||||
void init();
|
void init();
|
||||||
|
@ -84,14 +86,16 @@ public:
|
||||||
void write_fifo( int data );
|
void write_fifo( int data );
|
||||||
void timer_overflowed( int which_timer );
|
void timer_overflowed( int which_timer );
|
||||||
|
|
||||||
private:
|
// public only so save state routines can access it
|
||||||
int readIndex;
|
int readIndex;
|
||||||
int count;
|
int count;
|
||||||
int writeIndex;
|
int writeIndex;
|
||||||
bool enabled;
|
|
||||||
int timer;
|
|
||||||
u8 fifo [32];
|
u8 fifo [32];
|
||||||
int dac;
|
int dac;
|
||||||
|
private:
|
||||||
|
|
||||||
|
int timer;
|
||||||
|
bool enabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
static Gba_Pcm_Fifo pcm [2];
|
static Gba_Pcm_Fifo pcm [2];
|
||||||
|
@ -157,7 +161,7 @@ void Gba_Pcm::update( int dac )
|
||||||
{
|
{
|
||||||
blip_time_t time = blip_time();
|
blip_time_t time = blip_time();
|
||||||
|
|
||||||
dac >>= shift;
|
dac = (s8) dac >> shift;
|
||||||
int delta = dac - last_amp;
|
int delta = dac - last_amp;
|
||||||
if ( delta )
|
if ( delta )
|
||||||
{
|
{
|
||||||
|
@ -193,7 +197,7 @@ void Gba_Pcm_Fifo::timer_overflowed( int which_timer )
|
||||||
CPUCheckDMA( 3, which ? 4 : 2 );
|
CPUCheckDMA( 3, which ? 4 : 2 );
|
||||||
if ( count <= 16 )
|
if ( count <= 16 )
|
||||||
{
|
{
|
||||||
// Not filled by DMA, so fill with silence
|
// Not filled by DMA, so fill with 16 bytes of silence
|
||||||
int reg = which ? FIFOB_L : FIFOA_L;
|
int reg = which ? FIFOB_L : FIFOA_L;
|
||||||
for ( int n = 4; n--; )
|
for ( int n = 4; n--; )
|
||||||
{
|
{
|
||||||
|
@ -205,7 +209,7 @@ void Gba_Pcm_Fifo::timer_overflowed( int which_timer )
|
||||||
|
|
||||||
// Read next sample from FIFO
|
// Read next sample from FIFO
|
||||||
count--;
|
count--;
|
||||||
dac = (s8) fifo [readIndex];
|
dac = fifo [readIndex];
|
||||||
readIndex = (readIndex + 1) & 31;
|
readIndex = (readIndex + 1) & 31;
|
||||||
pcm.update( dac );
|
pcm.update( dac );
|
||||||
}
|
}
|
||||||
|
@ -299,15 +303,20 @@ static void apply_volume( bool apu_only = false )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void write_SGCNT0_H( int data )
|
||||||
|
{
|
||||||
|
WRITE16LE( &ioMem [SGCNT0_H], data & 0x770F );
|
||||||
|
pcm [0].write_control( data );
|
||||||
|
pcm [1].write_control( data >> 4 );
|
||||||
|
apply_volume( true );
|
||||||
|
}
|
||||||
|
|
||||||
void soundEvent(u32 address, u16 data)
|
void soundEvent(u32 address, u16 data)
|
||||||
{
|
{
|
||||||
switch ( address )
|
switch ( address )
|
||||||
{
|
{
|
||||||
case SGCNT0_H:
|
case SGCNT0_H:
|
||||||
WRITE16LE( &ioMem[address], data );
|
write_SGCNT0_H( data );
|
||||||
pcm [0].write_control( data );
|
|
||||||
pcm [1].write_control( data >> 4 );
|
|
||||||
apply_volume( true );
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FIFOA_L:
|
case FIFOA_L:
|
||||||
|
@ -351,6 +360,9 @@ static void end_frame( blip_time_t time )
|
||||||
|
|
||||||
static void flush_samples()
|
static void flush_samples()
|
||||||
{
|
{
|
||||||
|
// soundBufferLen should have a whole number of sample pairs
|
||||||
|
assert( soundBufferLen % (2 * sizeof *soundFinalWave) == 0 );
|
||||||
|
|
||||||
// number of samples in output buffer
|
// number of samples in output buffer
|
||||||
int const out_buf_size = soundBufferLen / sizeof *soundFinalWave;
|
int const out_buf_size = soundBufferLen / sizeof *soundFinalWave;
|
||||||
|
|
||||||
|
@ -368,6 +380,22 @@ static void flush_samples()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void apply_filtering()
|
||||||
|
{
|
||||||
|
soundFiltering_ = soundFiltering;
|
||||||
|
|
||||||
|
int const base_freq = (int) (32768 - soundFiltering_ * 16384);
|
||||||
|
int const nyquist = stereo_buffer->sample_rate() / 2;
|
||||||
|
|
||||||
|
for ( int i = 0; i < 3; i++ )
|
||||||
|
{
|
||||||
|
int cutoff = base_freq >> i;
|
||||||
|
if ( cutoff > nyquist )
|
||||||
|
cutoff = nyquist;
|
||||||
|
pcm_synth [i].treble_eq( blip_eq_t( 0, 0, stereo_buffer->sample_rate(), cutoff ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void soundTick()
|
static void soundTick()
|
||||||
{
|
{
|
||||||
if ( systemSoundOn && gb_apu && stereo_buffer )
|
if ( systemSoundOn && gb_apu && stereo_buffer )
|
||||||
|
@ -377,6 +405,9 @@ static void soundTick()
|
||||||
|
|
||||||
flush_samples();
|
flush_samples();
|
||||||
|
|
||||||
|
if ( soundFiltering_ != soundFiltering )
|
||||||
|
apply_filtering();
|
||||||
|
|
||||||
if ( soundVolume_ != soundVolume )
|
if ( soundVolume_ != soundVolume )
|
||||||
apply_volume();
|
apply_volume();
|
||||||
}
|
}
|
||||||
|
@ -386,9 +417,9 @@ void (*psoundTickfn)() = soundTick;
|
||||||
|
|
||||||
static void apply_muting()
|
static void apply_muting()
|
||||||
{
|
{
|
||||||
|
if ( !stereo_buffer || !ioMem )
|
||||||
|
return;
|
||||||
|
|
||||||
if ( !stereo_buffer || !ioMem )
|
|
||||||
return;
|
|
||||||
// PCM
|
// PCM
|
||||||
apply_control();
|
apply_control();
|
||||||
|
|
||||||
|
@ -406,8 +437,27 @@ if ( !stereo_buffer || !ioMem )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void reset_apu()
|
||||||
|
{
|
||||||
|
gb_apu->reset( gb_apu->mode_agb, true );
|
||||||
|
|
||||||
|
if ( stereo_buffer )
|
||||||
|
stereo_buffer->clear();
|
||||||
|
|
||||||
|
soundTicks = SOUND_CLOCK_TICKS;
|
||||||
|
}
|
||||||
|
|
||||||
static void remake_stereo_buffer()
|
static void remake_stereo_buffer()
|
||||||
{
|
{
|
||||||
|
if ( !ioMem )
|
||||||
|
return;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
// Direct3D might mess with FPU mode. Try uncommenting if more sound problems
|
||||||
|
// occur (also need to uncomment #include <float.h> above).
|
||||||
|
//_fpreset();
|
||||||
|
#endif
|
||||||
|
|
||||||
// Clears pointers kept to old stereo_buffer
|
// Clears pointers kept to old stereo_buffer
|
||||||
pcm [0].pcm.init();
|
pcm [0].pcm.init();
|
||||||
pcm [1].pcm.init();
|
pcm [1].pcm.init();
|
||||||
|
@ -424,17 +474,14 @@ static void remake_stereo_buffer()
|
||||||
// PCM
|
// PCM
|
||||||
pcm [0].which = 0;
|
pcm [0].which = 0;
|
||||||
pcm [1].which = 1;
|
pcm [1].which = 1;
|
||||||
for ( int i = 0; i < 3; i++ )
|
apply_filtering();
|
||||||
{
|
|
||||||
int freq = 32768 >> i;
|
|
||||||
pcm_synth [i].treble_eq( blip_eq_t( 0, 0, sample_rate, freq / 2 ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// APU
|
// APU
|
||||||
if ( !gb_apu )
|
if ( !gb_apu )
|
||||||
|
{
|
||||||
gb_apu = new Gb_Apu; // TODO: handle out of memory
|
gb_apu = new Gb_Apu; // TODO: handle out of memory
|
||||||
|
reset_apu();
|
||||||
gb_apu->reset( gb_apu->mode_agb, true );
|
}
|
||||||
|
|
||||||
apply_muting();
|
apply_muting();
|
||||||
apply_volume();
|
apply_volume();
|
||||||
|
@ -489,10 +536,11 @@ void soundReset()
|
||||||
systemSoundReset();
|
systemSoundReset();
|
||||||
|
|
||||||
remake_stereo_buffer();
|
remake_stereo_buffer();
|
||||||
|
reset_apu();
|
||||||
|
|
||||||
setsoundPaused(true);
|
setsoundPaused(true);
|
||||||
SOUND_CLOCK_TICKS = 167772;
|
SOUND_CLOCK_TICKS = SOUND_CLOCK_TICKS_;
|
||||||
soundTicks = SOUND_CLOCK_TICKS;
|
soundTicks = SOUND_CLOCK_TICKS_;
|
||||||
|
|
||||||
soundNextPosition = 0;
|
soundNextPosition = 0;
|
||||||
|
|
||||||
|
@ -533,12 +581,235 @@ void soundSetQuality(int quality)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void soundSaveGame(gzFile gzFile)
|
static int dummy_state [16];
|
||||||
|
|
||||||
|
#define SKIP( type, name ) { dummy_state, sizeof (type) }
|
||||||
|
|
||||||
|
#define LOAD( type, name ) { &name, sizeof (type) }
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
gb_apu_state_t apu;
|
||||||
|
|
||||||
|
// old state
|
||||||
|
u8 soundDSAValue;
|
||||||
|
int soundDSBValue;
|
||||||
|
} state;
|
||||||
|
|
||||||
|
// Old GBA sound state format
|
||||||
|
static variable_desc old_gba_state [] =
|
||||||
{
|
{
|
||||||
// TODO: implement
|
SKIP( int, soundPaused ),
|
||||||
|
SKIP( int, soundPlay ),
|
||||||
|
SKIP( int, soundTicks ),
|
||||||
|
SKIP( int, SOUND_CLOCK_TICKS ),
|
||||||
|
SKIP( int, soundLevel1 ),
|
||||||
|
SKIP( int, soundLevel2 ),
|
||||||
|
SKIP( int, soundBalance ),
|
||||||
|
SKIP( int, soundMasterOn ),
|
||||||
|
SKIP( int, soundIndex ),
|
||||||
|
SKIP( int, sound1On ),
|
||||||
|
SKIP( int, sound1ATL ),
|
||||||
|
SKIP( int, sound1Skip ),
|
||||||
|
SKIP( int, sound1Index ),
|
||||||
|
SKIP( int, sound1Continue ),
|
||||||
|
SKIP( int, sound1EnvelopeVolume ),
|
||||||
|
SKIP( int, sound1EnvelopeATL ),
|
||||||
|
SKIP( int, sound1EnvelopeATLReload ),
|
||||||
|
SKIP( int, sound1EnvelopeUpDown ),
|
||||||
|
SKIP( int, sound1SweepATL ),
|
||||||
|
SKIP( int, sound1SweepATLReload ),
|
||||||
|
SKIP( int, sound1SweepSteps ),
|
||||||
|
SKIP( int, sound1SweepUpDown ),
|
||||||
|
SKIP( int, sound1SweepStep ),
|
||||||
|
SKIP( int, sound2On ),
|
||||||
|
SKIP( int, sound2ATL ),
|
||||||
|
SKIP( int, sound2Skip ),
|
||||||
|
SKIP( int, sound2Index ),
|
||||||
|
SKIP( int, sound2Continue ),
|
||||||
|
SKIP( int, sound2EnvelopeVolume ),
|
||||||
|
SKIP( int, sound2EnvelopeATL ),
|
||||||
|
SKIP( int, sound2EnvelopeATLReload ),
|
||||||
|
SKIP( int, sound2EnvelopeUpDown ),
|
||||||
|
SKIP( int, sound3On ),
|
||||||
|
SKIP( int, sound3ATL ),
|
||||||
|
SKIP( int, sound3Skip ),
|
||||||
|
SKIP( int, sound3Index ),
|
||||||
|
SKIP( int, sound3Continue ),
|
||||||
|
SKIP( int, sound3OutputLevel ),
|
||||||
|
SKIP( int, sound4On ),
|
||||||
|
SKIP( int, sound4ATL ),
|
||||||
|
SKIP( int, sound4Skip ),
|
||||||
|
SKIP( int, sound4Index ),
|
||||||
|
SKIP( int, sound4Clock ),
|
||||||
|
SKIP( int, sound4ShiftRight ),
|
||||||
|
SKIP( int, sound4ShiftSkip ),
|
||||||
|
SKIP( int, sound4ShiftIndex ),
|
||||||
|
SKIP( int, sound4NSteps ),
|
||||||
|
SKIP( int, sound4CountDown ),
|
||||||
|
SKIP( int, sound4Continue ),
|
||||||
|
SKIP( int, sound4EnvelopeVolume ),
|
||||||
|
SKIP( int, sound4EnvelopeATL ),
|
||||||
|
SKIP( int, sound4EnvelopeATLReload ),
|
||||||
|
SKIP( int, sound4EnvelopeUpDown ),
|
||||||
|
LOAD( int, soundEnableFlag ),
|
||||||
|
SKIP( int, soundControl ),
|
||||||
|
LOAD( int, pcm [0].readIndex ),
|
||||||
|
LOAD( int, pcm [0].count ),
|
||||||
|
LOAD( int, pcm [0].writeIndex ),
|
||||||
|
SKIP( u8, soundDSAEnabled ), // was bool, which was one byte on MS compiler
|
||||||
|
SKIP( int, soundDSATimer ),
|
||||||
|
LOAD( u8 [32], pcm [0].fifo ),
|
||||||
|
LOAD( u8, state.soundDSAValue ),
|
||||||
|
LOAD( int, pcm [1].readIndex ),
|
||||||
|
LOAD( int, pcm [1].count ),
|
||||||
|
LOAD( int, pcm [1].writeIndex ),
|
||||||
|
SKIP( int, soundDSBEnabled ),
|
||||||
|
SKIP( int, soundDSBTimer ),
|
||||||
|
LOAD( u8 [32], pcm [1].fifo ),
|
||||||
|
LOAD( int, state.soundDSBValue ),
|
||||||
|
|
||||||
|
// skipped manually
|
||||||
|
//LOAD( int, soundBuffer[0][0], 6*735 },
|
||||||
|
//LOAD( int, soundFinalWave[0], 2*735 },
|
||||||
|
{ NULL, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
variable_desc old_gba_state2 [] =
|
||||||
|
{
|
||||||
|
LOAD( u8 [0x20], state.apu.regs [0x20] ),
|
||||||
|
SKIP( int, sound3Bank ),
|
||||||
|
SKIP( int, sound3DataSize ),
|
||||||
|
SKIP( int, sound3ForcedOutput ),
|
||||||
|
{ NULL, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
// New state format
|
||||||
|
static variable_desc gba_state [] =
|
||||||
|
{
|
||||||
|
// PCM
|
||||||
|
LOAD( int, pcm [0].readIndex ),
|
||||||
|
LOAD( int, pcm [0].count ),
|
||||||
|
LOAD( int, pcm [0].writeIndex ),
|
||||||
|
LOAD(u8[32],pcm[0].fifo ),
|
||||||
|
LOAD( int, pcm [0].dac ),
|
||||||
|
|
||||||
|
SKIP( int [4], room_for_expansion ),
|
||||||
|
|
||||||
|
LOAD( int, pcm [1].readIndex ),
|
||||||
|
LOAD( int, pcm [1].count ),
|
||||||
|
LOAD( int, pcm [1].writeIndex ),
|
||||||
|
LOAD(u8[32],pcm[1].fifo ),
|
||||||
|
LOAD( int, pcm [1].dac ),
|
||||||
|
|
||||||
|
SKIP( int [4], room_for_expansion ),
|
||||||
|
|
||||||
|
// APU
|
||||||
|
LOAD( u8 [0x40], state.apu.regs ), // last values written to registers and wave RAM (both banks)
|
||||||
|
LOAD( int, state.apu.frame_time ), // clocks until next frame sequencer action
|
||||||
|
LOAD( int, state.apu.frame_phase ), // next step frame sequencer will run
|
||||||
|
|
||||||
|
LOAD( int, state.apu.sweep_freq ), // sweep's internal frequency register
|
||||||
|
LOAD( int, state.apu.sweep_delay ), // clocks until next sweep action
|
||||||
|
LOAD( int, state.apu.sweep_enabled ),
|
||||||
|
LOAD( int, state.apu.sweep_neg ), // obscure internal flag
|
||||||
|
LOAD( int, state.apu.noise_divider ),
|
||||||
|
LOAD( int, state.apu.wave_buf ), // last read byte of wave RAM
|
||||||
|
|
||||||
|
LOAD( int [4], state.apu.delay ), // clocks until next channel action
|
||||||
|
LOAD( int [4], state.apu.length_ctr ),
|
||||||
|
LOAD( int [4], state.apu.phase ), // square/wave phase, noise LFSR
|
||||||
|
LOAD( int [4], state.apu.enabled ), // internal enabled flag
|
||||||
|
|
||||||
|
LOAD( int [3], state.apu.env_delay ), // clocks until next envelope action
|
||||||
|
LOAD( int [3], state.apu.env_volume ),
|
||||||
|
LOAD( int [3], state.apu.env_enabled ),
|
||||||
|
|
||||||
|
SKIP( int [13], room_for_expansion ),
|
||||||
|
|
||||||
|
// Emulator
|
||||||
|
LOAD( int, soundEnableFlag ),
|
||||||
|
|
||||||
|
SKIP( int [15], room_for_expansion ),
|
||||||
|
|
||||||
|
{ NULL, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reads and discards count bytes from in
|
||||||
|
static void skip_read( gzFile in, int count )
|
||||||
|
{
|
||||||
|
char buf [512];
|
||||||
|
|
||||||
|
while ( count )
|
||||||
|
{
|
||||||
|
int n = sizeof buf;
|
||||||
|
if ( n > count )
|
||||||
|
n = count;
|
||||||
|
|
||||||
|
count -= n;
|
||||||
|
utilGzRead( in, buf, n );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void soundReadGame(gzFile gzFile, int version)
|
void soundSaveGame( gzFile out )
|
||||||
{
|
{
|
||||||
// TODO: implement
|
gb_apu->save_state( &state.apu );
|
||||||
|
|
||||||
|
// Be sure areas for expansion get written as zero
|
||||||
|
memset( dummy_state, 0, sizeof dummy_state );
|
||||||
|
|
||||||
|
utilWriteData( out, gba_state );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void soundReadGameOld( gzFile in, int version )
|
||||||
|
{
|
||||||
|
// Read main data
|
||||||
|
utilReadData( in, old_gba_state );
|
||||||
|
skip_read( in, 6*735 + 2*735 );
|
||||||
|
|
||||||
|
// Copy APU regs
|
||||||
|
static int const regs_to_copy [] = {
|
||||||
|
NR10, NR11, NR12, NR13, NR14,
|
||||||
|
NR21, NR22, NR23, NR24,
|
||||||
|
NR30, NR31, NR32, NR33, NR34,
|
||||||
|
NR41, NR42, NR43, NR44,
|
||||||
|
NR50, NR51, NR52, -1
|
||||||
|
};
|
||||||
|
|
||||||
|
ioMem [NR52] |= 0x80; // old sound played even when this wasn't set (power on)
|
||||||
|
|
||||||
|
for ( int i = 0; regs_to_copy [i] >= 0; i++ )
|
||||||
|
state.apu.regs [gba_to_gb_sound( regs_to_copy [i] ) - 0xFF10] = ioMem [regs_to_copy [i]];
|
||||||
|
|
||||||
|
// Copy wave RAM to both banks
|
||||||
|
memcpy( &state.apu.regs [0x20], &ioMem [0x90], 0x10 );
|
||||||
|
memcpy( &state.apu.regs [0x30], &ioMem [0x90], 0x10 );
|
||||||
|
|
||||||
|
// Read both banks of wave RAM if available
|
||||||
|
if ( version >= SAVE_GAME_VERSION_3 )
|
||||||
|
utilReadData( in, old_gba_state2 );
|
||||||
|
|
||||||
|
// Restore PCM
|
||||||
|
pcm [0].dac = state.soundDSAValue;
|
||||||
|
pcm [1].dac = state.soundDSBValue;
|
||||||
|
|
||||||
|
int quality = utilReadInt( in ); // ignore this crap
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void soundReadGame( gzFile in, int version )
|
||||||
|
{
|
||||||
|
// Prepare APU and default state
|
||||||
|
reset_apu();
|
||||||
|
gb_apu->save_state( &state.apu );
|
||||||
|
|
||||||
|
if ( version > SAVE_GAME_VERSION_9 )
|
||||||
|
utilReadData( in, gba_state );
|
||||||
|
else
|
||||||
|
soundReadGameOld( in, version );
|
||||||
|
|
||||||
|
gb_apu->load_state( state.apu );
|
||||||
|
write_SGCNT0_H( READ16LE( &ioMem [SGCNT0_H] ) & 0x770F );
|
||||||
|
|
||||||
|
apply_muting();
|
||||||
}
|
}
|
||||||
|
|
30
src/Sound.h
30
src/Sound.h
|
@ -69,20 +69,26 @@ extern void setsystemSoundOn(bool value);
|
||||||
extern void setsoundPaused(bool value);
|
extern void setsoundPaused(bool value);
|
||||||
extern void interp_rate();
|
extern void interp_rate();
|
||||||
|
|
||||||
extern int SOUND_CLOCK_TICKS;
|
extern int SOUND_CLOCK_TICKS; // Number of 16.8 MHz clocks between calls to soundTick()
|
||||||
extern int soundTicks;
|
extern int soundTicks; // Number of 16.8 MHz clocks until soundTick() will be called
|
||||||
extern bool soundOffFlag;
|
extern int soundQuality; // sample rate = 44100 / soundQuality
|
||||||
extern bool soundPaused;
|
extern int soundBufferLen; // size of sound buffer in BYTES
|
||||||
extern int soundQuality;
|
extern u16 soundFinalWave[1470];// 16-bit SIGNED stereo sample buffer
|
||||||
extern int soundBufferLen;
|
extern int soundVolume; // emulator volume code (not linear)
|
||||||
extern int soundBufferTotalLen;
|
|
||||||
extern u32 soundNextPosition;
|
|
||||||
extern u16 soundFinalWave[1470];
|
|
||||||
extern int soundVolume;
|
|
||||||
extern int soundInterpolation;
|
|
||||||
|
|
||||||
extern bool soundEcho;
|
extern int soundInterpolation; // 1 if PCM should have low-pass filtering
|
||||||
|
extern float soundFiltering; // 0.0 = none, 1.0 = max (only if soundInterpolation!=0)
|
||||||
|
|
||||||
|
extern bool soundEcho; // enables echo for GB, not GBA
|
||||||
|
|
||||||
|
// Not used anymore
|
||||||
extern bool soundLowPass;
|
extern bool soundLowPass;
|
||||||
extern bool soundReverse;
|
extern bool soundReverse;
|
||||||
|
|
||||||
|
// Unknown purpose
|
||||||
|
extern int soundBufferTotalLen;
|
||||||
|
extern u32 soundNextPosition;
|
||||||
|
extern bool soundPaused;
|
||||||
|
extern bool soundOffFlag;
|
||||||
|
|
||||||
#endif // VBA_SOUND_H
|
#endif // VBA_SOUND_H
|
||||||
|
|
|
@ -138,6 +138,8 @@ void gbSoundTick()
|
||||||
|
|
||||||
flush_samples();
|
flush_samples();
|
||||||
|
|
||||||
|
gb_effects_config.enabled = soundEcho;
|
||||||
|
|
||||||
// Update effects config if it was changed
|
// Update effects config if it was changed
|
||||||
if ( memcmp( &gb_effects_config_current, &gb_effects_config,
|
if ( memcmp( &gb_effects_config_current, &gb_effects_config,
|
||||||
sizeof gb_effects_config ) )
|
sizeof gb_effects_config ) )
|
||||||
|
@ -145,6 +147,17 @@ void gbSoundTick()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void reset_apu()
|
||||||
|
{
|
||||||
|
// Use DMG or CGB sound differences based on type of game
|
||||||
|
gb_apu->reset( gbHardware & 1 ? gb_apu->mode_dmg : gb_apu->mode_cgb );
|
||||||
|
|
||||||
|
if ( stereo_buffer )
|
||||||
|
stereo_buffer->clear();
|
||||||
|
|
||||||
|
soundTicks = SOUND_CLOCK_TICKS;
|
||||||
|
}
|
||||||
|
|
||||||
static void remake_stereo_buffer()
|
static void remake_stereo_buffer()
|
||||||
{
|
{
|
||||||
// Stereo_Buffer
|
// Stereo_Buffer
|
||||||
|
@ -163,21 +176,26 @@ static void remake_stereo_buffer()
|
||||||
stereo_buffer->set_channel_count( chan_count, chan_types );
|
stereo_buffer->set_channel_count( chan_count, chan_types );
|
||||||
|
|
||||||
if ( !gb_apu )
|
if ( !gb_apu )
|
||||||
|
{
|
||||||
gb_apu = new Gb_Apu;
|
gb_apu = new Gb_Apu;
|
||||||
|
reset_apu();
|
||||||
|
}
|
||||||
|
|
||||||
apply_effects();
|
apply_effects();
|
||||||
|
|
||||||
// Use DMG or CGB sound differences based on type of game
|
|
||||||
gb_apu->reset( gbHardware & 1 ? gb_apu->mode_dmg : gb_apu->mode_cgb );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void gbSoundReset()
|
void gbSoundReset()
|
||||||
{
|
{
|
||||||
|
gb_effects_config.echo = 0.20;
|
||||||
|
gb_effects_config.stereo = 0.15;
|
||||||
|
gb_effects_config.surround = false;
|
||||||
|
|
||||||
|
SOUND_CLOCK_TICKS = 20000;
|
||||||
|
|
||||||
remake_stereo_buffer();
|
remake_stereo_buffer();
|
||||||
|
reset_apu();
|
||||||
|
|
||||||
soundPaused = 1;
|
soundPaused = 1;
|
||||||
SOUND_CLOCK_TICKS = 20000;
|
|
||||||
soundTicks = SOUND_CLOCK_TICKS;
|
|
||||||
soundNextPosition = 0;
|
soundNextPosition = 0;
|
||||||
|
|
||||||
// don't translate
|
// don't translate
|
||||||
|
@ -253,12 +271,142 @@ void gbSoundSetQuality(int quality)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char dummy_buf [735 * 2];
|
||||||
|
|
||||||
|
#define SKIP( type, name ) { dummy_buf, sizeof (type) }
|
||||||
|
|
||||||
|
// funny expr at end ensures that type matches type of variable
|
||||||
|
#define LOAD( type, name ) { &name, sizeof (name) + (&name - (type*) &name) }
|
||||||
|
|
||||||
|
static variable_desc gbsound_format [] =
|
||||||
|
{
|
||||||
|
SKIP( int, soundPaused ),
|
||||||
|
SKIP( int, soundPlay ),
|
||||||
|
SKIP( int, soundTicks ),
|
||||||
|
SKIP( int, SOUND_CLOCK_TICKS ),
|
||||||
|
SKIP( int, soundLevel1 ),
|
||||||
|
SKIP( int, soundLevel2 ),
|
||||||
|
SKIP( int, soundBalance ),
|
||||||
|
SKIP( int, soundMasterOn ),
|
||||||
|
SKIP( int, soundIndex ),
|
||||||
|
SKIP( int, soundVIN ),
|
||||||
|
SKIP( int, soundOn [0] ),
|
||||||
|
SKIP( int, soundATL [0] ),
|
||||||
|
SKIP( int, sound1Skip ),
|
||||||
|
SKIP( int, soundIndex [0] ),
|
||||||
|
SKIP( int, sound1Continue ),
|
||||||
|
SKIP( int, soundEnvelopeVolume [0] ),
|
||||||
|
SKIP( int, soundEnvelopeATL [0] ),
|
||||||
|
SKIP( int, sound1EnvelopeATLReload ),
|
||||||
|
SKIP( int, sound1EnvelopeUpDown ),
|
||||||
|
SKIP( int, sound1SweepATL ),
|
||||||
|
SKIP( int, sound1SweepATLReload ),
|
||||||
|
SKIP( int, sound1SweepSteps ),
|
||||||
|
SKIP( int, sound1SweepUpDown ),
|
||||||
|
SKIP( int, sound1SweepStep ),
|
||||||
|
SKIP( int, soundOn [1] ),
|
||||||
|
SKIP( int, soundATL [1] ),
|
||||||
|
SKIP( int, sound2Skip ),
|
||||||
|
SKIP( int, soundIndex [1] ),
|
||||||
|
SKIP( int, sound2Continue ),
|
||||||
|
SKIP( int, soundEnvelopeVolume [1] ),
|
||||||
|
SKIP( int, soundEnvelopeATL [1] ),
|
||||||
|
SKIP( int, sound2EnvelopeATLReload ),
|
||||||
|
SKIP( int, sound2EnvelopeUpDown ),
|
||||||
|
SKIP( int, soundOn [2] ),
|
||||||
|
SKIP( int, soundATL [2] ),
|
||||||
|
SKIP( int, sound3Skip ),
|
||||||
|
SKIP( int, soundIndex [2] ),
|
||||||
|
SKIP( int, sound3Continue ),
|
||||||
|
SKIP( int, sound3OutputLevel ),
|
||||||
|
SKIP( int, soundOn [3] ),
|
||||||
|
SKIP( int, soundATL [3] ),
|
||||||
|
SKIP( int, sound4Skip ),
|
||||||
|
SKIP( int, soundIndex [3] ),
|
||||||
|
SKIP( int, sound4Clock ),
|
||||||
|
SKIP( int, sound4ShiftRight ),
|
||||||
|
SKIP( int, sound4ShiftSkip ),
|
||||||
|
SKIP( int, sound4ShiftIndex ),
|
||||||
|
SKIP( int, sound4NSteps ),
|
||||||
|
SKIP( int, sound4CountDown ),
|
||||||
|
SKIP( int, sound4Continue ),
|
||||||
|
SKIP( int, soundEnvelopeVolume [2] ),
|
||||||
|
SKIP( int, soundEnvelopeATL [2] ),
|
||||||
|
SKIP( int, sound4EnvelopeATLReload ),
|
||||||
|
SKIP( int, sound4EnvelopeUpDown ),
|
||||||
|
SKIP( int, soundEnableFlag ),
|
||||||
|
{ NULL, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static variable_desc gbsound_format2 [] =
|
||||||
|
{
|
||||||
|
SKIP( int, sound1ATLreload ),
|
||||||
|
SKIP( int, freq1low ),
|
||||||
|
SKIP( int, freq1high ),
|
||||||
|
SKIP( int, sound2ATLreload ),
|
||||||
|
SKIP( int, freq2low ),
|
||||||
|
SKIP( int, freq2high ),
|
||||||
|
SKIP( int, sound3ATLreload ),
|
||||||
|
SKIP( int, freq3low ),
|
||||||
|
SKIP( int, freq3high ),
|
||||||
|
SKIP( int, sound4ATLreload ),
|
||||||
|
SKIP( int, freq4 ),
|
||||||
|
{ NULL, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static variable_desc gbsound_format3 [] =
|
||||||
|
{
|
||||||
|
SKIP( u8[2*735], soundBuffer ),
|
||||||
|
SKIP( u8[2*735], soundBuffer ),
|
||||||
|
SKIP( u16[735], soundFinalWave ),
|
||||||
|
{ NULL, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
void gbSoundSaveGame(gzFile gzFile)
|
void gbSoundSaveGame(gzFile gzFile)
|
||||||
{
|
{
|
||||||
// TODO: implement
|
// TODO: implement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
nr10 = 0,
|
||||||
|
nr11, nr12, nr13, nr14,
|
||||||
|
nr20, nr21, nr22, nr23, nr24,
|
||||||
|
nr30, nr31, nr32, nr33, nr34,
|
||||||
|
nr40, nr41, nr42, nr43, nr44,
|
||||||
|
nr50, nr51, nr52
|
||||||
|
};
|
||||||
|
|
||||||
void gbSoundReadGame(int version,gzFile gzFile)
|
void gbSoundReadGame(int version,gzFile gzFile)
|
||||||
{
|
{
|
||||||
// TODO: implement
|
return; // TODO: apparently GB save states don't work in the main emulator
|
||||||
|
|
||||||
|
// Load state
|
||||||
|
utilReadData( gzFile, gbsound_format );
|
||||||
|
|
||||||
|
if ( version >= 11 )
|
||||||
|
utilReadData( gzFile, gbsound_format2 );
|
||||||
|
|
||||||
|
utilReadData( gzFile, gbsound_format3 );
|
||||||
|
|
||||||
|
int quality = 1;
|
||||||
|
if ( version >= 7 )
|
||||||
|
quality = utilReadInt( gzFile );
|
||||||
|
|
||||||
|
gbSoundSetQuality( quality );
|
||||||
|
|
||||||
|
// Convert to format Gb_Apu uses
|
||||||
|
reset_apu();
|
||||||
|
gb_apu_state_t s;
|
||||||
|
gb_apu->save_state( &s ); // use fresh values for anything not restored
|
||||||
|
|
||||||
|
// Only some registers are properly preserved
|
||||||
|
static int const regs_to_copy [] = {
|
||||||
|
nr10, nr11, nr12, nr21, nr22, nr30, nr32, nr42, nr43, nr50, nr51, nr52, -1
|
||||||
|
};
|
||||||
|
for ( int i = 0; regs_to_copy [i] >= 0; i++ )
|
||||||
|
s.regs [regs_to_copy [i]] = gbMemory [0xFF10 + regs_to_copy [i]];
|
||||||
|
|
||||||
|
memcpy( &s.regs [0x20], &gbMemory [0xFF30], 0x10 ); // wave
|
||||||
|
|
||||||
|
gb_apu->load_state( s );
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Gb_Snd_Emu 0.2.0. http://www.slack.net/~ant/
|
// Gb_Snd_Emu $vers. http://www.slack.net/~ant/
|
||||||
|
|
||||||
#include "Gb_Apu.h"
|
#include "Gb_Apu.h"
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
// a Game Boy Advance emulator.
|
// a Game Boy Advance emulator.
|
||||||
#define GB_APU_OVERCLOCK 4
|
#define GB_APU_OVERCLOCK 4
|
||||||
|
|
||||||
|
#define GB_APU_CUSTOM_STATE 1
|
||||||
|
|
||||||
// Uncomment to enable platform-specific (and possibly non-portable) optimizations.
|
// Uncomment to enable platform-specific (and possibly non-portable) optimizations.
|
||||||
//#define BLARGG_NONPORTABLE 1
|
//#define BLARGG_NONPORTABLE 1
|
||||||
|
|
||||||
|
|
|
@ -166,7 +166,7 @@ bool DirectSound::init()
|
||||||
freq = 44100;
|
freq = 44100;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
soundBufferLen = freq*2/30;
|
soundBufferLen = freq/60*4;
|
||||||
soundBufferTotalLen = soundBufferLen * 10;
|
soundBufferTotalLen = soundBufferLen * 10;
|
||||||
|
|
||||||
ZeroMemory( &wfx, sizeof(WAVEFORMATEX) );
|
ZeroMemory( &wfx, sizeof(WAVEFORMATEX) );
|
||||||
|
@ -310,7 +310,7 @@ void DirectSound::write()
|
||||||
if( !speedup && synchronize && !theApp.throttle ) {
|
if( !speedup && synchronize && !theApp.throttle ) {
|
||||||
hr = dsbSecondary->GetStatus(&status);
|
hr = dsbSecondary->GetStatus(&status);
|
||||||
if( status & DSBSTATUS_PLAYING ) {
|
if( status & DSBSTATUS_PLAYING ) {
|
||||||
if( !soundPaused ) {
|
if( soundPaused ) {
|
||||||
while( true ) {
|
while( true ) {
|
||||||
dsbSecondary->GetCurrentPosition(&play, NULL);
|
dsbSecondary->GetCurrentPosition(&play, NULL);
|
||||||
int BufferLeft = ((soundNextPosition <= play) ?
|
int BufferLeft = ((soundNextPosition <= play) ?
|
||||||
|
|
|
@ -2007,3 +2007,4 @@ void MainWnd::OnUpdateOutputapiOpenal(CCmdUI *pCmdUI)
|
||||||
pCmdUI->SetCheck( ( theApp.audioAPI == OPENAL ) ? 1 : 0 );
|
pCmdUI->SetCheck( ( theApp.audioAPI == OPENAL ) ? 1 : 0 );
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -160,6 +160,10 @@ bool OpenAL::init()
|
||||||
winlog( "OpenAL::init\n" );
|
winlog( "OpenAL::init\n" );
|
||||||
assert( initialized == false );
|
assert( initialized == false );
|
||||||
|
|
||||||
|
if (theApp.OpenALAudiomixing)
|
||||||
|
{
|
||||||
|
device = alcOpenDevice( "Generic Software" );
|
||||||
|
}
|
||||||
device = alcOpenDevice( NULL );
|
device = alcOpenDevice( NULL );
|
||||||
assert( device != NULL );
|
assert( device != NULL );
|
||||||
|
|
||||||
|
|
|
@ -304,6 +304,7 @@ VBA::VBA()
|
||||||
input = NULL;
|
input = NULL;
|
||||||
joypadDefault = 0;
|
joypadDefault = 0;
|
||||||
autoFire = 0;
|
autoFire = 0;
|
||||||
|
OpenALAudiomixing = false;
|
||||||
autoFireToggle = false;
|
autoFireToggle = false;
|
||||||
winPauseNextFrame = false;
|
winPauseNextFrame = false;
|
||||||
soundRecording = false;
|
soundRecording = false;
|
||||||
|
@ -1420,6 +1421,7 @@ void VBA::loadSettings()
|
||||||
audioAPI = DIRECTSOUND;
|
audioAPI = DIRECTSOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OpenALAudiomixing = regQueryDwordValue( "OpenALAudiomixing", 0 );
|
||||||
windowPositionX = regQueryDwordValue("windowX", 0);
|
windowPositionX = regQueryDwordValue("windowX", 0);
|
||||||
if(windowPositionX < 0)
|
if(windowPositionX < 0)
|
||||||
windowPositionX = 0;
|
windowPositionX = 0;
|
||||||
|
|
|
@ -148,6 +148,7 @@ class VBA : public CWinApp
|
||||||
u32 autoFrameSkipLastTime;
|
u32 autoFrameSkipLastTime;
|
||||||
bool autoFrameSkip;
|
bool autoFrameSkip;
|
||||||
bool vsync;
|
bool vsync;
|
||||||
|
bool OpenALAudiomixing;
|
||||||
bool changingVideoSize;
|
bool changingVideoSize;
|
||||||
GUID videoDriverGUID;
|
GUID videoDriverGUID;
|
||||||
GUID *pVideoDriverGUID;
|
GUID *pVideoDriverGUID;
|
||||||
|
|
|
@ -806,13 +806,14 @@
|
||||||
#define IDC_COMBO_PLUGIN 40345
|
#define IDC_COMBO_PLUGIN 40345
|
||||||
#define ID_OUTPUTAPI_DIRECTSOUND 40346
|
#define ID_OUTPUTAPI_DIRECTSOUND 40346
|
||||||
#define ID_OUTPUTAPI_OPENAL 40347
|
#define ID_OUTPUTAPI_OPENAL 40347
|
||||||
|
#define ID_OUTPUTAPI_SOFTWAREMIXING 40348
|
||||||
|
|
||||||
// Next default values for new objects
|
// Next default values for new objects
|
||||||
//
|
//
|
||||||
#ifdef APSTUDIO_INVOKED
|
#ifdef APSTUDIO_INVOKED
|
||||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||||
#define _APS_NEXT_RESOURCE_VALUE 159
|
#define _APS_NEXT_RESOURCE_VALUE 159
|
||||||
#define _APS_NEXT_COMMAND_VALUE 40348
|
#define _APS_NEXT_COMMAND_VALUE 40349
|
||||||
#define _APS_NEXT_CONTROL_VALUE 1269
|
#define _APS_NEXT_CONTROL_VALUE 1269
|
||||||
#define _APS_NEXT_SYMED_VALUE 103
|
#define _APS_NEXT_SYMED_VALUE 103
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue