upgrade sound core and GB engine

This commit is contained in:
dborth 2008-10-18 06:49:04 +00:00
parent 3976f85610
commit 5fb5767632
40 changed files with 18834 additions and 13091 deletions

View File

@ -18,7 +18,7 @@ include $(DEVKITPPC)/gamecube_rules
TARGET := vbagx_gc
TARGETDIR := executables
BUILD := build_gc
SOURCES := source/vba source/vba/agb source/vba/dmg source/ngc source/sz
SOURCES := source/vba source/vba/agb source/vba/dmg source/vba/dmg/gb_apu source/ngc source/sz
INCLUDES := source/vba source/ngc
#---------------------------------------------------------------------------------

View File

@ -18,7 +18,7 @@ include $(DEVKITPPC)/wii_rules
TARGET := vbagx_wii
TARGETDIR := executables
BUILD := build_wii
SOURCES := source/vba source/vba/agb source/vba/dmg source/ngc source/sz
SOURCES := source/vba source/vba/agb source/vba/dmg source/vba/dmg/gb_apu source/ngc source/sz
INCLUDES := source/vba source/ngc
#---------------------------------------------------------------------------------

View File

@ -13,18 +13,19 @@
#include <stdlib.h>
#include <string.h>
#include "agb/GBA.h"
#include "agb/agbprint.h"
#include "unzip.h"
#include "Util.h"
#include "Flash.h"
#include "Port.h"
#include "RTC.h"
#include "Sound.h"
#include "unzip.h"
#include "Util.h"
#include "dmg/GB.h"
#include "Cheats.h"
#include "agb/GBA.h"
#include "agb/agbprint.h"
#include "dmg/gb.h"
#include "dmg/gbGlobals.h"
#include "images/saveicon.h"
//#include "dmg/gbSound.h"
#include "dmg/gbCheats.h"
#include "dmg/gbSound.h"
#include "vba.h"
#include "fileop.h"
@ -37,6 +38,7 @@
#include "video.h"
#include "menudraw.h"
#include "gcunzip.h"
#include "images/saveicon.h"
extern "C"
{
@ -63,22 +65,17 @@ int sensorX = 2047;
int sensorY = 2047;
int systemFrameSkip = 0;
bool systemSoundOn = false;
int systemVerbose = 0;
int cartridgeType = 0;
int srcWidth = 0;
int srcHeight = 0;
int destWidth = 0;
int destHeight = 0;
int srcPitch = 0;
int saveExists = 0;
int systemRedShift = 0;
int systemBlueShift = 0;
int systemGreenShift = 0;
int systemColorDepth = 0;
u16 systemGbPalette[24];
u16 systemColorMap16[0x10000];
//u32 systemColorMap32[0x10000];
u32 *systemColorMap32 = NULL;
struct EmulatedSystem emulator =
@ -148,7 +145,7 @@ void system10Frames(int rate)
else
timeOff = 0; // timeoff was not valid
if(diff > 175 && systemFrameSkip < 9)
if(diff > 170 && systemFrameSkip < 9)
systemFrameSkip++;
else if(diff < 150 && systemFrameSkip > 0)
systemFrameSkip--;
@ -602,42 +599,58 @@ bool LoadVBAROM(int method)
cartridgeType = 0;
srcWidth = 0;
srcHeight = 0;
destWidth = 0;
destHeight = 0;
srcPitch = 0;
switch( type )
{
case 2:
//WaitPrompt("GameBoy Advance Image");
cartridgeType = 2;
emulator = GBASystem;
srcWidth = 240;
srcHeight = 160;
loaded = VMCPULoadROM(method);
// Actual Visual Aspect is 1.57
hAspect = 70;
vAspect = 46;
srcPitch = 484;
soundQuality = 2;
soundBufferLen = 1470;
cpuSaveType = 0;
break;
//WaitPrompt("GameBoy Advance Image");
cartridgeType = 2;
emulator = GBASystem;
srcWidth = 240;
srcHeight = 160;
loaded = VMCPULoadROM(method);
// Actual Visual Aspect is 1.57
hAspect = 70;
vAspect = 46;
srcPitch = 484;
soundQuality = 2;
soundBufferLen = 736 * 2;
cpuSaveType = 0;
break;
case 1:
//WaitPrompt("GameBoy Image");
cartridgeType = 1;
emulator = GBSystem;
srcWidth = 160;
srcHeight = 144;
loaded = LoadGBROM(method);
// Actual physical aspect is 1.0
hAspect = 60;
vAspect = 46;
srcPitch = 324;
soundQuality = 1;
soundBufferLen = 1470 * 2;
break;
//WaitPrompt("GameBoy Image");
cartridgeType = 1;
emulator = GBSystem;
gbBorderOn = 0;
if(gbBorderOn)
{
srcWidth = 256;
srcHeight = 224;
gbBorderLineSkip = 256;
gbBorderColumnSkip = 48;
gbBorderRowSkip = 40;
}
else
{
srcWidth = 160;
srcHeight = 144;
gbBorderLineSkip = 160;
gbBorderColumnSkip = 0;
gbBorderRowSkip = 0;
}
loaded = LoadGBROM(method);
// Actual physical aspect is 1.0
hAspect = 60;
vAspect = 46;
srcPitch = 324;
soundQuality = 1;
soundBufferLen = 1470 * 2;
break;
}
if(!loaded)
@ -651,27 +664,30 @@ bool LoadVBAROM(int method)
flashSetSize(0x20000); // 128K saves
rtcEnable(true);
agbPrintEnable(false);
soundOffFlag = false;
soundLowPass = true;
// Setup GX
GX_Render_Init( srcWidth, srcHeight, hAspect, vAspect );
if ( cartridgeType == 1 )
{
gbGetHardwareType();
// used for the handling of the gb Boot Rom
//if (gbHardware & 5)
//gbCPUInit(gbBiosFileName, useBios);
gbSoundReset();
gbSoundSetQuality(soundQuality);
gbReset();
}
else
{
soundReset();
soundSetQuality(soundQuality);
CPUInit("/VBA/BIOS/BIOS.GBA", 1);
CPUReset();
}
soundVolume = 0;
systemSoundOn = true;
soundInit();
emulating = 1;

File diff suppressed because it is too large Load Diff

View File

@ -1,84 +1,83 @@
// -*- C++ -*-
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
// Copyright (C) 1999-2003 Forgotten
// Copyright (C) 2004 Forgotten and the VBA development team
// Copyright (C) 2004-2006 VBA development team
// 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// Sound emulation setup/options and GBA sound emulation
#ifndef VBA_SOUND_H
#define VBA_SOUND_H
#include "System.h"
#define NR10 0x60
#define NR11 0x62
#define NR12 0x63
#define NR13 0x64
#define NR14 0x65
#define NR21 0x68
#define NR22 0x69
#define NR23 0x6c
#define NR24 0x6d
#define NR30 0x70
#define NR31 0x72
#define NR32 0x73
#define NR33 0x74
#define NR34 0x75
#define NR41 0x78
#define NR42 0x79
#define NR43 0x7c
#define NR44 0x7d
#define NR50 0x80
#define NR51 0x81
#define NR52 0x84
//// Setup/options (these affect GBA and GB sound)
// Initializes sound and returns true if successful. Sets sound quality to
// current value in soundQuality global.
bool soundInit();
// Manages sound volume, where 1.0 is normal
void soundSetVolume( float );
float soundGetVolume();
// Manages muting bitmask. The bits control the following channels:
// 0x001 Pulse 1
// 0x002 Pulse 2
// 0x004 Wave
// 0x008 Noise
// 0x100 PCM 1
// 0x200 PCM 2
void soundSetEnable( int mask );
int soundGetEnable();
// Pauses/resumes system sound output
void soundPause();
void soundResume();
extern bool soundPaused; // current paused state
// Cleans up sound. Afterwards, soundInit() can be called again.
void soundShutdown();
// Sound buffering
extern int soundBufferLen; // size of sound buffer in BYTES
extern u16 soundFinalWave[1470];// 16-bit SIGNED stereo sample buffer
//// GBA sound options
// Sets sample rate to 44100 / quality
void soundSetQuality( int quality );
extern int soundQuality; // current sound quality
// Sound settings
extern bool soundInterpolation; // 1 if PCM should have low-pass filtering
extern float soundFiltering; // 0.0 = none, 1.0 = max
//// GBA sound emulation
// GBA sound registers
#define SGCNT0_H 0x82
#define FIFOA_L 0xa0
#define FIFOA_H 0xa2
#define FIFOB_L 0xa4
#define FIFOB_H 0xa6
void soundTick();
void soundShutdown();
bool soundInit();
void soundPause();
void soundResume();
void soundEnable(int);
void soundDisable(int);
int soundGetEnable();
// Resets emulated sound hardware
void soundReset();
void soundSaveGame(gzFile);
void soundReadGame(gzFile, int);
void soundEvent(u32, u8);
void soundEvent(u32, u16);
void soundTimerOverflow(int);
void soundSetQuality(int);
extern int SOUND_CLOCK_TICKS;
extern int soundTicks;
extern int soundPaused;
extern bool soundOffFlag;
extern int soundQuality;
extern int soundBufferLen;
extern int soundBufferTotalLen;
extern u32 soundNextPosition;
extern u16 soundFinalWave[1470];
extern int soundVolume;
// Emulates write to sound hardware
void soundEvent( u32 addr, u8 data );
void soundEvent( u32 addr, u16 data ); // TODO: error-prone to overload like this
extern bool soundEcho;
extern bool soundLowPass;
extern bool soundReverse;
// Notifies emulator that a timer has overflowed
void soundTimerOverflow( int which );
// Notifies emulator that PCM rate may have changed
void interp_rate();
// Notifies emulator that SOUND_CLOCK_TICKS clocks have passed
void psoundTickfn();
extern int SOUND_CLOCK_TICKS; // Number of 16.8 MHz clocks between calls to soundTick()
extern int soundTicks; // Number of 16.8 MHz clocks until soundTick() will be called
// Saves/loads emulator state
void soundSaveGame( gzFile );
void soundReadGame( gzFile, int version );
#endif // VBA_SOUND_H

View File

@ -21,7 +21,8 @@
#define VBA_SYSTEM_H
#include <stdint.h>
#include "unzip.h"
#include <zlib.h>
#ifndef NULL
#define NULL 0
@ -39,11 +40,6 @@ typedef int32_t s32;
typedef int64_t s64;
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
struct EmulatedSystem {
// main emulation function
void (*emuMain)(int);
@ -113,7 +109,6 @@ extern void winlog(const char *,...);
extern void (*dbgOutput)(const char *s, u32 addr);
extern void (*dbgSignal)(int sig,int number);
extern bool systemSoundOn; // old sound system
extern u16 systemColorMap16[0x10000];
//extern u32 systemColorMap32[0x10000];
extern u32 *systemColorMap32;

View File

@ -35,6 +35,7 @@ extern "C" {
#include "Globals.h"
#include "RTC.h"
#include "Port.h"
#include "unzip.h"
extern "C" {
#include "memgzio.h"

View File

@ -3740,7 +3740,7 @@ void CPULoop(int ticks)
// mute sound
soundTicks -= clockTicks;
if(soundTicks <= 0) {
soundTick(); // psoundTickfn();
psoundTickfn();
soundTicks += SOUND_CLOCK_TICKS;
}

File diff suppressed because it is too large Load Diff

65
source/vba/dmg/gb.h Normal file
View File

@ -0,0 +1,65 @@
// -*- C++ -*-
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
// Copyright (C) 1999-2003 Forgotten
// Copyright (C) 2005-2006 Forgotten and the VBA development team
// 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef VBA_GB_GB_H
#define VBA_GB_GB_H
#define C_FLAG 0x10
#define H_FLAG 0x20
#define N_FLAG 0x40
#define Z_FLAG 0x80
typedef union {
struct {
#ifdef WORDS_BIGENDIAN
u8 B1, B0;
#else
u8 B0,B1;
#endif
} B;
u16 W;
} gbRegister;
bool gbLoadRom(const char *);
void gbEmulate(int);
void gbWriteMemory(register u16, register u8);
void gbDrawLine();
bool gbIsGameboyRom(const char *);
void gbGetHardwareType();
void gbReset();
void gbCleanUp();
void gbCPUInit(const char *,bool);
bool gbWriteBatteryFile(const char *);
bool gbWriteBatteryFile(const char *, bool);
bool gbReadBatteryFile(const char *);
bool gbWriteSaveState(const char *);
bool gbWriteMemSaveState(char *, int);
bool gbReadSaveState(const char *);
bool gbReadMemSaveState(char *, int);
void gbSgbRenderBorder();
bool gbWritePNGFile(const char *);
bool gbWriteBMPFile(const char *);
bool gbReadGSASnapshot(const char *);
extern struct EmulatedSystem GBSystem;
bool MemgbReadBatteryFile(char * membuffer, int read);
int MemgbWriteBatteryFile(char * membuffer);
#endif

View File

@ -1,6 +1,6 @@
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
// Copyright (C) 1999-2003 Forgotten
// Copyright (C) 2004 Forgotten and the VBA development team
// Copyright (C) 2005 Forgotten and the VBA development team
// 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
@ -27,9 +27,11 @@
#include "gbCheats.h"
#include "gbGlobals.h"
#include "gb.h"
gbCheat gbCheatList[100];
int gbCheatNumber = 0;
int gbNextCheat = 0;
bool gbCheatMap[0x10000];
extern bool cheatsEnabled;
@ -41,59 +43,50 @@ void gbCheatUpdateMap()
{
memset(gbCheatMap, 0, 0x10000);
for(int i = 0; i < gbCheatNumber; i++)
{
if(gbCheatList[i].enabled)
gbCheatMap[gbCheatList[i].address] = true;
}
for(int i = 0; i < gbCheatNumber; i++) {
if(gbCheatList[i].enabled)
gbCheatMap[gbCheatList[i].address] = true;
}
}
void gbCheatsSaveGame(gzFile gzFile)
{
utilWriteInt(gzFile, gbCheatNumber);
if(gbCheatNumber)
if(gbCheatNumber>0)
utilGzWrite(gzFile, &gbCheatList[0], sizeof(gbCheat)*gbCheatNumber);
}
void gbCheatsReadGame(gzFile gzFile, int version)
{
if(version <= 8)
{
int gbGgOn = utilReadInt(gzFile);
if(version <= 8) {
int gbGgOn = utilReadInt(gzFile);
if(gbGgOn)
{
int n = utilReadInt(gzFile);
gbXxCheat tmpCheat;
for(int i = 0; i < n; i++)
{
utilGzRead(gzFile,&tmpCheat, sizeof(gbXxCheat));
gbAddGgCheat(tmpCheat.cheatCode, tmpCheat.cheatDesc);
}
}
int gbGsOn = utilReadInt(gzFile);
if(gbGsOn)
{
int n = utilReadInt(gzFile);
gbXxCheat tmpCheat;
for(int i = 0; i < n; i++)
{
utilGzRead(gzFile,&tmpCheat, sizeof(gbXxCheat));
gbAddGsCheat(tmpCheat.cheatCode, tmpCheat.cheatDesc);
}
}
if(gbGgOn) {
int n = utilReadInt(gzFile);
gbXxCheat tmpCheat;
for(int i = 0; i < n; i++) {
utilGzRead(gzFile,&tmpCheat, sizeof(gbXxCheat));
gbAddGgCheat(tmpCheat.cheatCode, tmpCheat.cheatDesc);
}
}
else
{
gbCheatNumber = utilReadInt(gzFile);
if(gbCheatNumber)
{
utilGzRead(gzFile, &gbCheatList[0], sizeof(gbCheat)*gbCheatNumber);
}
int gbGsOn = utilReadInt(gzFile);
if(gbGsOn) {
int n = utilReadInt(gzFile);
gbXxCheat tmpCheat;
for(int i = 0; i < n; i++) {
utilGzRead(gzFile,&tmpCheat, sizeof(gbXxCheat));
gbAddGsCheat(tmpCheat.cheatCode, tmpCheat.cheatDesc);
}
}
} else {
gbCheatNumber = utilReadInt(gzFile);
if(gbCheatNumber>0) {
utilGzRead(gzFile, &gbCheatList[0], sizeof(gbCheat)*gbCheatNumber);
}
}
gbCheatUpdateMap();
}
@ -102,7 +95,7 @@ void gbCheatsSaveCheatList(const char *file)
{
if(gbCheatNumber == 0)
return;
FILE* f = fopen(file, "wb");
FILE *f = fopen(file, "wb");
if(f == NULL)
return;
int version = 1;
@ -122,53 +115,47 @@ bool gbCheatsLoadCheatList(const char *file)
int count = 0;
FILE* f = fopen(file, "rb");
FILE *f = fopen(file, "rb");
if(f == NULL)
return false;
int version = 0;
if(fread(&version, 1, sizeof(version), f) != sizeof(version))
{
fclose(f);
return false;
}
if(fread(&version, 1, sizeof(version), f) != sizeof(version)) {
fclose(f);
return false;
}
if(version != 1)
{
systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_VERSION,
N_("Unsupported cheat list version %d"), version);
fclose(f);
return false;
}
if(version != 1) {
systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_VERSION,
N_("Unsupported cheat list version %d"), version);
fclose(f);
return false;
}
int type = 0;
if(fread(&type, 1, sizeof(type), f) != sizeof(type))
{
fclose(f);
return false;
}
if(fread(&type, 1, sizeof(type), f) != sizeof(type)) {
fclose(f);
return false;
}
if(type != 1)
{
systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_TYPE,
N_("Unsupported cheat list type %d"), type);
fclose(f);
return false;
}
if(type != 1) {
systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_TYPE,
N_("Unsupported cheat list type %d"), type);
fclose(f);
return false;
}
if(fread(&count, 1, sizeof(count), f) != sizeof(count))
{
fclose(f);
return false;
}
if(fread(&count, 1, sizeof(count), f) != sizeof(count)) {
fclose(f);
return false;
}
if(fread(gbCheatList, 1, sizeof(gbCheatList), f) != sizeof(gbCheatList))
{
fclose(f);
return false;
}
if(fread(gbCheatList, 1, sizeof(gbCheatList), f) != sizeof(gbCheatList)) {
fclose(f);
return false;
}
gbCheatNumber = count;
gbCheatUpdateMap();
@ -178,7 +165,7 @@ bool gbCheatsLoadCheatList(const char *file)
bool gbVerifyGsCode(const char *code)
{
int len = strlen(code);
size_t len = strlen(code);
if(len == 0)
return true;
@ -191,32 +178,26 @@ bool gbVerifyGsCode(const char *code)
return false;
int address = GBCHEAT_HEX_VALUE(code[6]) << 12 |
GBCHEAT_HEX_VALUE(code[7]) << 8 |
GBCHEAT_HEX_VALUE(code[4]) << 4 |
GBCHEAT_HEX_VALUE(code[5]);
if(address < 0xa000 ||
address > 0xdfff)
return false;
GBCHEAT_HEX_VALUE(code[7]) << 8 |
GBCHEAT_HEX_VALUE(code[4]) << 4 |
GBCHEAT_HEX_VALUE(code[5]);
return true;
}
void gbAddGsCheat(const char *code, const char *desc)
bool gbAddGsCheat(const char *code, const char *desc)
{
if(gbCheatNumber > 99)
{
systemMessage(MSG_MAXIMUM_NUMBER_OF_CHEATS,
N_("Maximum number of cheats reached."));
return;
}
if(gbCheatNumber > 99) {
systemMessage(MSG_MAXIMUM_NUMBER_OF_CHEATS,
N_("Maximum number of cheats reached."));
return false;
}
if(!gbVerifyGsCode(code))
{
systemMessage(MSG_INVALID_GAMESHARK_CODE,
N_("Invalid GameShark code: %s"), code);
return;
}
if(!gbVerifyGsCode(code)) {
systemMessage(MSG_INVALID_GAMESHARK_CODE,
N_("Invalid GameShark code: %s"), code);
return false;
}
int i = gbCheatNumber;
@ -224,33 +205,43 @@ void gbAddGsCheat(const char *code, const char *desc)
strcpy(gbCheatList[i].cheatDesc, desc);
gbCheatList[i].code = GBCHEAT_HEX_VALUE(code[0]) << 4 |
GBCHEAT_HEX_VALUE(code[1]);
GBCHEAT_HEX_VALUE(code[1]);
gbCheatList[i].value = GBCHEAT_HEX_VALUE(code[2]) << 4 |
GBCHEAT_HEX_VALUE(code[3]);
GBCHEAT_HEX_VALUE(code[3]);
gbCheatList[i].address = GBCHEAT_HEX_VALUE(code[6]) << 12 |
GBCHEAT_HEX_VALUE(code[7]) << 8 |
GBCHEAT_HEX_VALUE(code[4]) << 4 |
GBCHEAT_HEX_VALUE(code[5]);
GBCHEAT_HEX_VALUE(code[7]) << 8 |
GBCHEAT_HEX_VALUE(code[4]) << 4 |
GBCHEAT_HEX_VALUE(code[5]);
gbCheatList[i].compare = 0;
gbCheatList[i].enabled = true;
gbCheatMap[gbCheatList[i].address] = true;
int gsCode = gbCheatList[i].code;
if ((gsCode !=1) && ((gsCode & 0xF0) !=0x80) && ((gsCode & 0xF0) !=0x90) &&
((gsCode & 0xF0) !=0xA0) && ((gsCode) !=0xF0) && ((gsCode) !=0xF1))
systemMessage(MSG_WRONG_GAMESHARK_CODE,
N_("Wrong GameShark code type : %s"), code);
else if (((gsCode & 0xF0) ==0xA0) || ((gsCode) ==0xF0) || ((gsCode) ==0xF1))
systemMessage(MSG_UNSUPPORTED_GAMESHARK_CODE,
N_("Unsupported GameShark code type : %s"), code);
gbCheatNumber++;
return true;
}
bool gbVerifyGgCode(const char *code)
{
int len = strlen(code);
size_t len = strlen(code);
if(len != 11 &&
len != 7 &&
len != 6 &&
len != 0)
len != 7 &&
len != 6 &&
len != 0)
return false;
if(len == 0)
@ -270,28 +261,26 @@ bool gbVerifyGgCode(const char *code)
return false;
if(!GBCHEAT_IS_HEX(code[6]))
return false;
if(code[7] != 0)
{
if(code[7] != '-')
if(code[7] != 0) {
if(code[7] != '-')
return false;
if(code[8] != 0) {
if(!GBCHEAT_IS_HEX(code[8]))
return false;
if(!GBCHEAT_IS_HEX(code[9]))
return false;
if(!GBCHEAT_IS_HEX(code[10]))
return false;
if(code[8] != 0)
{
if(!GBCHEAT_IS_HEX(code[8]))
return false;
if(!GBCHEAT_IS_HEX(code[9]))
return false;
if(!GBCHEAT_IS_HEX(code[10]))
return false;
}
}
}
// int replace = (GBCHEAT_HEX_VALUE(code[0]) << 4) +
// GBCHEAT_HEX_VALUE(code[1]);
int address = (GBCHEAT_HEX_VALUE(code[2]) << 8) +
(GBCHEAT_HEX_VALUE(code[4]) << 4) +
(GBCHEAT_HEX_VALUE(code[5])) +
((GBCHEAT_HEX_VALUE(code[6]) ^ 0x0f) << 12);
(GBCHEAT_HEX_VALUE(code[4]) << 4) +
(GBCHEAT_HEX_VALUE(code[5])) +
((GBCHEAT_HEX_VALUE(code[6]) ^ 0x0f) << 12);
if(address >= 0x8000 && address <= 0x9fff)
return false;
@ -303,7 +292,7 @@ bool gbVerifyGgCode(const char *code)
return true;
int compare = (GBCHEAT_HEX_VALUE(code[8]) << 4) +
(GBCHEAT_HEX_VALUE(code[10]));
(GBCHEAT_HEX_VALUE(code[10]));
compare = compare ^ 0xff;
compare = (compare >> 2) | ( (compare << 6) & 0xc0);
compare ^= 0x45;
@ -316,74 +305,74 @@ bool gbVerifyGgCode(const char *code)
return true;
}
void gbAddGgCheat(const char *code, const char *desc)
bool gbAddGgCheat(const char *code, const char *desc)
{
if(gbCheatNumber > 99)
{
systemMessage(MSG_MAXIMUM_NUMBER_OF_CHEATS,
N_("Maximum number of cheats reached."));
return;
}
if(gbCheatNumber > 99) {
systemMessage(MSG_MAXIMUM_NUMBER_OF_CHEATS,
N_("Maximum number of cheats reached."));
return false;
}
if(!gbVerifyGgCode(code))
{
systemMessage(MSG_INVALID_GAMEGENIE_CODE,
N_("Invalid GameGenie code: %s"), code);
return;
}
if(!gbVerifyGgCode(code)) {
systemMessage(MSG_INVALID_GAMEGENIE_CODE,
N_("Invalid GameGenie code: %s"), code);
return false;
}
int i = gbCheatNumber;
int len = strlen(code);
size_t len = strlen(code);
strcpy(gbCheatList[i].cheatCode, code);
strcpy(gbCheatList[i].cheatDesc, desc);
gbCheatList[i].code = 1;
gbCheatList[i].code = 0x101;
gbCheatList[i].value = (GBCHEAT_HEX_VALUE(code[0]) << 4) +
GBCHEAT_HEX_VALUE(code[1]);
GBCHEAT_HEX_VALUE(code[1]);
gbCheatList[i].address = (GBCHEAT_HEX_VALUE(code[2]) << 8) +
(GBCHEAT_HEX_VALUE(code[4]) << 4) +
(GBCHEAT_HEX_VALUE(code[5])) +
((GBCHEAT_HEX_VALUE(code[6]) ^ 0x0f) << 12);
(GBCHEAT_HEX_VALUE(code[4]) << 4) +
(GBCHEAT_HEX_VALUE(code[5])) +
((GBCHEAT_HEX_VALUE(code[6]) ^ 0x0f) << 12);
gbCheatList[i].compare = 0;
if(len != 7 && len != 8)
{
if(len != 7 && len != 8) {
int compare = (GBCHEAT_HEX_VALUE(code[8]) << 4) +
(GBCHEAT_HEX_VALUE(code[10]));
compare = compare ^ 0xff;
compare = (compare >> 2) | ( (compare << 6) & 0xc0);
compare ^= 0x45;
int compare = (GBCHEAT_HEX_VALUE(code[8]) << 4) +
(GBCHEAT_HEX_VALUE(code[10]));
compare = compare ^ 0xff;
compare = (compare >> 2) | ( (compare << 6) & 0xc0);
compare ^= 0x45;
gbCheatList[i].compare = compare;
//gbCheatList[i].code = 0;
gbCheatList[i].code = 0x100; // fix for compare value
}
gbCheatList[i].compare = compare;
gbCheatList[i].code = 0;
}
gbCheatList[i].enabled = true;
gbCheatMap[gbCheatList[i].address] = true;
gbCheatNumber++;
return true;
}
void gbCheatRemove(int i)
{
if(i < 0 || i >= gbCheatNumber)
{
systemMessage(MSG_INVALID_CHEAT_TO_REMOVE,
N_("Invalid cheat to remove %d"), i);
return;
}
if(i < 0 || i >= gbCheatNumber) {
systemMessage(MSG_INVALID_CHEAT_TO_REMOVE,
N_("Invalid cheat to remove %d"), i);
return;
}
if((i+1) < gbCheatNumber)
{
memcpy(&gbCheatList[i], &gbCheatList[i+1], sizeof(gbCheat)*
(gbCheatNumber-i-1));
}
if((i+1) < gbCheatNumber) {
memcpy(&gbCheatList[i], &gbCheatList[i+1], sizeof(gbCheat)*
(gbCheatNumber-i-1));
}
gbCheatNumber--;
@ -398,37 +387,32 @@ void gbCheatRemoveAll()
void gbCheatEnable(int i)
{
if(i >=0 && i < gbCheatNumber)
{
if(!gbCheatList[i].enabled)
{
gbCheatList[i].enabled = true;
gbCheatUpdateMap();
}
if(i >=0 && i < gbCheatNumber) {
if(!gbCheatList[i].enabled) {
gbCheatList[i].enabled = true;
gbCheatUpdateMap();
}
}
}
void gbCheatDisable(int i)
{
if(i >=0 && i < gbCheatNumber)
{
if(gbCheatList[i].enabled)
{
gbCheatList[i].enabled = false;
gbCheatUpdateMap();
}
if(i >=0 && i < gbCheatNumber) {
if(gbCheatList[i].enabled) {
gbCheatList[i].enabled = false;
gbCheatUpdateMap();
}
}
}
bool gbCheatReadGSCodeFile(const char *fileName)
{
FILE* file = fopen(fileName, "rb");
FILE *file = fopen(fileName, "rb");
if(!file)
{
systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName);
return false;
}
if(!file) {
systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName);
return false;
}
fseek(file, 0x18, SEEK_SET);
int count = 0;
@ -438,15 +422,14 @@ bool gbCheatReadGSCodeFile(const char *fileName)
char desc[13];
char code[9];
int i;
for(i = 0; i < count; i++)
{
fread(&dummy, 1, 2, file);
fread(desc, 1, 12, file);
desc[12] = 0;
fread(code, 1, 8, file);
code[8] = 0;
gbAddGsCheat(code, desc);
}
for(i = 0; i < count; i++) {
fread(&dummy, 1, 2, file);
fread(desc, 1, 12, file);
desc[12] = 0;
fread(code, 1, 8, file);
code[8] = 0;
gbAddGsCheat(code, desc);
}
for(i = 0; i < gbCheatNumber; i++)
gbCheatDisable(i);
@ -455,25 +438,49 @@ bool gbCheatReadGSCodeFile(const char *fileName)
return true;
}
// Used to emulated GG codes
u8 gbCheatRead(u16 address)
{
if(!cheatsEnabled)
return gbMemoryMap[address>>12][address & 0xFFF];
for(int i = 0; i < gbCheatNumber; i++)
{
if(gbCheatList[i].enabled && gbCheatList[i].address == address)
{
switch(gbCheatList[i].code)
{
case 0x100: // GameGenie support
if(gbMemoryMap[address>>12][address&0xFFF] == gbCheatList[i].compare)
return gbCheatList[i].value;
break;
case 0x00:
for(int i = 0; i < gbCheatNumber; i++) {
if(gbCheatList[i].enabled && gbCheatList[i].address == address) {
switch(gbCheatList[i].code) {
case 0x100: // GameGenie support
if(gbMemoryMap[address>>12][address&0xFFF] == gbCheatList[i].compare)
return gbCheatList[i].value;
break;
case 0x101: // GameGenie 6 digits code support
return gbCheatList[i].value;
break;
}
}
}
return gbMemoryMap[address>>12][address&0xFFF];
}
// Used to emulate GS codes.
void gbCheatWrite(bool reboot)
{
if(cheatsEnabled)
{
u16 address = 0;
if (gbNextCheat >= gbCheatNumber)
gbNextCheat = 0;
for(int i = gbNextCheat; i < gbCheatNumber; i++) {
if(gbCheatList[i].enabled) {
address = gbCheatList[i].address;
if ((!reboot) && (address >= 0x8000) && !((address>=0xA000) && (address<0xC000)))
{ // These codes are executed one per one, at each Vblank
switch(gbCheatList[i].code) {
case 0x01:
case 0x80:
return gbCheatList[i].value;
gbWriteMemory(address, gbCheatList[i].value);
gbNextCheat = i+1;
return;
case 0x90:
case 0x91:
case 0x92:
@ -482,16 +489,34 @@ u8 gbCheatRead(u16 address)
case 0x95:
case 0x96:
case 0x97:
if(address >= 0xd000 && address < 0xe000)
{
if(((gbMemoryMap[0x0d] - gbWram)/0x1000) ==
(gbCheatList[i].code - 0x90))
return gbCheatList[i].value;
}
else
return gbCheatList[i].value;
}
case 0x98:
case 0x99:
case 0x9A:
case 0x9B:
case 0x9C:
case 0x9D:
case 0x9E:
case 0x9F:
int oldbank = gbMemory[0xff70];
gbWriteMemory(0xff70, gbCheatList[i].code & 0xf);
gbWriteMemory(address, gbCheatList[i].value);
gbWriteMemory(0xff70, oldbank);
gbNextCheat = i+1;
return;
}
}
else // These codes are only executed when the game is booted
{
switch(gbCheatList[i].code & 0xF0) {
case 0x80:
gbWriteMemory(0x0000, 0x0A);
gbWriteMemory(0x4000, gbCheatList[i].value & 0xF);
gbWriteMemory(address, gbCheatList[i].value);
gbNextCheat = i+1;
return;
}
}
}
}
return gbMemoryMap[address>>12][address&0xFFF];
}
}

View File

@ -22,36 +22,38 @@
#include "../System.h"
struct gbXxCheat
{
char cheatDesc[100];
char cheatCode[20];
};
struct gbXxCheat {
char cheatDesc[100];
char cheatCode[20];
};
struct gbCheat
{
char cheatCode[20];
char cheatDesc[32];
u16 address;
int code;
u8 compare;
u8 value;
bool enabled;
};
struct gbCheat {
char cheatCode[20];
char cheatDesc[32];
u16 address;
int code;
u8 compare;
u8 value;
bool enabled;
};
extern void gbCheatsSaveGame(gzFile);
extern void gbCheatsReadGame(gzFile, int);
extern void gbCheatsSaveCheatList(const char *);
extern bool gbCheatsLoadCheatList(const char *);
extern bool gbCheatReadGSCodeFile(const char *);
void gbCheatsSaveGame(gzFile);
void gbCheatsReadGame(gzFile, int);
void gbCheatsSaveCheatList(const char *);
bool gbCheatsLoadCheatList(const char *);
bool gbCheatReadGSCodeFile(const char *);
bool gbAddGsCheat(const char *, const char*);
bool gbAddGgCheat(const char *, const char*);
void gbCheatRemove(int);
void gbCheatRemoveAll();
void gbCheatEnable(int);
void gbCheatDisable(int);
u8 gbCheatRead(u16);
void gbCheatWrite(bool);
bool gbVerifyGsCode(const char *code);
bool gbVerifyGgCode(const char *code);
extern void gbAddGsCheat(const char *, const char*);
extern void gbAddGgCheat(const char *, const char*);
extern void gbCheatRemove(int);
extern void gbCheatRemoveAll();
extern void gbCheatEnable(int);
extern void gbCheatDisable(int);
extern u8 gbCheatRead(u16);
extern int gbCheatNumber;
extern gbCheat gbCheatList[100];

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -22,117 +22,115 @@
#include "../System.h"
#include "gbGlobals.h"
typedef struct
{
u8 mask;
u8 value;
char *mnen;
}
GBOPCODE;
typedef struct {
u8 mask;
u8 value;
const char *mnen;
} GBOPCODE;
#define GB_READ(x) gbMemoryMap[(x)>>12][(x)&0xfff]
static char *registers[] =
static const char *registers[] =
{ "B", "C", "D", "E", "H", "L", "(HL)", "A" };
static char *registers16[] =
static const char *registers16[] =
{ "BC", "DE", "HL", "SP", // for some operations
"BC", "DE", "HL", "AF" }; // for push/pop
static char *cond[] =
static const char *cond[] =
{ "NZ", "Z", "NC", "C" };
static char hexDigits[16] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
static GBOPCODE opcodes[] = {
{ 0xff, 0x00, "NOP" },
{ 0xcf, 0x01, "LD %R4,%W" },
{ 0xff, 0x02, "LD (BC),A" },
{ 0xcf, 0x03, "INC %R4" },
{ 0xc7, 0x04, "INC %r3" },
{ 0xc7, 0x05, "DEC %r3" },
{ 0xc7, 0x06, "LD %r3,%B" },
{ 0xff, 0x07, "RLCA" },
{ 0xff, 0x08, "LD (%W),SP" },
{ 0xcf, 0x09, "ADD HL,%R4" },
{ 0xff, 0x0a, "LD A,(BC)" },
{ 0xcf, 0x0b, "DEC %R4" },
{ 0xff, 0x0f, "RRCA" },
{ 0xff, 0x10, "STOP" },
{ 0xff, 0x12, "LD (DE),A" },
{ 0xff, 0x17, "RLA" },
{ 0xff, 0x18, "JR %d" },
{ 0xff, 0x1a, "LD A,(DE)" },
{ 0xff, 0x1f, "RRA" },
{ 0xe7, 0x20, "JR %c3,%d" },
{ 0xff, 0x22, "LDI (HL),A" },
{ 0xff, 0x27, "DAA" },
{ 0xff, 0x2a, "LDI A,(HL)" },
{ 0xff, 0x2f, "CPL" },
{ 0xff, 0x32, "LDD (HL),A" },
{ 0xff, 0x37, "SCF" },
{ 0xff, 0x3a, "LDD A,(HL)" },
{ 0xff, 0x3f, "CCF" },
{ 0xff, 0x76, "HALT" },
{ 0xc0, 0x40, "LD %r3,%r0" },
{ 0xf8, 0x80, "ADD A,%r0" },
{ 0xf8, 0x88, "ADC A,%r0" },
{ 0xf8, 0x90, "SUB %r0" },
{ 0xf8, 0x98, "SBC A,%r0" },
{ 0xf8, 0xa0, "AND %r0" },
{ 0xf8, 0xa8, "XOR %r0" },
{ 0xf8, 0xb0, "OR %r0" },
{ 0xf8, 0xb8, "CP %r0" },
{ 0xe7, 0xc0, "RET %c3" },
{ 0xcf, 0xc1, "POP %t4" },
{ 0xe7, 0xc2, "JP %c3,%W" },
{ 0xff, 0xc3, "JP %W" },
{ 0xe7, 0xc4, "CALL %c3,%W" },
{ 0xcf, 0xc5, "PUSH %t4" },
{ 0xff, 0xc6, "ADD A,%B" },
{ 0xc7, 0xc7, "RST %P" },
{ 0xff, 0xc9, "RET" },
{ 0xff, 0xcd, "CALL %W" },
{ 0xff, 0xce, "ADC %B" },
{ 0xff, 0xd6, "SUB %B" },
{ 0xff, 0xd9, "RETI" },
{ 0xff, 0xde, "SBC %B" },
{ 0xff, 0xe0, "LD (FF%B),A" },
{ 0xff, 0xe2, "LD (FF00h+C),A" },
{ 0xff, 0xe6, "AND %B" },
{ 0xff, 0xe8, "ADD SP,%D" },
{ 0xff, 0xe9, "LD PC,HL" },
{ 0xff, 0xea, "LD (%W),A" },
{ 0xff, 0xee, "XOR %B" },
{ 0xff, 0xf0, "LD A,(FF%B)" },
{ 0xff, 0xf2, "LD A,(FF00h+C)" },
{ 0xff, 0xf3, "DI" },
{ 0xff, 0xf6, "OR %B" },
{ 0xff, 0xf8, "LD HL,SP%D" },
{ 0xff, 0xf9, "LD SP,HL" },
{ 0xff, 0xfa, "LD A,(%W)" },
{ 0xff, 0xfb, "EI" },
{ 0xff, 0xfe, "CP %B" },
{ 0x00, 0x00, "DB %B" }
};
{ 0xff, 0x00, "NOP" },
{ 0xcf, 0x01, "LD %R4,%W" },
{ 0xff, 0x02, "LD (BC),A" },
{ 0xcf, 0x03, "INC %R4" },
{ 0xc7, 0x04, "INC %r3" },
{ 0xc7, 0x05, "DEC %r3" },
{ 0xc7, 0x06, "LD %r3,%B" },
{ 0xff, 0x07, "RLCA" },
{ 0xff, 0x08, "LD (%W),SP" },
{ 0xcf, 0x09, "ADD HL,%R4" },
{ 0xff, 0x0a, "LD A,(BC)" },
{ 0xcf, 0x0b, "DEC %R4" },
{ 0xff, 0x0f, "RRCA" },
{ 0xff, 0x10, "STOP" },
{ 0xff, 0x12, "LD (DE),A" },
{ 0xff, 0x17, "RLA" },
{ 0xff, 0x18, "JR %d" },
{ 0xff, 0x1a, "LD A,(DE)" },
{ 0xff, 0x1f, "RRA" },
{ 0xe7, 0x20, "JR %c3,%d" },
{ 0xff, 0x22, "LDI (HL),A" },
{ 0xff, 0x27, "DAA" },
{ 0xff, 0x2a, "LDI A,(HL)" },
{ 0xff, 0x2f, "CPL" },
{ 0xff, 0x32, "LDD (HL),A" },
{ 0xff, 0x37, "SCF" },
{ 0xff, 0x3a, "LDD A,(HL)" },
{ 0xff, 0x3f, "CCF" },
{ 0xff, 0x76, "HALT" },
{ 0xc0, 0x40, "LD %r3,%r0" },
{ 0xf8, 0x80, "ADD A,%r0" },
{ 0xf8, 0x88, "ADC A,%r0" },
{ 0xf8, 0x90, "SUB %r0" },
{ 0xf8, 0x98, "SBC A,%r0" },
{ 0xf8, 0xa0, "AND %r0" },
{ 0xf8, 0xa8, "XOR %r0" },
{ 0xf8, 0xb0, "OR %r0" },
{ 0xf8, 0xb8, "CP %r0" },
{ 0xe7, 0xc0, "RET %c3" },
{ 0xcf, 0xc1, "POP %t4" },
{ 0xe7, 0xc2, "JP %c3,%W" },
{ 0xff, 0xc3, "JP %W" },
{ 0xe7, 0xc4, "CALL %c3,%W" },
{ 0xcf, 0xc5, "PUSH %t4" },
{ 0xff, 0xc6, "ADD A,%B" },
{ 0xc7, 0xc7, "RST %P" },
{ 0xff, 0xc9, "RET" },
{ 0xff, 0xcd, "CALL %W" },
{ 0xff, 0xce, "ADC %B" },
{ 0xff, 0xd6, "SUB %B" },
{ 0xff, 0xd9, "RETI" },
{ 0xff, 0xde, "SBC %B" },
{ 0xff, 0xe0, "LD (FF%B),A" },
{ 0xff, 0xe2, "LD (FF00h+C),A" },
{ 0xff, 0xe6, "AND %B" },
{ 0xff, 0xe8, "ADD SP,%D" },
{ 0xff, 0xe9, "LD PC,HL" },
{ 0xff, 0xea, "LD (%W),A" },
{ 0xff, 0xee, "XOR %B" },
{ 0xff, 0xf0, "LD A,(FF%B)" },
{ 0xff, 0xf2, "LD A,(FF00h+C)" },
{ 0xff, 0xf3, "DI" },
{ 0xff, 0xf6, "OR %B" },
{ 0xff, 0xf8, "LD HL,SP%D" },
{ 0xff, 0xf9, "LD SP,HL" },
{ 0xff, 0xfa, "LD A,(%W)" },
{ 0xff, 0xfb, "EI" },
{ 0xff, 0xfe, "CP %B" },
{ 0x00, 0x00, "DB %B" }
};
static GBOPCODE cbOpcodes[] = {
{ 0xf8, 0x00, "RLC %r0" },
{ 0xf8, 0x08, "RRC %r0" },
{ 0xf8, 0x10, "RL %r0" },
{ 0xf8, 0x18, "RR %r0" },
{ 0xf8, 0x20, "SLA %r0" },
{ 0xf8, 0x28, "SRA %r0" },
{ 0xf8, 0x30, "SWAP %r0" },
{ 0xf8, 0x38, "SRL %r0" },
{ 0xc0, 0x40, "BIT %b,%r0" },
{ 0xc0, 0x80, "RES %b,%r0" },
{ 0xc0, 0xc0, "SET %b,%r0" },
{ 0x00, 0x00, "DB CBh,%B" }
};
{ 0xf8, 0x00, "RLC %r0" },
{ 0xf8, 0x08, "RRC %r0" },
{ 0xf8, 0x10, "RL %r0" },
{ 0xf8, 0x18, "RR %r0" },
{ 0xf8, 0x20, "SLA %r0" },
{ 0xf8, 0x28, "SRA %r0" },
{ 0xf8, 0x30, "SWAP %r0" },
{ 0xf8, 0x38, "SRL %r0" },
{ 0xc0, 0x40, "BIT %b,%r0" },
{ 0xc0, 0x80, "RES %b,%r0" },
{ 0xc0, 0xc0, "SET %b,%r0" },
{ 0x00, 0x00, "DB CBh,%B" }
};
static char *addHex(char *p, u8 value)
{
@ -147,12 +145,11 @@ static char *addHex16(char *p, u16 value)
return addHex(p, value & 255);
}
static char *addStr(char *p, char *s)
static char *addStr(char *p, const char *s)
{
while(*s)
{
*p++ = *s++;
}
while(*s) {
*p++ = *s++;
}
return p;
}
@ -166,19 +163,16 @@ int gbDis(char *buffer, u16 address)
u8 opcode = GB_READ(address);
address++;
char *mnen;
const char *mnen;
GBOPCODE *op;
if(opcode == 0xcb)
{
opcode = GB_READ(address);
address++;
instr++;
op = cbOpcodes;
}
else
{
op = opcodes;
}
if(opcode == 0xcb) {
opcode = GB_READ(address);
address++;
instr++;
op = cbOpcodes;
} else {
op = opcodes;
}
while(op->value != (opcode & op->mask)) op++;
mnen = op->mnen;
@ -186,75 +180,70 @@ int gbDis(char *buffer, u16 address)
s8 disp;
int shift;
while(*mnen)
{
if(*mnen == '%')
{
mnen++;
switch(*mnen++)
{
case 'W':
b0 = GB_READ(address);
address++;
b1 = GB_READ(address);
address++;
p = addHex16(p, b0|b1<<8);
instr += 2;
*p++ = 'h';
break;
case 'B':
p = addHex(p, GB_READ(address));
*p++ = 'h';
address++;
instr++;
break;
case 'D':
disp = GB_READ(address);
if(disp >= 0)
*p++ = '+';
p += sprintf(p, "%d", disp);
instr++;
break;
case 'd':
disp = GB_READ(address);
address++;
p = addHex16(p, address+disp);
*p++ = 'h';
instr++;
break;
case 'b':
// kind of a hack, but it works :-)
*p++ = hexDigits[(opcode >> 3) & 7];
break;
case 'r':
shift = *mnen++ - '0';
p = addStr(p, registers[(opcode >> shift) & 7]);
break;
case 'R':
shift = *mnen++ - '0';
p = addStr(p, registers16[(opcode >> shift) & 3]);
break;
case 't':
shift = *mnen++ - '0';
p = addStr(p, registers16[4+((opcode >> shift) & 3)]);
break;
case 'P':
p = addHex(p, ((opcode >> 3) & 7) * 8);
break;
case 'c':
shift = *mnen++ - '0';
p = addStr(p, cond[(opcode >> shift) & 3]);
break;
}
}
else
*p++ = *mnen++;
}
for(int i = 0; i < instr; i++)
{
u16 a = addr + i;
addHex(buffer+5+i*2, GB_READ(a));
}
while(*mnen) {
if(*mnen == '%') {
mnen++;
switch(*mnen++) {
case 'W':
b0 = GB_READ(address);
address++;
b1 = GB_READ(address);
address++;
p = addHex16(p, b0|b1<<8);
instr += 2;
*p++ = 'h';
break;
case 'B':
p = addHex(p, GB_READ(address));
*p++ = 'h';
address++;
instr++;
break;
case 'D':
disp = GB_READ(address);
if(disp >= 0)
*p++ = '+';
p += sprintf(p, "%d", disp);
instr++;
break;
case 'd':
disp = GB_READ(address);
address++;
p = addHex16(p, address+disp);
*p++ = 'h';
instr++;
break;
case 'b':
// kind of a hack, but it works :-)
*p++ = hexDigits[(opcode >> 3) & 7];
break;
case 'r':
shift = *mnen++ - '0';
p = addStr(p, registers[(opcode >> shift) & 7]);
break;
case 'R':
shift = *mnen++ - '0';
p = addStr(p, registers16[(opcode >> shift) & 3]);
break;
case 't':
shift = *mnen++ - '0';
p = addStr(p, registers16[4+((opcode >> shift) & 3)]);
break;
case 'P':
p = addHex(p, ((opcode >> 3) & 7) * 8);
break;
case 'c':
shift = *mnen++ - '0';
p = addStr(p, cond[(opcode >> shift) & 3]);
break;
}
} else
*p++ = *mnen++;
}
for(int i = 0; i < instr; i++) {
u16 a = addr + i;
addHex(buffer+5+i*2, GB_READ(a));
}
*p = 0;
return instr;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
// Copyright (C) 1999-2003 Forgotten
// Copyright (C) 2004 Forgotten and the VBA development team
// Copyright (C) 2005-2006 Forgotten and the VBA development team
// 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
@ -24,6 +24,7 @@ int gbRomSizeMask = 0;
int gbRomSize = 0;
int gbRamSizeMask = 0;
int gbRamSize = 0;
int gbTAMA5ramSize = 0;
u8 *gbMemory = NULL;
u8 *gbVram = NULL;
@ -31,6 +32,7 @@ u8 *gbRom = NULL;
u8 *gbRam = NULL;
u8 *gbWram = NULL;
u16 *gbLineBuffer = NULL;
u8 *gbTAMA5ram = NULL;
u16 gbPalette[128];
u8 gbBgp[4] = { 0, 1, 2, 3};
@ -38,6 +40,7 @@ u8 gbObp0[4] = { 0, 1, 2, 3};
u8 gbObp1[4] = { 0, 1, 2, 3};
int gbWindowLine = -1;
bool genericflashcardEnable = false;
int gbCgbMode = 0;
u16 gbColorFilter[32768];

View File

@ -1,7 +1,7 @@
// -*- C++ -*-
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
// Copyright (C) 1999-2003 Forgotten
// Copyright (C) 2004 Forgotten and the VBA development team
// Copyright (C) 2005-2006 Forgotten and the VBA development team
// 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
@ -21,6 +21,12 @@ extern int gbRomSizeMask;
extern int gbRomSize;
extern int gbRamSize;
extern int gbRamSizeMask;
extern int gbTAMA5ramSize;
extern bool useBios;
extern bool skipBios;
extern u8 *bios;
extern bool skipSaveGameBattery;
extern u8 *gbRom;
extern u8 *gbRam;
@ -28,6 +34,7 @@ extern u8 *gbVram;
extern u8 *gbWram;
extern u8 *gbMemory;
extern u16 *gbLineBuffer;
extern u8 *gbTAMA5ram;
extern u8 *gbMemoryMap[16];
@ -46,6 +53,19 @@ extern u8 gbBgp[4];
extern u8 gbObp0[4];
extern u8 gbObp1[4];
extern u16 gbPalette[128];
extern bool gbScreenOn;
extern bool gbDrawWindow;
extern u8 gbSCYLine[300];
// gbSCXLine is used for the emulation (bug) of the SX change
// found in the Artic Zone game.
extern u8 gbSCXLine[300];
// gbBgpLine is used for the emulation of the
// Prehistorik Man's title screen scroller.
extern u8 gbBgpLine[300];
extern u8 gbObp0Line [300];
extern u8 gbObp1Line [300];
// gbSpritesTicks is used for the emulation of Parodius' Laser Beam.
extern u8 gbSpritesTicks[300];
extern u8 register_LCDC;
extern u8 register_LY;
@ -54,8 +74,10 @@ extern u8 register_SCX;
extern u8 register_WY;
extern u8 register_WX;
extern u8 register_VBK;
extern u8 oldRegister_WY;
extern int emulating;
extern bool genericflashcardEnable;
extern int gbBorderLineSkip;
extern int gbBorderRowSkip;
@ -63,6 +85,6 @@ extern int gbBorderColumnSkip;
extern int gbDmaTicks;
extern void gbRenderLine();
extern void gbDrawSprites();
extern void gbDrawSprites(bool);
extern u8 (*gbSerialFunction)(u8);

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
// -*- C++ -*-
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
// Copyright (C) 1999-2003 Forgotten
// Copyright (C) 2004 Forgotten and the VBA development team
// Copyright (C) 2004-2006 Forgotten and the VBA development team
// 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
@ -19,99 +19,136 @@
#include <time.h>
struct mapperMBC1
{
int mapperRAMEnable;
int mapperROMBank;
int mapperRAMBank;
int mapperMemoryModel;
int mapperROMHighAddress;
int mapperRAMAddress;
};
struct mapperMBC1 {
int mapperRAMEnable;
int mapperROMBank;
int mapperRAMBank;
int mapperMemoryModel;
int mapperROMHighAddress;
int mapperRAMAddress;
int mapperRomBank0Remapping;
};
struct mapperMBC2
{
int mapperRAMEnable;
int mapperROMBank;
};
struct mapperMBC2 {
int mapperRAMEnable;
int mapperROMBank;
};
struct mapperMBC3
{
int mapperRAMEnable;
int mapperROMBank;
int mapperRAMBank;
int mapperRAMAddress;
int mapperClockLatch;
int mapperClockRegister;
int mapperSeconds;
int mapperMinutes;
int mapperHours;
int mapperDays;
int mapperControl;
int mapperLSeconds;
int mapperLMinutes;
int mapperLHours;
int mapperLDays;
int mapperLControl;
time_t mapperLastTime;
};
struct mapperMBC3 {
int mapperRAMEnable;
int mapperROMBank;
int mapperRAMBank;
int mapperRAMAddress;
int mapperClockLatch;
int mapperClockRegister;
int mapperSeconds;
int mapperMinutes;
int mapperHours;
int mapperDays;
int mapperControl;
int mapperLSeconds;
int mapperLMinutes;
int mapperLHours;
int mapperLDays;
int mapperLControl;
time_t mapperLastTime;
};
struct mapperMBC5
{
int mapperRAMEnable;
int mapperROMBank;
int mapperRAMBank;
int mapperROMHighAddress;
int mapperRAMAddress;
int isRumbleCartridge;
};
struct mapperMBC5 {
int mapperRAMEnable;
int mapperROMBank;
int mapperRAMBank;
int mapperROMHighAddress;
int mapperRAMAddress;
int isRumbleCartridge;
};
struct mapperMBC7
{
int mapperRAMEnable;
int mapperROMBank;
int mapperRAMBank;
int mapperRAMAddress;
int cs;
int sk;
int state;
int buffer;
int idle;
int count;
int code;
int address;
int writeEnable;
int value;
};
struct mapperMBC7 {
int mapperRAMEnable;
int mapperROMBank;
int mapperRAMBank;
int mapperRAMAddress;
int cs;
int sk;
int state;
int buffer;
int idle;
int count;
int code;
int address;
int writeEnable;
int value;
};
struct mapperHuC1
{
int mapperRAMEnable;
int mapperROMBank;
int mapperRAMBank;
int mapperMemoryModel;
int mapperROMHighAddress;
int mapperRAMAddress;
};
struct mapperHuC1 {
int mapperRAMEnable;
int mapperROMBank;
int mapperRAMBank;
int mapperMemoryModel;
int mapperROMHighAddress;
int mapperRAMAddress;
};
struct mapperHuC3
{
int mapperRAMEnable;
int mapperROMBank;
int mapperRAMBank;
int mapperRAMAddress;
int mapperAddress;
int mapperRAMFlag;
int mapperRAMValue;
int mapperRegister1;
int mapperRegister2;
int mapperRegister3;
int mapperRegister4;
int mapperRegister5;
int mapperRegister6;
int mapperRegister7;
int mapperRegister8;
};
struct mapperHuC3 {
int mapperRAMEnable;
int mapperROMBank;
int mapperRAMBank;
int mapperRAMAddress;
int mapperAddress;
int mapperRAMFlag;
int mapperRAMValue;
int mapperRegister1;
int mapperRegister2;
int mapperRegister3;
int mapperRegister4;
int mapperRegister5;
int mapperRegister6;
int mapperRegister7;
int mapperRegister8;
};
struct mapperTAMA5 {
int mapperRAMEnable;
int mapperROMBank;
int mapperRAMBank;
int mapperRAMAddress;
int mapperRamByteSelect;
int mapperCommandNumber;
int mapperLastCommandNumber;
int mapperCommands[0x10];
int mapperRegister;
int mapperClockLatch;
int mapperClockRegister;
int mapperSeconds;
int mapperMinutes;
int mapperHours;
int mapperDays;
int mapperMonths;
int mapperYears;
int mapperControl;
int mapperLSeconds;
int mapperLMinutes;
int mapperLHours;
int mapperLDays;
int mapperLMonths;
int mapperLYears;
int mapperLControl;
time_t mapperLastTime;
};
struct mapperMMM01 {
int mapperRAMEnable;
int mapperROMBank;
int mapperRAMBank;
int mapperMemoryModel;
int mapperROMHighAddress;
int mapperRAMAddress;
int mapperRomBank0Remapping;
};
struct mapperGS3 {
int mapperROMBank;
};
extern mapperMBC1 gbDataMBC1;
extern mapperMBC2 gbDataMBC2;
@ -119,9 +156,13 @@ extern mapperMBC3 gbDataMBC3;
extern mapperMBC5 gbDataMBC5;
extern mapperHuC1 gbDataHuC1;
extern mapperHuC3 gbDataHuC3;
extern mapperTAMA5 gbDataTAMA5;
extern mapperMMM01 gbDataMMM01;
extern mapperGS3 gbDataGS3;
void mapperMBC1ROM(u16,u8);
void mapperMBC1RAM(u16,u8);
u8 mapperMBC1ReadRAM(u16);
void mapperMBC2ROM(u16,u8);
void mapperMBC2RAM(u16,u8);
void mapperMBC3ROM(u16,u8);
@ -129,6 +170,7 @@ void mapperMBC3RAM(u16,u8);
u8 mapperMBC3ReadRAM(u16);
void mapperMBC5ROM(u16,u8);
void mapperMBC5RAM(u16,u8);
u8 mapperMBC5ReadRAM(u16);
void mapperMBC7ROM(u16,u8);
void mapperMBC7RAM(u16,u8);
u8 mapperMBC7ReadRAM(u16);
@ -137,7 +179,13 @@ void mapperHuC1RAM(u16,u8);
void mapperHuC3ROM(u16,u8);
void mapperHuC3RAM(u16,u8);
u8 mapperHuC3ReadRAM(u16);
void mapperTAMA5RAM(u16,u8);
u8 mapperTAMA5ReadRAM(u16);
void memoryUpdateTAMA5Clock();
void mapperMMM01ROM(u16,u8);
void mapperMMM01RAM(u16,u8);
void mapperGGROM(u16,u8);
void mapperGS3ROM(u16,u8);
//extern void (*mapper)(u16,u8);
//extern void (*mapperRAM)(u16,u8);
//extern u8 (*mapperReadRAM)(u16);
@ -149,7 +197,9 @@ extern void memoryUpdateMapMBC5();
extern void memoryUpdateMapMBC7();
extern void memoryUpdateMapHuC1();
extern void memoryUpdateMapHuC3();
extern void memoryUpdateMapTAMA5();
extern void memoryUpdateMapMMM01();
extern void memoryUpdateMapGS3();

View File

@ -33,13 +33,12 @@ bool gbPrinterCheckCRC()
{
u16 crc = 0;
for(int i = 2; i < (6+gbPrinterDataSize); i++)
{
crc += gbPrinterPacket[i];
}
for(int i = 2; i < (6+gbPrinterDataSize); i++) {
crc += gbPrinterPacket[i];
}
int msgCrc = gbPrinterPacket[6+gbPrinterDataSize] +
(gbPrinterPacket[7+gbPrinterDataSize]<<8);
(gbPrinterPacket[7+gbPrinterDataSize]<<8);
return msgCrc == crc;
}
@ -106,144 +105,125 @@ void gbPrinterShowData()
void gbPrinterReceiveData()
{
if(gbPrinterPacket[3])
{ // compressed
u8 *data = &gbPrinterPacket[6];
u8 *dest = &gbPrinterData[gbPrinterDataCount];
int len = 0;
while(len < gbPrinterDataSize)
{
u8 control = *data++;
if(control & 0x80)
{ // repeated data
control &= 0x7f;
control += 2;
memset(dest, *data++, control);
len += control;
dest += control;
}
else
{ // raw data
control++;
memcpy(dest, data, control);
dest += control;
data += control;
len += control;
}
}
}
else
{
memcpy(&gbPrinterData[gbPrinterDataCount],
&gbPrinterPacket[6],
gbPrinterDataSize);
gbPrinterDataCount += gbPrinterDataSize;
if(gbPrinterPacket[3]) { // compressed
u8 *data = &gbPrinterPacket[6];
u8 *dest = &gbPrinterData[gbPrinterDataCount];
int len = 0;
while(len < gbPrinterDataSize) {
u8 control = *data++;
if(control & 0x80) { // repeated data
control &= 0x7f;
control += 2;
memset(dest, *data++, control);
len += control;
dest += control;
} else { // raw data
control++;
memcpy(dest, data, control);
dest += control;
data += control;
len += control;
}
}
} else {
memcpy(&gbPrinterData[gbPrinterDataCount],
&gbPrinterPacket[6],
gbPrinterDataSize);
gbPrinterDataCount += gbPrinterDataSize;
}
}
void gbPrinterCommand()
{
switch(gbPrinterPacket[2])
{
case 0x01:
// reset/initialize packet
gbPrinterDataCount = 0;
gbPrinterStatus = 0;
break;
case 0x02:
// print packet
gbPrinterShowData();
break;
case 0x04:
// data packet
gbPrinterReceiveData();
break;
case 0x0f:
// NUL packet
break;
}
switch(gbPrinterPacket[2]) {
case 0x01:
// reset/initialize packet
gbPrinterDataCount = 0;
gbPrinterStatus = 0;
break;
case 0x02:
// print packet
gbPrinterShowData();
break;
case 0x04:
// data packet
gbPrinterReceiveData();
break;
case 0x0f:
// NUL packet
break;
}
}
u8 gbPrinterSend(u8 b)
{
switch(gbPrinterState)
{
case 0:
gbPrinterCount = 0;
// receiving preamble
if(b == 0x88)
{
gbPrinterPacket[gbPrinterCount++] = b;
gbPrinterState++;
}
else
{
// todo: handle failure
gbPrinterReset();
}
break;
case 1:
// receiving preamble
if(b == 0x33)
{
gbPrinterPacket[gbPrinterCount++] = b;
gbPrinterState++;
}
else
{
// todo: handle failure
gbPrinterReset();
}
break;
case 2:
// receiving header
gbPrinterPacket[gbPrinterCount++] = b;
if(gbPrinterCount == 6)
{
gbPrinterState++;
gbPrinterDataSize = gbPrinterPacket[4] + (gbPrinterPacket[5]<<8);
}
break;
case 3:
// receiving data
if(gbPrinterDataSize)
{
gbPrinterPacket[gbPrinterCount++] = b;
if(gbPrinterCount == (6+gbPrinterDataSize))
{
gbPrinterState++;
}
break;
}
gbPrinterState++;
// intentionally move to next if no data to receive
case 4:
// receiving CRC
switch(gbPrinterState) {
case 0:
gbPrinterCount = 0;
// receiving preamble
if(b == 0x88) {
gbPrinterPacket[gbPrinterCount++] = b;
gbPrinterState++;
break;
case 5:
// receiving CRC-2
} else {
// todo: handle failure
gbPrinterReset();
}
break;
case 1:
// receiving preamble
if(b == 0x33) {
gbPrinterPacket[gbPrinterCount++] = b;
if(gbPrinterCheckCRC())
{
gbPrinterCommand();
}
gbPrinterState++;
break;
case 6:
// receiving dummy 1
gbPrinterPacket[gbPrinterCount++] = b;
gbPrinterResult = 0x81;
} else {
// todo: handle failure
gbPrinterReset();
}
break;
case 2:
// receiving header
gbPrinterPacket[gbPrinterCount++] = b;
if(gbPrinterCount == 6) {
gbPrinterState++;
break;
case 7:
// receiving dummy 2
gbPrinterDataSize = gbPrinterPacket[4] + (gbPrinterPacket[5]<<8);
}
break;
case 3:
// receiving data
if(gbPrinterDataSize) {
gbPrinterPacket[gbPrinterCount++] = b;
gbPrinterResult = gbPrinterStatus;
gbPrinterState = 0;
gbPrinterCount = 0;
if(gbPrinterCount == (6+gbPrinterDataSize)) {
gbPrinterState++;
}
break;
}
gbPrinterState++;
// intentionally move to next if no data to receive
case 4:
// receiving CRC
gbPrinterPacket[gbPrinterCount++] = b;
gbPrinterState++;
break;
case 5:
// receiving CRC-2
gbPrinterPacket[gbPrinterCount++] = b;
if(gbPrinterCheckCRC()) {
gbPrinterCommand();
}
gbPrinterState++;
break;
case 6:
// receiving dummy 1
gbPrinterPacket[gbPrinterCount++] = b;
gbPrinterResult = 0x81;
gbPrinterState++;
break;
case 7:
// receiving dummy 2
gbPrinterPacket[gbPrinterCount++] = b;
gbPrinterResult = gbPrinterStatus;
gbPrinterState = 0;
gbPrinterCount = 0;
break;
}
return gbPrinterResult;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +1,39 @@
// -*- C++ -*-
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
// Copyright (C) 1999-2003 Forgotten
// Copyright (C) 2004 Forgotten and the VBA development team
// GB sound emulation
// 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef VBA_GBSOUND_H
#define VBA_GBSOUND_H
// See Sound.h for sound setup/options
//// GB sound options
// Sets sample rate to 44100 / quality
void gbSoundSetQuality( int quality );
extern int soundQuality; // current sound quality
// Manages declicking mode. When enabled, clicks are reduced. Note that clicks
// are normal for GB and GBC sound hardware.
void gbSoundSetDeclicking( bool enable );
bool gbSoundGetDeclicking();
// Effects configuration
struct gb_effects_config_t
{
bool enabled; // false = disable all effects
float echo; // 0.0 = none, 1.0 = lots
float stereo; // 0.0 = channels in center, 1.0 = channels on left/right
bool surround; // true = put some channels in back
};
// Changes effects configuration
void gbSoundConfigEffects( gb_effects_config_t const& );
extern gb_effects_config_t gb_effects_config; // current configuration
//// GB sound emulation
// GB sound registers
#define NR10 0xff10
#define NR11 0xff11
#define NR12 0xff12
@ -39,21 +56,23 @@
#define NR51 0xff25
#define NR52 0xff26
#define SOUND_EVENT(address,value) \
gbSoundEvent(address,value)
// Resets emulated sound hardware
void gbSoundReset();
extern void gbSoundTick();
extern void gbSoundPause();
extern void gbSoundResume();
extern void gbSoundEnable(int);
extern void gbSoundDisable(int);
extern int gbSoundGetEnable();
extern void gbSoundReset();
extern void gbSoundSaveGame(gzFile);
extern void gbSoundReadGame(int,gzFile);
extern void gbSoundEvent(register u16, register int);
extern void gbSoundSetQuality(int);
// Emulates write to sound hardware
void gbSoundEvent( u16 address, int data );
#define SOUND_EVENT gbSoundEvent
extern int soundTicks;
extern int soundQuality;
extern int SOUND_CLOCK_TICKS;
// Emulates read from sound hardware
u8 gbSoundRead( u16 address );
// Notifies emulator that SOUND_CLOCK_TICKS clocks have passed
void gbSoundTick();
extern int SOUND_CLOCK_TICKS; // Number of 16.8 MHz clocks between calls to gbSoundTick()
extern int soundTicks; // Number of 16.8 MHz clocks until gbSoundTick() will be called
// Saves/loads emulator state
void gbSoundSaveGame( gzFile out );
void gbSoundReadGame( int version, gzFile in );
#endif

View File

@ -0,0 +1,465 @@
// Blip_Buffer 0.4.1. http://www.slack.net/~ant/
#include "Blip_Buffer.h"
#include <assert.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
/* Copyright (C) 2003-2007 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module 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 Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
// TODO: use scoped for variables in treble_eq()
#ifdef BLARGG_ENABLE_OPTIMIZER
#include BLARGG_ENABLE_OPTIMIZER
#endif
int const silent_buf_size = 1; // size used for Silent_Blip_Buffer
Blip_Buffer::Blip_Buffer()
{
factor_ = LONG_MAX;
buffer_ = 0;
buffer_size_ = 0;
sample_rate_ = 0;
bass_shift_ = 0;
clock_rate_ = 0;
bass_freq_ = 16;
length_ = 0;
// assumptions code makes about implementation-defined features
#ifndef NDEBUG
// right shift of negative value preserves sign
buf_t_ i = -0x7FFFFFFE;
assert( (i >> 1) == -0x3FFFFFFF );
// casting to short truncates to 16 bits and sign-extends
i = 0x18000;
assert( (short) i == -0x8000 );
#endif
clear();
}
Blip_Buffer::~Blip_Buffer()
{
if ( buffer_size_ != silent_buf_size )
free( buffer_ );
}
Silent_Blip_Buffer::Silent_Blip_Buffer()
{
factor_ = 0;
buffer_ = buf;
buffer_size_ = silent_buf_size;
clear();
}
void Blip_Buffer::clear( int entire_buffer )
{
offset_ = 0;
reader_accum_ = 0;
modified_ = 0;
if ( buffer_ )
{
long count = (entire_buffer ? buffer_size_ : samples_avail());
memset( buffer_, 0, (count + blip_buffer_extra_) * sizeof (buf_t_) );
}
}
Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec )
{
if ( buffer_size_ == silent_buf_size )
{
assert( 0 );
return "Internal (tried to resize Silent_Blip_Buffer)";
}
// start with maximum length that resampled time can represent
long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - blip_buffer_extra_ - 64;
if ( msec != blip_max_length )
{
long s = (new_rate * (msec + 1) + 999) / 1000;
if ( s < new_size )
new_size = s;
else
assert( 0 ); // fails if requested buffer length exceeds limit
}
if ( buffer_size_ != new_size )
{
void* p = realloc( buffer_, (new_size + blip_buffer_extra_) * sizeof *buffer_ );
if ( !p )
return "Out of memory";
buffer_ = (buf_t_*) p;
}
buffer_size_ = new_size;
assert( buffer_size_ != silent_buf_size ); // size should never happen to match this
// update things based on the sample rate
sample_rate_ = new_rate;
length_ = new_size * 1000 / new_rate - 1;
if ( msec )
assert( length_ == msec ); // ensure length is same as that passed in
// update these since they depend on sample rate
if ( clock_rate_ )
clock_rate( clock_rate_ );
bass_freq( bass_freq_ );
clear();
return 0; // success
}
blip_resampled_time_t Blip_Buffer::clock_rate_factor( long rate ) const
{
double ratio = (double) sample_rate_ / rate;
blip_long factor = (blip_long) floor( ratio * (1L << BLIP_BUFFER_ACCURACY) + 0.5 );
assert( factor > 0 || !sample_rate_ ); // fails if clock/output ratio is too large
return (blip_resampled_time_t) factor;
}
void Blip_Buffer::bass_freq( int freq )
{
bass_freq_ = freq;
int shift = 31;
if ( freq > 0 )
{
shift = 13;
long f = (freq << 16) / sample_rate_;
while ( (f >>= 1) && --shift ) { }
}
bass_shift_ = shift;
}
void Blip_Buffer::end_frame( blip_time_t t )
{
offset_ += t * factor_;
assert( samples_avail() <= (long) buffer_size_ ); // fails if time is past end of buffer
}
long Blip_Buffer::count_samples( blip_time_t t ) const
{
blip_resampled_time_t last_sample = resampled_time( t ) >> BLIP_BUFFER_ACCURACY;
blip_resampled_time_t first_sample = offset_ >> BLIP_BUFFER_ACCURACY;
return long (last_sample - first_sample);
}
blip_time_t Blip_Buffer::count_clocks( long count ) const
{
if ( !factor_ )
{
assert( 0 ); // sample rate and clock rates must be set first
return 0;
}
if ( count > buffer_size_ )
count = buffer_size_;
blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
return (blip_time_t) ((time - offset_ + factor_ - 1) / factor_);
}
void Blip_Buffer::remove_samples( long count )
{
if ( count )
{
remove_silence( count );
// copy remaining samples to beginning and clear old samples
long remain = samples_avail() + blip_buffer_extra_;
memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ );
memset( buffer_ + remain, 0, count * sizeof *buffer_ );
}
}
// Blip_Synth_
Blip_Synth_Fast_::Blip_Synth_Fast_()
{
buf = 0;
last_amp = 0;
delta_factor = 0;
}
void Blip_Synth_Fast_::volume_unit( double new_unit )
{
delta_factor = int (new_unit * (1L << blip_sample_bits) + 0.5);
}
#if !BLIP_BUFFER_FAST
Blip_Synth_::Blip_Synth_( short* p, int w ) :
impulses( p ),
width( w )
{
volume_unit_ = 0.0;
kernel_unit = 0;
buf = 0;
last_amp = 0;
delta_factor = 0;
}
#undef PI
#define PI 3.1415926535897932384626433832795029
static void gen_sinc( float* out, int count, double oversample, double treble, double cutoff )
{
if ( cutoff >= 0.999 )
cutoff = 0.999;
if ( treble < -300.0 )
treble = -300.0;
if ( treble > 5.0 )
treble = 5.0;
double const maxh = 4096.0;
double const rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - cutoff) );
double const pow_a_n = pow( rolloff, maxh - maxh * cutoff );
double const to_angle = PI / 2 / maxh / oversample;
for ( int i = 0; i < count; i++ )
{
double angle = ((i - count) * 2 + 1) * to_angle;
double c = rolloff * cos( (maxh - 1.0) * angle ) - cos( maxh * angle );
double cos_nc_angle = cos( maxh * cutoff * angle );
double cos_nc1_angle = cos( (maxh * cutoff - 1.0) * angle );
double cos_angle = cos( angle );
c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle;
double d = 1.0 + rolloff * (rolloff - cos_angle - cos_angle);
double b = 2.0 - cos_angle - cos_angle;
double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle;
out [i] = (float) ((a * d + c * b) / (b * d)); // a / b + c / d
}
}
void blip_eq_t::generate( float* out, int count ) const
{
// lower cutoff freq for narrow kernels with their wider transition band
// (8 points->1.49, 16 points->1.15)
double oversample = blip_res * 2.25 / count + 0.85;
double half_rate = sample_rate * 0.5;
if ( cutoff_freq )
oversample = half_rate / cutoff_freq;
double cutoff = rolloff_freq * oversample / half_rate;
gen_sinc( out, count, blip_res * oversample, treble, cutoff );
// apply (half of) hamming window
double to_fraction = PI / (count - 1);
for ( int i = count; i--; )
out [i] *= 0.54f - 0.46f * (float) cos( i * to_fraction );
}
void Blip_Synth_::adjust_impulse()
{
// sum pairs for each phase and add error correction to end of first half
int const size = impulses_size();
for ( int p = blip_res; p-- >= blip_res / 2; )
{
int p2 = blip_res - 2 - p;
long error = kernel_unit;
for ( int i = 1; i < size; i += blip_res )
{
error -= impulses [i + p ];
error -= impulses [i + p2];
}
if ( p == p2 )
error /= 2; // phase = 0.5 impulse uses same half for both sides
impulses [size - blip_res + p] += (short) error;
//printf( "error: %ld\n", error );
}
//for ( int i = blip_res; i--; printf( "\n" ) )
// for ( int j = 0; j < width / 2; j++ )
// printf( "%5ld,", impulses [j * blip_res + i + 1] );
}
void Blip_Synth_::treble_eq( blip_eq_t const& eq )
{
float fimpulse [blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2];
int const half_size = blip_res / 2 * (width - 1);
eq.generate( &fimpulse [blip_res], half_size );
int i;
// need mirror slightly past center for calculation
for ( i = blip_res; i--; )
fimpulse [blip_res + half_size + i] = fimpulse [blip_res + half_size - 1 - i];
// starts at 0
for ( i = 0; i < blip_res; i++ )
fimpulse [i] = 0.0f;
// find rescale factor
double total = 0.0;
for ( i = 0; i < half_size; i++ )
total += fimpulse [blip_res + i];
//double const base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB
//double const base_unit = 37888.0; // allows treble to +5 dB
double const base_unit = 32768.0; // necessary for blip_unscaled to work
double rescale = base_unit / 2 / total;
kernel_unit = (long) base_unit;
// integrate, first difference, rescale, convert to int
double sum = 0.0;
double next = 0.0;
int const size = this->impulses_size();
for ( i = 0; i < size; i++ )
{
impulses [i] = (short) (int) floor( (next - sum) * rescale + 0.5 );
sum += fimpulse [i];
next += fimpulse [i + blip_res];
}
adjust_impulse();
// volume might require rescaling
double vol = volume_unit_;
if ( vol )
{
volume_unit_ = 0.0;
volume_unit( vol );
}
}
void Blip_Synth_::volume_unit( double new_unit )
{
if ( new_unit != volume_unit_ )
{
// use default eq if it hasn't been set yet
if ( !kernel_unit )
treble_eq( -8.0 );
volume_unit_ = new_unit;
double factor = new_unit * (1L << blip_sample_bits) / kernel_unit;
if ( factor > 0.0 )
{
int shift = 0;
// if unit is really small, might need to attenuate kernel
while ( factor < 2.0 )
{
shift++;
factor *= 2.0;
}
if ( shift )
{
kernel_unit >>= shift;
assert( kernel_unit > 0 ); // fails if volume unit is too low
// keep values positive to avoid round-towards-zero of sign-preserving
// right shift for negative values
long offset = 0x8000 + (1 << (shift - 1));
long offset2 = 0x8000 >> shift;
for ( int i = impulses_size(); i--; )
impulses [i] = (short) (int) (((impulses [i] + offset) >> shift) - offset2);
adjust_impulse();
}
}
delta_factor = (int) floor( factor + 0.5 );
//printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit );
}
}
#endif
long Blip_Buffer::read_samples( blip_sample_t* out_, long max_samples, int stereo )
{
long count = samples_avail();
if ( count > max_samples )
count = max_samples;
if ( count )
{
int const bass = BLIP_READER_BASS( *this );
BLIP_READER_BEGIN( reader, *this );
BLIP_READER_ADJ_( reader, count );
blip_sample_t* BLIP_RESTRICT out = out_ + count;
blip_long offset = (blip_long) -count;
if ( !stereo )
{
do
{
blip_long s = BLIP_READER_READ( reader );
BLIP_READER_NEXT_IDX_( reader, bass, offset );
BLIP_CLAMP( s, s );
out [offset] = (blip_sample_t) s;
}
while ( ++offset );
}
else
{
do
{
blip_long s = BLIP_READER_READ( reader );
BLIP_READER_NEXT_IDX_( reader, bass, offset );
BLIP_CLAMP( s, s );
out [offset * 2] = (blip_sample_t) s;
}
while ( ++offset );
}
BLIP_READER_END( reader, *this );
remove_samples( count );
}
return count;
}
void Blip_Buffer::mix_samples( blip_sample_t const* in, long count )
{
if ( buffer_size_ == silent_buf_size )
{
assert( 0 );
return;
}
buf_t_* out = buffer_ + (offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2;
int const sample_shift = blip_sample_bits - 16;
int prev = 0;
while ( count-- )
{
blip_long s = (blip_long) *in++ << sample_shift;
*out += s - prev;
prev = s;
++out;
}
*out -= prev;
}
blip_ulong const subsample_mask = (1L << BLIP_BUFFER_ACCURACY) - 1;
void Blip_Buffer::save_state( blip_buffer_state_t* out )
{
assert( samples_avail() == 0 );
out->offset_ = offset_;
out->reader_accum_ = reader_accum_;
memcpy( out->buf, &buffer_ [offset_ >> BLIP_BUFFER_ACCURACY], sizeof out->buf );
}
void Blip_Buffer::load_state( blip_buffer_state_t const& in )
{
clear( false );
offset_ = in.offset_;
reader_accum_ = in.reader_accum_;
memcpy( buffer_, in.buf, sizeof in.buf );
}

View File

@ -0,0 +1,556 @@
// Band-limited sound synthesis buffer
// Blip_Buffer 0.4.1
#ifndef BLIP_BUFFER_H
#define BLIP_BUFFER_H
// internal
#include <limits.h>
#if INT_MAX < 0x7FFFFFFF || LONG_MAX == 0x7FFFFFFF
typedef long blip_long;
typedef unsigned long blip_ulong;
#else
typedef int blip_long;
typedef unsigned blip_ulong;
#endif
// Time unit at source clock rate
typedef blip_long blip_time_t;
// Output samples are 16-bit signed, with a range of -32768 to 32767
typedef short blip_sample_t;
enum { blip_sample_max = 32767 };
struct blip_buffer_state_t;
class Blip_Buffer {
public:
typedef const char* blargg_err_t;
// Sets output sample rate and buffer length in milliseconds (1/1000 sec, defaults
// to 1/4 second) and clears buffer. If there isn't enough memory, leaves buffer
// untouched and returns "Out of memory", otherwise returns NULL.
blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 );
// Sets number of source time units per second
void clock_rate( long clocks_per_sec );
// Ends current time frame of specified duration and makes its samples available
// (along with any still-unread samples) for reading with read_samples(). Begins
// a new time frame at the end of the current frame.
void end_frame( blip_time_t time );
// Reads at most 'max_samples' out of buffer into 'dest', removing them from from
// the buffer. Returns number of samples actually read and removed. If stereo is
// true, increments 'dest' one extra time after writing each sample, to allow
// easy interleving of two channels into a stereo output buffer.
long read_samples( blip_sample_t* dest, long max_samples, int stereo = 0 );
// Additional features
// Removes all available samples and clear buffer to silence. If 'entire_buffer' is
// false, just clears out any samples waiting rather than the entire buffer.
void clear( int entire_buffer = 1 );
// Number of samples available for reading with read_samples()
long samples_avail() const;
// Removes 'count' samples from those waiting to be read
void remove_samples( long count );
// Sets frequency high-pass filter frequency, where higher values reduce bass more
void bass_freq( int frequency );
// Current output sample rate
long sample_rate() const;
// Length of buffer in milliseconds
int length() const;
// Number of source time units per second
long clock_rate() const;
// Experimental features
// Saves state, including high-pass filter and tails of last deltas.
// All samples must have been read from buffer before calling this.
void save_state( blip_buffer_state_t* out );
// Loads state. State must have been saved from Blip_Buffer with same
// settings during same run of program. States can NOT be stored on disk.
// Clears buffer before loading state.
void load_state( blip_buffer_state_t const& in );
// Number of samples delay from synthesis to samples read out
int output_latency() const;
// Counts number of clocks needed until 'count' samples will be available.
// If buffer can't even hold 'count' samples, returns number of clocks until
// buffer becomes full.
blip_time_t count_clocks( long count ) const;
// Number of raw samples that can be mixed within frame of specified duration.
long count_samples( blip_time_t duration ) const;
// Mixes in 'count' samples from 'buf_in'
void mix_samples( blip_sample_t const* buf_in, long count );
// Signals that sound has been added to buffer. Could be done automatically in
// Blip_Synth, but that would affect performance more, as you can arrange that
// this is called only once per time frame rather than for every delta.
void set_modified() { modified_ = this; }
// not documented yet
blip_ulong unsettled() const;
Blip_Buffer* clear_modified() { Blip_Buffer* b = modified_; modified_ = 0; return b; }
void remove_silence( long count );
typedef blip_ulong blip_resampled_time_t;
blip_resampled_time_t resampled_duration( int t ) const { return t * factor_; }
blip_resampled_time_t resampled_time( blip_time_t t ) const { return t * factor_ + offset_; }
blip_resampled_time_t clock_rate_factor( long clock_rate ) const;
public:
Blip_Buffer();
~Blip_Buffer();
// Deprecated
typedef blip_resampled_time_t resampled_time_t;
blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); }
blargg_err_t sample_rate( long r, int msec ) { return set_sample_rate( r, msec ); }
private:
// noncopyable
Blip_Buffer( const Blip_Buffer& );
Blip_Buffer& operator = ( const Blip_Buffer& );
public:
typedef blip_long buf_t_;
blip_ulong factor_;
blip_resampled_time_t offset_;
buf_t_* buffer_;
blip_long buffer_size_;
blip_long reader_accum_;
int bass_shift_;
private:
long sample_rate_;
long clock_rate_;
int bass_freq_;
int length_;
Blip_Buffer* modified_; // non-zero = true (more optimal than using bool, heh)
friend class Blip_Reader;
};
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
// Number of bits in resample ratio fraction. Higher values give a more accurate ratio
// but reduce maximum buffer size.
#ifndef BLIP_BUFFER_ACCURACY
#define BLIP_BUFFER_ACCURACY 16
#endif
// Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in
// noticeable broadband noise when synthesizing high frequency square waves.
// Affects size of Blip_Synth objects since they store the waveform directly.
#ifndef BLIP_PHASE_BITS
#if BLIP_BUFFER_FAST
#define BLIP_PHASE_BITS 8
#else
#define BLIP_PHASE_BITS 6
#endif
#endif
// Internal
typedef blip_ulong blip_resampled_time_t;
int const blip_widest_impulse_ = 16;
int const blip_buffer_extra_ = blip_widest_impulse_ + 2;
int const blip_res = 1 << BLIP_PHASE_BITS;
class blip_eq_t;
class Blip_Synth_Fast_ {
public:
Blip_Buffer* buf;
int last_amp;
int delta_factor;
void volume_unit( double );
Blip_Synth_Fast_();
void treble_eq( blip_eq_t const& ) { }
};
class Blip_Synth_ {
public:
Blip_Buffer* buf;
int last_amp;
int delta_factor;
void volume_unit( double );
Blip_Synth_( short* impulses, int width );
void treble_eq( blip_eq_t const& );
private:
double volume_unit_;
short* const impulses;
int const width;
blip_long kernel_unit;
int impulses_size() const { return blip_res / 2 * width + 1; }
void adjust_impulse();
};
// Quality level, better = slower. In general, use blip_good_quality.
const int blip_med_quality = 8;
const int blip_good_quality = 12;
const int blip_high_quality = 16;
// Range specifies the greatest expected change in amplitude. Calculate it
// by finding the difference between the maximum and minimum expected
// amplitudes (max - min).
template<int quality,int range>
class Blip_Synth {
public:
// Sets overall volume of waveform
void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); }
// Configures low-pass filter (see blip_buffer.txt)
void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); }
// Gets/sets Blip_Buffer used for output
Blip_Buffer* output() const { return impl.buf; }
void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; }
// Updates amplitude of waveform at given time. Using this requires a separate
// Blip_Synth for each waveform.
void update( blip_time_t time, int amplitude );
// Low-level interface
// Adds an amplitude transition of specified delta, optionally into specified buffer
// rather than the one set with output(). Delta can be positive or negative.
// The actual change in amplitude is delta * (volume / range)
void offset( blip_time_t, int delta, Blip_Buffer* ) const;
void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); }
// Works directly in terms of fractional output samples. Contact author for more info.
void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const;
// Same as offset(), except code is inlined for higher performance
void offset_inline( blip_time_t t, int delta, Blip_Buffer* buf ) const {
offset_resampled( t * buf->factor_ + buf->offset_, delta, buf );
}
void offset_inline( blip_time_t t, int delta ) const {
offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf );
}
private:
#if BLIP_BUFFER_FAST
Blip_Synth_Fast_ impl;
#else
Blip_Synth_ impl;
typedef short imp_t;
imp_t impulses [blip_res * (quality / 2) + 1];
public:
Blip_Synth() : impl( impulses, quality ) { }
#endif
};
// Low-pass equalization parameters
class blip_eq_t {
public:
// Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce
// treble, small positive values (0 to 5.0) increase treble.
blip_eq_t( double treble_db = 0 );
// See blip_buffer.txt
blip_eq_t( double treble, long rolloff_freq, long sample_rate, long cutoff_freq = 0 );
private:
double treble;
long rolloff_freq;
long sample_rate;
long cutoff_freq;
void generate( float* out, int count ) const;
friend class Blip_Synth_;
};
int const blip_sample_bits = 30;
// Dummy Blip_Buffer to direct sound output to, for easy muting without
// having to stop sound code.
class Silent_Blip_Buffer : public Blip_Buffer {
buf_t_ buf [blip_buffer_extra_ + 1];
public:
// The following cannot be used (an assertion will fail if attempted):
blargg_err_t set_sample_rate( long samples_per_sec, int msec_length );
blip_time_t count_clocks( long count ) const;
void mix_samples( blip_sample_t const* buf, long count );
Silent_Blip_Buffer();
};
#if __GNUC__ >= 3 || _MSC_VER >= 1100
#define BLIP_RESTRICT __restrict
#else
#define BLIP_RESTRICT
#endif
// Optimized reading from Blip_Buffer, for use in custom sample output
// Begins reading from buffer. Name should be unique to the current block.
#define BLIP_READER_BEGIN( name, blip_buffer ) \
const Blip_Buffer::buf_t_* BLIP_RESTRICT name##_reader_buf = (blip_buffer).buffer_;\
blip_long name##_reader_accum = (blip_buffer).reader_accum_
// Gets value to pass to BLIP_READER_NEXT()
#define BLIP_READER_BASS( blip_buffer ) ((blip_buffer).bass_shift_)
// Constant value to use instead of BLIP_READER_BASS(), for slightly more optimal
// code at the cost of having no bass control
int const blip_reader_default_bass = 9;
// Current sample
#define BLIP_READER_READ( name ) (name##_reader_accum >> (blip_sample_bits - 16))
// Current raw sample in full internal resolution
#define BLIP_READER_READ_RAW( name ) (name##_reader_accum)
// Advances to next sample
#define BLIP_READER_NEXT( name, bass ) \
(void) (name##_reader_accum += *name##_reader_buf++ - (name##_reader_accum >> (bass)))
// Ends reading samples from buffer. The number of samples read must now be removed
// using Blip_Buffer::remove_samples().
#define BLIP_READER_END( name, blip_buffer ) \
(void) ((blip_buffer).reader_accum_ = name##_reader_accum)
// experimental
#define BLIP_READER_ADJ_( name, offset ) (name##_reader_buf += offset)
blip_long const blip_reader_idx_factor = sizeof (Blip_Buffer::buf_t_);
#define BLIP_READER_NEXT_IDX_( name, bass, idx ) {\
name##_reader_accum -= name##_reader_accum >> (bass);\
name##_reader_accum += name##_reader_buf [(idx)];\
}
#define BLIP_READER_NEXT_RAW_IDX_( name, bass, idx ) {\
name##_reader_accum -= name##_reader_accum >> (bass);\
name##_reader_accum +=\
*(Blip_Buffer::buf_t_ const*) ((char const*) name##_reader_buf + (idx));\
}
// Compatibility with older version
const long blip_unscaled = 65535;
const int blip_low_quality = blip_med_quality;
const int blip_best_quality = blip_high_quality;
// Deprecated; use BLIP_READER macros as follows:
// Blip_Reader r; r.begin( buf ); -> BLIP_READER_BEGIN( r, buf );
// int bass = r.begin( buf ) -> BLIP_READER_BEGIN( r, buf ); int bass = BLIP_READER_BASS( buf );
// r.read() -> BLIP_READER_READ( r )
// r.read_raw() -> BLIP_READER_READ_RAW( r )
// r.next( bass ) -> BLIP_READER_NEXT( r, bass )
// r.next() -> BLIP_READER_NEXT( r, blip_reader_default_bass )
// r.end( buf ) -> BLIP_READER_END( r, buf )
class Blip_Reader {
public:
int begin( Blip_Buffer& );
blip_long read() const { return accum >> (blip_sample_bits - 16); }
blip_long read_raw() const { return accum; }
void next( int bass_shift = 9 ) { accum += *buf++ - (accum >> bass_shift); }
void end( Blip_Buffer& b ) { b.reader_accum_ = accum; }
private:
const Blip_Buffer::buf_t_* buf;
blip_long accum;
};
#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \
defined (__x86_64__) || defined (__ia64__) || defined (__i386__)
#define BLIP_CLAMP_( in ) in < -0x8000 || 0x7FFF < in
#else
#define BLIP_CLAMP_( in ) (blip_sample_t) in != in
#endif
// Clamp sample to blip_sample_t range
#define BLIP_CLAMP( sample, out )\
{ if ( BLIP_CLAMP_( (sample) ) ) (out) = ((sample) >> 24) ^ 0x7FFF; }
struct blip_buffer_state_t
{
blip_resampled_time_t offset_;
blip_long reader_accum_;
blip_long buf [blip_buffer_extra_];
};
// End of public interface
#ifndef assert
#include <assert.h>
#endif
template<int quality,int range>
inline void Blip_Synth<quality,range>::offset_resampled( blip_resampled_time_t time,
int delta, Blip_Buffer* blip_buf ) const
{
// If this assertion fails, it means that an attempt was made to add a delta
// at a negative time or past the end of the buffer.
assert( (blip_long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ );
delta *= impl.delta_factor;
blip_long* BLIP_RESTRICT buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY);
int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1));
#if BLIP_BUFFER_FAST
blip_long left = buf [0] + delta;
// Kind of crappy, but doing shift after multiply results in overflow.
// Alternate way of delaying multiply by delta_factor results in worse
// sub-sample resolution.
blip_long right = (delta >> BLIP_PHASE_BITS) * phase;
left -= right;
right += buf [1];
buf [0] = left;
buf [1] = right;
#else
int const fwd = (blip_widest_impulse_ - quality) / 2;
int const rev = fwd + quality - 2;
int const mid = quality / 2 - 1;
imp_t const* BLIP_RESTRICT imp = impulses + blip_res - phase;
#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \
defined (__x86_64__) || defined (__ia64__) || defined (__i386__)
// this straight forward version gave in better code on GCC for x86
#define ADD_IMP( out, in ) \
buf [out] += (blip_long) imp [blip_res * (in)] * delta
#define BLIP_FWD( i ) {\
ADD_IMP( fwd + i, i );\
ADD_IMP( fwd + 1 + i, i + 1 );\
}
#define BLIP_REV( r ) {\
ADD_IMP( rev - r, r + 1 );\
ADD_IMP( rev + 1 - r, r );\
}
BLIP_FWD( 0 )
if ( quality > 8 ) BLIP_FWD( 2 )
if ( quality > 12 ) BLIP_FWD( 4 )
{
ADD_IMP( fwd + mid - 1, mid - 1 );
ADD_IMP( fwd + mid , mid );
imp = impulses + phase;
}
if ( quality > 12 ) BLIP_REV( 6 )
if ( quality > 8 ) BLIP_REV( 4 )
BLIP_REV( 2 )
ADD_IMP( rev , 1 );
ADD_IMP( rev + 1, 0 );
#undef ADD_IMP
#else
// for RISC processors, help compiler by reading ahead of writes
#define BLIP_FWD( i ) {\
blip_long t0 = i0 * delta + buf [fwd + i];\
blip_long t1 = imp [blip_res * (i + 1)] * delta + buf [fwd + 1 + i];\
i0 = imp [blip_res * (i + 2)];\
buf [fwd + i] = t0;\
buf [fwd + 1 + i] = t1;\
}
#define BLIP_REV( r ) {\
blip_long t0 = i0 * delta + buf [rev - r];\
blip_long t1 = imp [blip_res * r] * delta + buf [rev + 1 - r];\
i0 = imp [blip_res * (r - 1)];\
buf [rev - r] = t0;\
buf [rev + 1 - r] = t1;\
}
blip_long i0 = *imp;
BLIP_FWD( 0 )
if ( quality > 8 ) BLIP_FWD( 2 )
if ( quality > 12 ) BLIP_FWD( 4 )
{
blip_long t0 = i0 * delta + buf [fwd + mid - 1];
blip_long t1 = imp [blip_res * mid] * delta + buf [fwd + mid ];
imp = impulses + phase;
i0 = imp [blip_res * mid];
buf [fwd + mid - 1] = t0;
buf [fwd + mid ] = t1;
}
if ( quality > 12 ) BLIP_REV( 6 )
if ( quality > 8 ) BLIP_REV( 4 )
BLIP_REV( 2 )
blip_long t0 = i0 * delta + buf [rev ];
blip_long t1 = *imp * delta + buf [rev + 1];
buf [rev ] = t0;
buf [rev + 1] = t1;
#endif
#endif
}
#undef BLIP_FWD
#undef BLIP_REV
template<int quality,int range>
#if BLIP_BUFFER_FAST
inline
#endif
void Blip_Synth<quality,range>::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const
{
offset_resampled( t * buf->factor_ + buf->offset_, delta, buf );
}
template<int quality,int range>
#if BLIP_BUFFER_FAST
inline
#endif
void Blip_Synth<quality,range>::update( blip_time_t t, int amp )
{
int delta = amp - impl.last_amp;
impl.last_amp = amp;
offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf );
}
inline blip_eq_t::blip_eq_t( double t ) :
treble( t ), rolloff_freq( 0 ), sample_rate( 44100 ), cutoff_freq( 0 ) { }
inline blip_eq_t::blip_eq_t( double t, long rf, long sr, long cf ) :
treble( t ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { }
inline int Blip_Buffer::length() const { return length_; }
inline long Blip_Buffer::samples_avail() const { return (long) (offset_ >> BLIP_BUFFER_ACCURACY); }
inline long Blip_Buffer::sample_rate() const { return sample_rate_; }
inline int Blip_Buffer::output_latency() const { return blip_widest_impulse_ / 2; }
inline long Blip_Buffer::clock_rate() const { return clock_rate_; }
inline void Blip_Buffer::clock_rate( long cps ) { factor_ = clock_rate_factor( clock_rate_ = cps ); }
inline int Blip_Reader::begin( Blip_Buffer& blip_buf )
{
buf = blip_buf.buffer_;
accum = blip_buf.reader_accum_;
return blip_buf.bass_shift_;
}
inline void Blip_Buffer::remove_silence( long count )
{
// fails if you try to remove more samples than available
assert( count <= samples_avail() );
offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
}
inline blip_ulong Blip_Buffer::unsettled() const
{
return reader_accum_ >> (blip_sample_bits - 16);
}
int const blip_max_length = 0;
int const blip_default_length = 250; // 1/4 second
#endif

View File

@ -0,0 +1,638 @@
// Game_Music_Emu $vers. http://www.slack.net/~ant/
#include "Effects_Buffer.h"
#include <string.h>
/* Copyright (C) 2006-2007 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module 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 Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
int const fixed_shift = 12;
#define TO_FIXED( f ) fixed_t ((f) * ((fixed_t) 1 << fixed_shift))
#define FROM_FIXED( f ) ((f) >> fixed_shift)
int const max_read = 2560; // determines minimum delay
Effects_Buffer::Effects_Buffer( int max_bufs, long echo_size_ ) : Multi_Buffer( stereo )
{
echo_size = max( max_read * (long) stereo, echo_size_ & ~1 );
clock_rate_ = 0;
bass_freq_ = 90;
bufs = 0;
bufs_size = 0;
bufs_max = max( max_bufs, (int) extra_chans );
no_echo = true;
no_effects = true;
// defaults
config_.enabled = false;
config_.delay [0] = 120;
config_.delay [1] = 122;
config_.feedback = 0.2f;
config_.treble = 0.4f;
static float const sep = 0.8f;
config_.side_chans [0].pan = -sep;
config_.side_chans [1].pan = +sep;
config_.side_chans [0].vol = 1.0f;
config_.side_chans [1].vol = 1.0f;
memset( &s, 0, sizeof s );
clear();
}
Effects_Buffer::~Effects_Buffer()
{
delete_bufs();
}
// avoid using new []
blargg_err_t Effects_Buffer::new_bufs( int size )
{
bufs = (buf_t*) malloc( size * sizeof *bufs );
CHECK_ALLOC( bufs );
for ( int i = 0; i < size; i++ )
new (bufs + i) buf_t;
bufs_size = size;
return 0;
}
void Effects_Buffer::delete_bufs()
{
if ( bufs )
{
for ( int i = bufs_size; --i >= 0; )
bufs [i].~buf_t();
free( bufs );
bufs = 0;
}
bufs_size = 0;
}
blargg_err_t Effects_Buffer::set_sample_rate( long rate, int msec )
{
// extra to allow farther past-the-end pointers
mixer.samples_read = 0;
RETURN_ERR( echo.resize( echo_size + stereo ) );
return Multi_Buffer::set_sample_rate( rate, msec );
}
void Effects_Buffer::clock_rate( long rate )
{
clock_rate_ = rate;
for ( int i = bufs_size; --i >= 0; )
bufs [i].clock_rate( clock_rate_ );
}
void Effects_Buffer::bass_freq( int freq )
{
bass_freq_ = freq;
for ( int i = bufs_size; --i >= 0; )
bufs [i].bass_freq( bass_freq_ );
}
blargg_err_t Effects_Buffer::set_channel_count( int count, int const* types )
{
RETURN_ERR( Multi_Buffer::set_channel_count( count, types ) );
delete_bufs();
mixer.samples_read = 0;
RETURN_ERR( chans.resize( count + extra_chans ) );
RETURN_ERR( new_bufs( min( bufs_max, count + extra_chans ) ) );
for ( int i = bufs_size; --i >= 0; )
RETURN_ERR( bufs [i].set_sample_rate( sample_rate(), length() ) );
for ( int i = chans.size(); --i >= 0; )
{
chan_t& ch = chans [i];
ch.cfg.vol = 1.0f;
ch.cfg.pan = 0.0f;
ch.cfg.surround = false;
ch.cfg.echo = false;
}
// side channels with echo
chans [2].cfg.echo = true;
chans [3].cfg.echo = true;
clock_rate( clock_rate_ );
bass_freq( bass_freq_ );
apply_config();
clear();
return 0;
}
void Effects_Buffer::clear_echo()
{
if ( echo.size() )
memset( echo.begin(), 0, echo.size() * sizeof echo [0] );
}
void Effects_Buffer::clear()
{
echo_pos = 0;
s.low_pass [0] = 0;
s.low_pass [1] = 0;
mixer.samples_read = 0;
for ( int i = bufs_size; --i >= 0; )
bufs [i].clear();
clear_echo();
}
Effects_Buffer::channel_t Effects_Buffer::channel( int i )
{
i += extra_chans;
require( extra_chans <= i && i < (int) chans.size() );
return chans [i].channel;
}
// Configuration
// 3 wave positions with/without surround, 2 multi (one with same config as wave)
int const simple_bufs = 3 * 2 + 2 - 1;
Simple_Effects_Buffer::Simple_Effects_Buffer() :
Effects_Buffer( extra_chans + simple_bufs, 18 * 1024L )
{
config_.echo = 0.20f;
config_.stereo = 0.20f;
config_.surround = true;
config_.enabled = false;
}
void Simple_Effects_Buffer::apply_config()
{
Effects_Buffer::config_t& c = Effects_Buffer::config();
c.enabled = config_.enabled;
if ( c.enabled )
{
c.delay [0] = 120;
c.delay [1] = 122;
c.feedback = config_.echo * 0.7f;
c.treble = 0.6f - 0.3f * config_.echo;
float sep = config_.stereo + 0.80f;
if ( sep > 1.0f )
sep = 1.0f;
c.side_chans [0].pan = -sep;
c.side_chans [1].pan = +sep;
for ( int i = channel_count(); --i >= 0; )
{
chan_config_t& ch = Effects_Buffer::chan_config( i );
ch.pan = 0.0f;
ch.surround = config_.surround;
ch.echo = false;
int const type = (channel_types() ? channel_types() [i] : 0);
if ( !(type & noise_type) )
{
int index = (type & type_index_mask) % 6 - 3;
if ( index < 0 )
{
index += 3;
ch.surround = false;
ch.echo = true;
}
if ( index >= 1 )
{
ch.pan = config_.stereo;
if ( index == 1 )
ch.pan = -ch.pan;
}
}
else if ( type & 1 )
{
ch.surround = false;
}
}
}
Effects_Buffer::apply_config();
}
int Effects_Buffer::min_delay() const
{
require( sample_rate() );
return max_read * 1000L / sample_rate();
}
int Effects_Buffer::max_delay() const
{
require( sample_rate() );
return (echo_size / stereo - max_read) * 1000L / sample_rate();
}
void Effects_Buffer::apply_config()
{
int i;
if ( !bufs_size )
return;
s.treble = TO_FIXED( config_.treble );
bool echo_dirty = false;
fixed_t old_feedback = s.feedback;
s.feedback = TO_FIXED( config_.feedback );
if ( !old_feedback && s.feedback )
echo_dirty = true;
// delays
for ( i = stereo; --i >= 0; )
{
long delay = config_.delay [i] * sample_rate() / 1000 * stereo;
delay = max( delay, long (max_read * stereo) );
delay = min( delay, long (echo_size - max_read * stereo) );
if ( s.delay [i] != delay )
{
s.delay [i] = delay;
echo_dirty = true;
}
}
// side channels
for ( i = 2; --i >= 0; )
{
chans [i+2].cfg.vol = chans [i].cfg.vol = config_.side_chans [i].vol * 0.5f;
chans [i+2].cfg.pan = chans [i].cfg.pan = config_.side_chans [i].pan;
}
// convert volumes
for ( i = chans.size(); --i >= 0; )
{
chan_t& ch = chans [i];
ch.vol [0] = TO_FIXED( ch.cfg.vol - ch.cfg.vol * ch.cfg.pan );
ch.vol [1] = TO_FIXED( ch.cfg.vol + ch.cfg.vol * ch.cfg.pan );
if ( ch.cfg.surround )
ch.vol [0] = -ch.vol [0];
}
assign_buffers();
// set side channels
for ( i = chans.size(); --i >= 0; )
{
chan_t& ch = chans [i];
ch.channel.left = chans [ch.cfg.echo*2 ].channel.center;
ch.channel.right = chans [ch.cfg.echo*2+1].channel.center;
}
bool old_echo = !no_echo && !no_effects;
// determine whether effects and echo are needed at all
no_effects = true;
no_echo = true;
for ( i = chans.size(); --i >= extra_chans; )
{
chan_t& ch = chans [i];
if ( ch.cfg.echo && s.feedback )
no_echo = false;
if ( ch.vol [0] != TO_FIXED( 1 ) || ch.vol [1] != TO_FIXED( 1 ) )
no_effects = false;
}
if ( !no_echo )
no_effects = false;
if ( chans [0].vol [0] != TO_FIXED( 1 ) ||
chans [0].vol [1] != TO_FIXED( 0 ) ||
chans [1].vol [0] != TO_FIXED( 0 ) ||
chans [1].vol [1] != TO_FIXED( 1 ) )
no_effects = false;
if ( !config_.enabled )
no_effects = true;
if ( no_effects )
{
for ( i = chans.size(); --i >= 0; )
{
chan_t& ch = chans [i];
ch.channel.center = &bufs [2];
ch.channel.left = &bufs [0];
ch.channel.right = &bufs [1];
}
}
mixer.bufs [0] = &bufs [0];
mixer.bufs [1] = &bufs [1];
mixer.bufs [2] = &bufs [2];
if ( echo_dirty || (!old_echo && (!no_echo && !no_effects)) )
clear_echo();
channels_changed();
}
void Effects_Buffer::assign_buffers()
{
// assign channels to buffers
int buf_count = 0;
for ( int i = 0; i < (int) chans.size(); i++ )
{
// put second two side channels at end to give priority to main channels
// in case closest matching is necessary
int x = i;
if ( i > 1 )
x += 2;
if ( x >= (int) chans.size() )
x -= (chans.size() - 2);
chan_t& ch = chans [x];
int b = 0;
for ( ; b < buf_count; b++ )
{
if ( ch.vol [0] == bufs [b].vol [0] &&
ch.vol [1] == bufs [b].vol [1] &&
(ch.cfg.echo == bufs [b].echo || !s.feedback) )
break;
}
if ( b >= buf_count )
{
if ( buf_count < bufs_max )
{
bufs [b].vol [0] = ch.vol [0];
bufs [b].vol [1] = ch.vol [1];
bufs [b].echo = ch.cfg.echo;
buf_count++;
}
else
{
// TODO: this is a mess, needs refinement
dprintf( "Effects_Buffer ran out of buffers; using closest match\n" );
b = 0;
fixed_t best_dist = TO_FIXED( 8 );
for ( int h = buf_count; --h >= 0; )
{
#define CALC_LEVELS( vols, sum, diff, surround ) \
fixed_t sum, diff;\
bool surround = false;\
{\
fixed_t vol_0 = vols [0];\
if ( vol_0 < 0 ) vol_0 = -vol_0, surround = true;\
fixed_t vol_1 = vols [1];\
if ( vol_1 < 0 ) vol_1 = -vol_1, surround = true;\
sum = vol_0 + vol_1;\
diff = vol_0 - vol_1;\
}
CALC_LEVELS( ch.vol, ch_sum, ch_diff, ch_surround );
CALC_LEVELS( bufs [h].vol, buf_sum, buf_diff, buf_surround );
fixed_t dist = abs( ch_sum - buf_sum ) + abs( ch_diff - buf_diff );
if ( ch_surround != buf_surround )
dist += TO_FIXED( 1 ) / 2;
if ( s.feedback && ch.cfg.echo != bufs [h].echo )
dist += TO_FIXED( 1 ) / 2;
if ( best_dist > dist )
{
best_dist = dist;
b = h;
}
}
}
}
//dprintf( "ch %d->buf %d\n", x, b );
ch.channel.center = &bufs [b];
}
}
// Mixing
void Effects_Buffer::end_frame( blip_time_t time )
{
for ( int i = bufs_size; --i >= 0; )
bufs [i].end_frame( time );
}
long Effects_Buffer::read_samples( blip_sample_t* out, long out_size )
{
out_size = min( out_size, samples_avail() );
int pair_count = int (out_size >> 1);
require( pair_count * stereo == out_size ); // must read an even number of samples
if ( pair_count )
{
if ( no_effects )
{
mixer.read_pairs( out, pair_count );
}
else
{
int pairs_remain = pair_count;
do
{
// mix at most max_read pairs at a time
int count = max_read;
if ( count > pairs_remain )
count = pairs_remain;
if ( no_echo )
{
// optimization: clear echo here to keep mix_effects() a leaf function
echo_pos = 0;
memset( echo.begin(), 0, count * stereo * sizeof echo [0] );
}
mix_effects( out, count );
blargg_long new_echo_pos = echo_pos + count * stereo;
if ( new_echo_pos >= echo_size )
new_echo_pos -= echo_size;
echo_pos = new_echo_pos;
assert( echo_pos < echo_size );
out += count * stereo;
mixer.samples_read += count;
pairs_remain -= count;
}
while ( pairs_remain );
}
if ( samples_avail() <= 0 || immediate_removal() )
{
for ( int i = bufs_size; --i >= 0; )
{
buf_t& b = bufs [i];
// TODO: might miss non-silence settling since it checks END of last read
if ( b.non_silent() )
b.remove_samples( mixer.samples_read );
else
b.remove_silence( mixer.samples_read );
}
mixer.samples_read = 0;
}
}
return out_size;
}
void Effects_Buffer::mix_effects( blip_sample_t* out_, int pair_count )
{
typedef fixed_t stereo_fixed_t [stereo];
// add channels with echo, do echo, add channels without echo, then convert to 16-bit and output
int echo_phase = 1;
do
{
// mix any modified buffers
{
buf_t* buf = bufs;
int bufs_remain = bufs_size;
do
{
if ( buf->non_silent() && ( buf->echo == (bool)echo_phase ) )
{
stereo_fixed_t* BLIP_RESTRICT out = (stereo_fixed_t*) &echo [echo_pos];
int const bass = BLIP_READER_BASS( *buf );
BLIP_READER_BEGIN( in, *buf );
BLIP_READER_ADJ_( in, mixer.samples_read );
fixed_t const vol_0 = buf->vol [0];
fixed_t const vol_1 = buf->vol [1];
int count = unsigned (echo_size - echo_pos) / stereo;
int remain = pair_count;
if ( count > remain )
count = remain;
do
{
remain -= count;
BLIP_READER_ADJ_( in, count );
out += count;
int offset = -count;
do
{
fixed_t s = BLIP_READER_READ( in );
BLIP_READER_NEXT_IDX_( in, bass, offset );
out [offset] [0] += s * vol_0;
out [offset] [1] += s * vol_1;
}
while ( ++offset );
out = (stereo_fixed_t*) echo.begin();
count = remain;
}
while ( remain );
BLIP_READER_END( in, *buf );
}
buf++;
}
while ( --bufs_remain );
}
// add echo
if ( echo_phase && !no_echo )
{
fixed_t const feedback = s.feedback;
fixed_t const treble = s.treble;
int i = 1;
do
{
fixed_t low_pass = s.low_pass [i];
fixed_t* echo_end = &echo [echo_size + i];
fixed_t const* BLIP_RESTRICT in_pos = &echo [echo_pos + i];
blargg_long out_offset = echo_pos + i + s.delay [i];
if ( out_offset >= echo_size )
out_offset -= echo_size;
assert( out_offset < echo_size );
fixed_t* BLIP_RESTRICT out_pos = &echo [out_offset];
// break into up to three chunks to avoid having to handle wrap-around
// in middle of core loop
int remain = pair_count;
do
{
fixed_t const* pos = in_pos;
if ( pos < out_pos )
pos = out_pos;
int count = blargg_ulong ((char*) echo_end - (char const*) pos) /
unsigned (stereo * sizeof (fixed_t));
if ( count > remain )
count = remain;
remain -= count;
in_pos += count * stereo;
out_pos += count * stereo;
int offset = -count;
do
{
low_pass += FROM_FIXED( in_pos [offset * stereo] - low_pass ) * treble;
out_pos [offset * stereo] = FROM_FIXED( low_pass ) * feedback;
}
while ( ++offset );
if ( in_pos >= echo_end ) in_pos -= echo_size;
if ( out_pos >= echo_end ) out_pos -= echo_size;
}
while ( remain );
s.low_pass [i] = low_pass;
}
while ( --i >= 0 );
}
}
while ( --echo_phase >= 0 );
// clamp to 16 bits
{
stereo_fixed_t const* BLIP_RESTRICT in = (stereo_fixed_t*) &echo [echo_pos];
typedef blip_sample_t stereo_blip_sample_t [stereo];
stereo_blip_sample_t* BLIP_RESTRICT out = (stereo_blip_sample_t*) out_;
int count = unsigned (echo_size - echo_pos) / (unsigned) stereo;
int remain = pair_count;
if ( count > remain )
count = remain;
do
{
remain -= count;
in += count;
out += count;
int offset = -count;
do
{
fixed_t in_0 = FROM_FIXED( in [offset] [0] );
fixed_t in_1 = FROM_FIXED( in [offset] [1] );
BLIP_CLAMP( in_0, in_0 );
out [offset] [0] = (blip_sample_t) in_0;
BLIP_CLAMP( in_1, in_1 );
out [offset] [1] = (blip_sample_t) in_1;
}
while ( ++offset );
in = (stereo_fixed_t*) echo.begin();
count = remain;
}
while ( remain );
}
}

View File

@ -0,0 +1,143 @@
// Multi-channel effects buffer with echo and individual panning for each channel
// Game_Music_Emu $vers
#ifndef EFFECTS_BUFFER_H
#define EFFECTS_BUFFER_H
#include "Multi_Buffer.h"
// See Simple_Effects_Buffer (below) for a simpler interface
class Effects_Buffer : public Multi_Buffer {
public:
// To reduce memory usage, fewer buffers can be used (with a best-fit
// approach if there are too few), and maximum echo delay can be reduced
Effects_Buffer( int max_bufs = 32, long echo_size = 24 * 1024L );
struct pan_vol_t
{
float vol; // 0.0 = silent, 0.5 = half volume, 1.0 = normal
float pan; // -1.0 = left, 0.0 = center, +1.0 = right
};
// Global configuration
struct config_t
{
bool enabled; // false = disable all effects
// Current sound is echoed at adjustable left/right delay,
// with reduced treble and volume (feedback).
float treble; // 1.0 = full treble, 0.1 = very little, 0.0 = silent
int delay [2]; // left, right delays (msec)
float feedback; // 0.0 = no echo, 0.5 = each echo half previous, 1.0 = cacophony
pan_vol_t side_chans [2]; // left and right side channel volume and pan
};
config_t& config() { return config_; }
// Limits of delay (msec)
int min_delay() const;
int max_delay() const;
// Per-channel configuration. Two or more channels with matching parameters are
// optimized to internally use the same buffer.
struct chan_config_t : pan_vol_t
{
// (inherited from pan_vol_t)
//float vol; // these only affect center channel
//float pan;
bool surround; // if true, negates left volume to put sound in back
bool echo; // false = channel doesn't have any echo
};
chan_config_t& chan_config( int i ) { return chans [i + extra_chans].cfg; }
// Apply any changes made to config() and chan_config()
virtual void apply_config();
public:
~Effects_Buffer();
blargg_err_t set_sample_rate( long samples_per_sec, int msec = blip_default_length );
blargg_err_t set_channel_count( int, int const* = 0 );
void clock_rate( long );
void bass_freq( int );
void clear();
channel_t channel( int );
void end_frame( blip_time_t );
long read_samples( blip_sample_t*, long );
long samples_avail() const { return (bufs [0].samples_avail() - mixer.samples_read) * 2; }
enum { stereo = 2 };
typedef blargg_long fixed_t;
protected:
enum { extra_chans = stereo * stereo };
private:
config_t config_;
long clock_rate_;
int bass_freq_;
blargg_long echo_size;
struct chan_t
{
fixed_t vol [stereo];
chan_config_t cfg;
channel_t channel;
};
blargg_vector<chan_t> chans;
struct buf_t : Tracked_Blip_Buffer
{
fixed_t vol [stereo];
bool echo;
void* operator new ( size_t, void* p ) { return p; }
void operator delete ( void* ) { }
~buf_t() { }
};
buf_t* bufs;
int bufs_size;
int bufs_max; // bufs_size <= bufs_max, to limit memory usage
Stereo_Mixer mixer;
struct {
long delay [stereo];
fixed_t treble;
fixed_t feedback;
fixed_t low_pass [stereo];
} s;
blargg_vector<fixed_t> echo;
blargg_long echo_pos;
bool no_effects;
bool no_echo;
void assign_buffers();
void clear_echo();
void mix_effects( blip_sample_t* out, int pair_count );
blargg_err_t new_bufs( int size );
void delete_bufs();
};
// Simpler interface and lower memory usage
class Simple_Effects_Buffer : public Effects_Buffer {
public:
struct config_t
{
bool enabled; // false = disable all effects
float echo; // 0.0 = none, 1.0 = lots
float stereo; // 0.0 = channels in center, 1.0 = channels on left/right
bool surround; // true = put some channels in back
};
config_t& config() { return config_; }
// Apply any changes made to config()
void apply_config();
public:
Simple_Effects_Buffer();
private:
config_t config_;
void chan_config(); // hide
};
#endif

View File

@ -0,0 +1,394 @@
// Gb_Snd_Emu 0.2.0. http://www.slack.net/~ant/
#include "Gb_Apu.h"
/* Copyright (C) 2003-2007 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module 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 Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
unsigned const vol_reg = 0xFF24;
unsigned const stereo_reg = 0xFF25;
unsigned const status_reg = 0xFF26;
unsigned const wave_ram = 0xFF30;
int const power_mask = 0x80;
void Gb_Apu::treble_eq( blip_eq_t const& eq )
{
good_synth.treble_eq( eq );
med_synth .treble_eq( eq );
}
inline int Gb_Apu::calc_output( int osc ) const
{
int bits = regs [stereo_reg - start_addr] >> osc;
return (bits >> 3 & 2) | (bits & 1);
}
void Gb_Apu::set_output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right, int osc )
{
// Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL)
require( !center || (center && !left && !right) || (center && left && right) );
require( (unsigned) osc <= osc_count ); // fails if you pass invalid osc index
if ( !center || !left || !right )
{
left = center;
right = center;
}
int i = (unsigned) osc % osc_count;
do
{
Gb_Osc& o = *oscs [i];
o.outputs [1] = right;
o.outputs [2] = left;
o.outputs [3] = center;
o.output = o.outputs [calc_output( i )];
}
while ( ++i < osc );
}
void Gb_Apu::synth_volume( int iv )
{
double v = volume_ * 0.60 / osc_count / 15 /*steps*/ / 8 /*master vol range*/ * iv;
good_synth.volume( v );
med_synth .volume( v );
}
void Gb_Apu::apply_volume()
{
// TODO: Doesn't handle differing left and right volumes (panning).
// Not worth the complexity.
int data = regs [vol_reg - start_addr];
int left = data >> 4 & 7;
int right = data & 7;
//if ( data & 0x88 ) dprintf( "Vin: %02X\n", data & 0x88 );
//if ( left != right ) dprintf( "l: %d r: %d\n", left, right );
synth_volume( max( left, right ) + 1 );
}
void Gb_Apu::volume( double v )
{
if ( volume_ != v )
{
volume_ = v;
apply_volume();
}
}
void Gb_Apu::reset_regs()
{
for ( int i = 0; i < 0x20; i++ )
regs [i] = 0;
square1.reset();
square2.reset();
wave .reset();
noise .reset();
apply_volume();
}
void Gb_Apu::reset_lengths()
{
square1.length_ctr = 64;
square2.length_ctr = 64;
wave .length_ctr = 256;
noise .length_ctr = 64;
}
void Gb_Apu::reduce_clicks( bool reduce )
{
reduce_clicks_ = reduce;
// Click reduction makes DAC off generate same output as volume 0
int dac_off_amp = 0;
if ( reduce && wave.mode != mode_agb ) // AGB already eliminates clicks
dac_off_amp = -Gb_Osc::dac_bias;
for ( int i = 0; i < osc_count; i++ )
oscs [i]->dac_off_amp = dac_off_amp;
// AGB always eliminates clicks on wave channel using same method
if ( wave.mode == mode_agb )
wave.dac_off_amp = -Gb_Osc::dac_bias;
}
void Gb_Apu::reset( mode_t mode, bool agb_wave )
{
// Hardware mode
if ( agb_wave )
mode = mode_agb; // using AGB wave features implies AGB hardware
wave.agb_mask = agb_wave ? 0xFF : 0;
for ( int i = 0; i < osc_count; i++ )
oscs [i]->mode = mode;
reduce_clicks( reduce_clicks_ );
// Reset state
frame_time = 0;
last_time = 0;
frame_phase = 0;
reset_regs();
reset_lengths();
// Load initial wave RAM
static byte const initial_wave [2] [16] = {
{0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C,0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA},
{0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF},
};
for ( int b = 2; --b >= 0; )
{
// Init both banks (does nothing if not in AGB mode)
// TODO: verify that this works
write_register( 0, 0xFF1A, b * 0x40 );
for ( unsigned i = 0; i < sizeof initial_wave [0]; i++ )
write_register( 0, i + wave_ram, initial_wave [(mode != mode_dmg)] [i] );
}
}
void Gb_Apu::set_tempo( double t )
{
frame_period = 4194304 / 512; // 512 Hz
if ( t != 1.0 )
frame_period = blip_time_t (frame_period / t);
}
Gb_Apu::Gb_Apu()
{
wave.wave_ram = &regs [wave_ram - start_addr];
oscs [0] = &square1;
oscs [1] = &square2;
oscs [2] = &wave;
oscs [3] = &noise;
for ( int i = osc_count; --i >= 0; )
{
Gb_Osc& o = *oscs [i];
o.regs = &regs [i * 5];
o.output = 0;
o.outputs [0] = 0;
o.outputs [1] = 0;
o.outputs [2] = 0;
o.outputs [3] = 0;
o.good_synth = &good_synth;
o.med_synth = &med_synth;
}
reduce_clicks_ = false;
set_tempo( 1.0 );
volume_ = 1.0;
reset();
}
void Gb_Apu::run_until_( blip_time_t end_time )
{
while ( true )
{
// run oscillators
blip_time_t time = end_time;
if ( time > frame_time )
time = frame_time;
square1.run( last_time, time );
square2.run( last_time, time );
wave .run( last_time, time );
noise .run( last_time, time );
last_time = time;
if ( time == end_time )
break;
// run frame sequencer
frame_time += frame_period * Gb_Osc::clk_mul;
switch ( frame_phase++ )
{
case 2:
case 6:
// 128 Hz
square1.clock_sweep();
case 0:
case 4:
// 256 Hz
square1.clock_length();
square2.clock_length();
wave .clock_length();
noise .clock_length();
break;
case 7:
// 64 Hz
frame_phase = 0;
square1.clock_envelope();
square2.clock_envelope();
noise .clock_envelope();
}
}
}
inline void Gb_Apu::run_until( blip_time_t time )
{
require( time >= last_time ); // end_time must not be before previous time
if ( time > last_time )
run_until_( time );
}
void Gb_Apu::end_frame( blip_time_t end_time )
{
if ( end_time > last_time )
run_until( end_time );
frame_time -= end_time;
assert( frame_time >= 0 );
last_time -= end_time;
assert( last_time >= 0 );
}
void Gb_Apu::silence_osc( Gb_Osc& o )
{
int delta = -o.last_amp;
if ( delta )
{
o.last_amp = 0;
if ( o.output )
{
o.output->set_modified();
med_synth.offset( last_time, delta, o.output );
}
}
}
void Gb_Apu::apply_stereo()
{
for ( int i = osc_count; --i >= 0; )
{
Gb_Osc& o = *oscs [i];
Blip_Buffer* out = o.outputs [calc_output( i )];
if ( o.output != out )
{
silence_osc( o );
o.output = out;
}
}
}
void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data )
{
require( (unsigned) data < 0x100 );
int reg = addr - start_addr;
if ( (unsigned) reg >= register_count )
{
require( false );
return;
}
if ( addr < status_reg && !(regs [status_reg - start_addr] & power_mask) )
{
// Power is off
// length counters can only be written in DMG mode
if ( wave.mode != mode_dmg || (reg != 1 && reg != 5+1 && reg != 10+1 && reg != 15+1) )
return;
if ( reg < 10 )
data &= 0x3F; // clear square duty
}
run_until( time );
if ( addr >= wave_ram )
{
wave.write( addr, data );
}
else
{
int old_data = regs [reg];
regs [reg] = data;
if ( addr < vol_reg )
{
// Oscillator
write_osc( reg / 5, reg, old_data, data );
}
else if ( addr == vol_reg && data != old_data )
{
// Master volume
for ( int i = osc_count; --i >= 0; )
silence_osc( *oscs [i] );
apply_volume();
}
else if ( addr == stereo_reg )
{
// Stereo panning
apply_stereo();
}
else if ( addr == status_reg && (data ^ old_data) & power_mask )
{
// Power control
frame_phase = 0;
for ( int i = osc_count; --i >= 0; )
silence_osc( *oscs [i] );
reset_regs();
if ( wave.mode != mode_dmg )
reset_lengths();
regs [status_reg - start_addr] = data;
}
}
}
int Gb_Apu::read_register( blip_time_t time, unsigned addr )
{
run_until( time );
int reg = addr - start_addr;
if ( (unsigned) reg >= register_count )
{
require( false );
return 0;
}
if ( addr >= wave_ram )
return wave.read( addr );
// Value read back has some bits always set
static byte const masks [] = {
0x80,0x3F,0x00,0xFF,0xBF,
0xFF,0x3F,0x00,0xFF,0xBF,
0x7F,0xFF,0x9F,0xFF,0xBF,
0xFF,0xFF,0x00,0x00,0xBF,
0x00,0x00,0x70,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
};
int mask = masks [reg];
if ( wave.agb_mask && (reg == 10 || reg == 12) )
mask = 0x1F; // extra implemented bits in wave regs on AGB
int data = regs [reg] | mask;
// Status register
if ( addr == status_reg )
{
data &= 0xF0;
data |= (int) square1.enabled << 0;
data |= (int) square2.enabled << 1;
data |= (int) wave .enabled << 2;
data |= (int) noise .enabled << 3;
}
return data;
}

View File

@ -0,0 +1,182 @@
// Nintendo Game Boy sound hardware emulator with save state support
// Gb_Snd_Emu 0.2.0
#ifndef GB_APU_H
#define GB_APU_H
#include "Gb_Oscs.h"
struct gb_apu_state_t;
class Gb_Apu {
public:
// Basics
// Clock rate that sound hardware runs at.
enum { clock_rate = 4194304 * GB_APU_OVERCLOCK };
// Sets buffer(s) to generate sound into. If left and right are NULL, output is mono.
// If all are NULL, no output is generated but other emulation still runs.
// If chan is specified, only that channel's output is changed, otherwise all are.
enum { osc_count = 4 }; // 0: Square 1, 1: Square 2, 2: Wave, 3: Noise
void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL,
int chan = osc_count );
// Resets hardware to initial power on state BEFORE boot ROM runs. Mode selects
// sound hardware. Additional AGB wave features are enabled separately.
enum mode_t {
mode_dmg, // Game Boy monochrome
mode_cgb, // Game Boy Color
mode_agb // Game Boy Advance
};
void reset( mode_t mode = mode_cgb, bool agb_wave = false );
// Reads and writes must be within the start_addr to end_addr range, inclusive.
// Addresses outside this range are not mapped to the sound hardware.
enum { start_addr = 0xFF10 };
enum { end_addr = 0xFF3F };
enum { register_count = end_addr - start_addr + 1 };
// Times are specified as the number of clocks since the beginning of the
// current time frame.
// Emulates CPU write of data to addr at specified time.
void write_register( blip_time_t time, unsigned addr, int data );
// Emulates CPU read from addr at specified time.
int read_register( blip_time_t time, unsigned addr );
// Emulates sound hardware up to specified time, ends current time frame, then
// starts a new frame at time 0.
void end_frame( blip_time_t frame_length );
// Sound adjustments
// Sets overall volume, where 1.0 is normal.
void volume( double );
// If true, reduces clicking by disabling DAC biasing. Note that this reduces
// emulation accuracy, since the clicks are authentic.
void reduce_clicks( bool reduce = true );
// Sets treble equalization.
void treble_eq( blip_eq_t const& );
// Treble and bass values for various hardware.
enum {
speaker_treble = -47, // speaker on system
speaker_bass = 2000,
dmg_treble = 0, // headphones on each system
dmg_bass = 30,
cgb_treble = 0,
cgb_bass = 300, // CGB has much less bass
agb_treble = 0,
agb_bass = 30
};
// Sets frame sequencer rate, where 1.0 is normal. Meant for adjusting the
// tempo in a game music player.
void set_tempo( double );
// Save states
// Saves full emulation state to state_out. Data format is portable and
// includes some extra space to avoid expansion in case more state needs
// to be stored in the future.
void save_state( gb_apu_state_t* state_out );
// Loads state. You should call reset() BEFORE this.
blargg_err_t load_state( gb_apu_state_t const& in );
public:
Gb_Apu();
// Use set_output() in place of these
BLARGG_DEPRECATED void output ( Blip_Buffer* c ) { set_output( c, c, c ); }
BLARGG_DEPRECATED void output ( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( c, l, r ); }
BLARGG_DEPRECATED void osc_output( int i, Blip_Buffer* c ) { set_output( c, c, c, i ); }
BLARGG_DEPRECATED void osc_output( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( c, l, r, i ); }
private:
// noncopyable
Gb_Apu( const Gb_Apu& );
Gb_Apu& operator = ( const Gb_Apu& );
Gb_Osc* oscs [osc_count];
blip_time_t last_time; // time sound emulator has been run to
blip_time_t frame_period; // clocks between each frame sequencer step
double volume_;
bool reduce_clicks_;
Gb_Sweep_Square square1;
Gb_Square square2;
Gb_Wave wave;
Gb_Noise noise;
blip_time_t frame_time; // time of next frame sequencer action
int frame_phase; // phase of next frame sequencer step
enum { regs_size = register_count + 0x10 };
BOOST::uint8_t regs [regs_size];// last values written to registers
// large objects after everything else
Gb_Osc::Good_Synth good_synth;
Gb_Osc::Med_Synth med_synth;
void reset_lengths();
void reset_regs();
int calc_output( int osc ) const;
void apply_stereo();
void apply_volume();
void synth_volume( int );
void run_until_( blip_time_t );
void run_until( blip_time_t );
void silence_osc( Gb_Osc& );
void write_osc( int index, int reg, int old_data, int data );
const char* save_load( gb_apu_state_t*, bool save );
void save_load2( gb_apu_state_t*, bool save );
friend class Gb_Apu_Tester;
};
// Format of save state. Should be stable across versions of the library,
// with earlier versions properly opening later save states. Includes some
// room for expansion so the state size shouldn't increase.
struct gb_apu_state_t
{
#if GB_APU_CUSTOM_STATE
// Values stored as plain int so your code can read/write them easily.
// Structure can NOT be written to disk, since format is not portable.
typedef int val_t;
#else
// Values written in portable little-endian format, allowing structure
// to be written directly to disk.
typedef unsigned char val_t [4];
#endif
enum { format0 = 0x50414247 };
val_t format; // format of all following data
val_t version; // later versions just add fields to end
unsigned char regs [0x40];
val_t frame_time;
val_t frame_phase;
val_t sweep_freq;
val_t sweep_delay;
val_t sweep_enabled;
val_t sweep_neg;
val_t noise_divider;
val_t wave_buf;
val_t delay [4];
val_t length_ctr [4];
val_t phase [4];
val_t enabled [4];
val_t env_delay [3];
val_t env_volume [3];
val_t env_enabled [3];
val_t unused [13]; // for future expansion
};
#endif

View File

@ -0,0 +1,118 @@
// Gb_Snd_Emu $vers. http://www.slack.net/~ant/
#include "Gb_Apu.h"
#include <string.h>
/* Copyright (C) 2007 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module 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 Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
#if GB_APU_CUSTOM_STATE
#define REFLECT( x, y ) (save ? (io->y) = (x) : (x) = (io->y) )
#else
#define REFLECT( x, y ) (save ? set_val( io->y, x ) : (void) ((x) = get_val( io->y )))
static blargg_ulong get_val( byte const* p )
{
return p [3] * 0x1000000 + p [2] * 0x10000 + p [1] * 0x100 + p [0];
}
static void set_val( byte* p, blargg_ulong n )
{
p [0] = (byte) (n );
p [1] = (byte) (n >> 8);
p [2] = (byte) (n >> 16);
p [3] = (byte) (n >> 24);
}
#endif
inline const char* Gb_Apu::save_load( gb_apu_state_t* io, bool save )
{
#if !GB_APU_CUSTOM_STATE
assert( sizeof (gb_apu_state_t) == 256 );
#endif
int format = io->format0;
REFLECT( format, format );
if ( format != io->format0 )
return "Unsupported sound save state format";
int version = 0;
REFLECT( version, version );
// Registers and wave RAM
assert( regs_size == sizeof io->regs );
if ( save )
memcpy( io->regs, regs, sizeof io->regs );
else
memcpy( regs, io->regs, sizeof regs );
// Frame sequencer
REFLECT( frame_time, frame_time );
REFLECT( frame_phase, frame_phase );
REFLECT( square1.sweep_freq, sweep_freq );
REFLECT( square1.sweep_delay, sweep_delay );
REFLECT( square1.sweep_enabled, sweep_enabled );
REFLECT( square1.sweep_neg, sweep_neg );
REFLECT( noise.divider, noise_divider );
REFLECT( wave.sample_buf, wave_buf );
return 0;
}
// second function to avoid inline limits of some compilers
inline void Gb_Apu::save_load2( gb_apu_state_t* io, bool save )
{
for ( int i = osc_count; --i >= 0; )
{
Gb_Osc& osc = *oscs [i];
REFLECT( osc.delay, delay [i] );
REFLECT( osc.length_ctr, length_ctr [i] );
REFLECT( osc.phase, phase [i] );
REFLECT( osc.enabled, enabled [i] );
if ( i != 2 )
{
int j = min( i, 2 );
Gb_Env& env = STATIC_CAST(Gb_Env&,osc);
REFLECT( env.env_delay, env_delay [j] );
REFLECT( env.volume, env_volume [j] );
REFLECT( env.env_enabled, env_enabled [j] );
}
}
}
void Gb_Apu::save_state( gb_apu_state_t* out )
{
(void) save_load( out, true );
save_load2( out, true );
#if !GB_APU_CUSTOM_STATE
memset( out->unused, 0, sizeof out->unused );
#endif
}
blargg_err_t Gb_Apu::load_state( gb_apu_state_t const& in )
{
RETURN_ERR( save_load( CONST_CAST(gb_apu_state_t*,&in), false ) );
save_load2( CONST_CAST(gb_apu_state_t*,&in), false );
apply_stereo();
synth_volume( 0 ); // suppress output for the moment
run_until_( last_time ); // get last_amp updated
apply_volume(); // now use correct volume
return 0;
}

View File

@ -0,0 +1,665 @@
// Gb_Snd_Emu 0.2.0. http://www.slack.net/~ant/
#include "Gb_Apu.h"
/* Copyright (C) 2003-2007 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module 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 Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
bool const cgb_02 = false; // enables bug in early CGB units that causes problems in some games
bool const cgb_05 = false; // enables CGB-05 zombie behavior
int const trigger_mask = 0x80;
int const length_enabled = 0x40;
void Gb_Osc::reset()
{
output = 0;
last_amp = 0;
delay = 0;
phase = 0;
enabled = false;
}
inline void Gb_Osc::update_amp( blip_time_t time, int new_amp )
{
output->set_modified();
int delta = new_amp - last_amp;
if ( delta )
{
last_amp = new_amp;
med_synth->offset( time, delta, output );
}
}
// Units
void Gb_Osc::clock_length()
{
if ( (regs [4] & length_enabled) && length_ctr )
{
if ( --length_ctr <= 0 )
enabled = false;
}
}
inline int Gb_Env::reload_env_timer()
{
int raw = regs [2] & 7;
env_delay = (raw ? raw : 8);
return raw;
}
void Gb_Env::clock_envelope()
{
if ( env_enabled && --env_delay <= 0 && reload_env_timer() )
{
int v = volume + (regs [2] & 0x08 ? +1 : -1);
if ( 0 <= v && v <= 15 )
volume = v;
else
env_enabled = false;
}
}
inline void Gb_Sweep_Square::reload_sweep_timer()
{
sweep_delay = (regs [0] & period_mask) >> 4;
if ( !sweep_delay )
sweep_delay = 8;
}
void Gb_Sweep_Square::calc_sweep( bool update )
{
int const shift = regs [0] & shift_mask;
int const delta = sweep_freq >> shift;
sweep_neg = (regs [0] & 0x08) != 0;
int const freq = sweep_freq + (sweep_neg ? -delta : delta);
if ( freq > 0x7FF )
{
enabled = false;
}
else if ( shift && update )
{
sweep_freq = freq;
regs [3] = freq & 0xFF;
regs [4] = (regs [4] & ~0x07) | (freq >> 8 & 0x07);
}
}
void Gb_Sweep_Square::clock_sweep()
{
if ( --sweep_delay <= 0 )
{
reload_sweep_timer();
if ( sweep_enabled && (regs [0] & period_mask) )
{
calc_sweep( true );
calc_sweep( false );
}
}
}
int Gb_Wave::access( unsigned addr ) const
{
if ( enabled )
{
addr = phase & (bank_size - 1);
if ( mode == Gb_Apu::mode_dmg )
{
addr++;
if ( delay > clk_mul )
return -1; // can only access within narrow time window while playing
}
addr >>= 1;
}
return addr & 0x0F;
}
// write_register
int Gb_Osc::write_trig( int frame_phase, int max_len, int old_data )
{
int data = regs [4];
if ( (frame_phase & 1) && !(old_data & length_enabled) && length_ctr )
{
if ( (data & length_enabled) || cgb_02 )
length_ctr--;
}
if ( data & trigger_mask )
{
enabled = true;
if ( !length_ctr )
{
length_ctr = max_len;
if ( (frame_phase & 1) && (data & length_enabled) )
length_ctr--;
}
}
if ( !length_ctr )
enabled = false;
return data & trigger_mask;
}
inline void Gb_Env::zombie_volume( int old, int data )
{
int v = volume;
if ( mode == Gb_Apu::mode_agb || cgb_05 )
{
// CGB-05 behavior, very close to AGB behavior as well
if ( (old ^ data) & 8 )
{
if ( !(old & 8) )
{
v++;
if ( old & 7 )
v++;
}
v = 16 - v;
}
else if ( (old & 0x0F) == 8 )
{
v++;
}
}
else
{
// CGB-04&02 behavior, very close to MGB behavior as well
if ( !(old & 7) && env_enabled )
v++;
else if ( !(old & 8) )
v += 2;
if ( (old ^ data) & 8 )
v = 16 - v;
}
volume = v & 0x0F;
}
bool Gb_Env::write_register( int frame_phase, int reg, int old, int data )
{
int const max_len = 64;
switch ( reg )
{
case 1:
length_ctr = max_len - (data & (max_len - 1));
break;
case 2:
if ( !dac_enabled() )
enabled = false;
zombie_volume( old, data );
if ( (data & 7) && env_delay == 8 )
{
env_delay = 1;
clock_envelope(); // TODO: really happens at next length clock
}
break;
case 4:
if ( write_trig( frame_phase, max_len, old ) )
{
volume = regs [2] >> 4;
reload_env_timer();
env_enabled = true;
if ( frame_phase == 7 )
env_delay++;
if ( !dac_enabled() )
enabled = false;
return true;
}
}
return false;
}
bool Gb_Square::write_register( int frame_phase, int reg, int old_data, int data )
{
bool result = Gb_Env::write_register( frame_phase, reg, old_data, data );
if ( result )
delay = (delay & (4 * clk_mul - 1)) + period();
return result;
}
inline void Gb_Noise::write_register( int frame_phase, int reg, int old_data, int data )
{
if ( Gb_Env::write_register( frame_phase, reg, old_data, data ) )
{
phase = 0x7FFF;
delay += 8 * clk_mul;
}
}
inline void Gb_Sweep_Square::write_register( int frame_phase, int reg, int old_data, int data )
{
if ( reg == 0 && sweep_enabled && sweep_neg && !(data & 0x08) )
enabled = false; // sweep negate disabled after used
if ( Gb_Square::write_register( frame_phase, reg, old_data, data ) )
{
sweep_freq = frequency();
sweep_neg = false;
reload_sweep_timer();
sweep_enabled = (regs [0] & (period_mask | shift_mask)) != 0;
if ( regs [0] & shift_mask )
calc_sweep( false );
}
}
void Gb_Wave::corrupt_wave()
{
int pos = ((phase + 1) & (bank_size - 1)) >> 1;
if ( pos < 4 )
wave_ram [0] = wave_ram [pos];
else
for ( int i = 4; --i >= 0; )
wave_ram [i] = wave_ram [(pos & ~3) + i];
}
inline void Gb_Wave::write_register( int frame_phase, int reg, int old_data, int data )
{
int const max_len = 256;
switch ( reg )
{
case 0:
if ( !dac_enabled() )
enabled = false;
break;
case 1:
length_ctr = max_len - data;
break;
case 4:
bool was_enabled = enabled;
if ( write_trig( frame_phase, max_len, old_data ) )
{
if ( !dac_enabled() )
enabled = false;
else if ( mode == Gb_Apu::mode_dmg && was_enabled &&
(unsigned) (delay - 2 * clk_mul) < 2 * clk_mul )
corrupt_wave();
phase = 0;
delay = period() + 6 * clk_mul;
}
}
}
void Gb_Apu::write_osc( int index, int reg, int old_data, int data )
{
reg -= index * 5;
switch ( index )
{
case 0: square1.write_register( frame_phase, reg, old_data, data ); break;
case 1: square2.write_register( frame_phase, reg, old_data, data ); break;
case 2: wave .write_register( frame_phase, reg, old_data, data ); break;
case 3: noise .write_register( frame_phase, reg, old_data, data ); break;
}
}
// Synthesis
void Gb_Square::run( blip_time_t time, blip_time_t end_time )
{
// Calc duty and phase
static byte const duty_offsets [4] = { 1, 1, 3, 7 };
static byte const duties [4] = { 1, 2, 4, 6 };
int const duty_code = regs [1] >> 6;
int duty_offset = duty_offsets [duty_code];
int duty = duties [duty_code];
if ( mode == Gb_Apu::mode_agb )
{
// AGB uses inverted duty
duty_offset -= duty;
duty = 8 - duty;
}
int ph = (this->phase + duty_offset) & 7;
// Determine what will be generated
int vol = 0;
Blip_Buffer* const out = this->output;
if ( out )
{
int amp = dac_off_amp;
if ( dac_enabled() )
{
if ( enabled )
vol = this->volume;
amp = -dac_bias;
if ( mode == Gb_Apu::mode_agb )
amp = -(vol >> 1);
// Play inaudible frequencies as constant amplitude
if ( frequency() >= 0x7FA && delay < 32 * clk_mul )
{
amp += (vol * duty) >> 3;
vol = 0;
}
if ( ph < duty )
{
amp += vol;
vol = -vol;
}
}
update_amp( time, amp );
}
// Generate wave
time += delay;
if ( time < end_time )
{
int const per = this->period();
if ( !vol )
{
// Maintain phase when not playing
int count = (end_time - time + per - 1) / per;
ph += count; // will be masked below
time += (blip_time_t) count * per;
}
else
{
// Output amplitude transitions
int delta = vol;
do
{
ph = (ph + 1) & 7;
if ( ph == 0 || ph == duty )
{
good_synth->offset_inline( time, delta, out );
delta = -delta;
}
time += per;
}
while ( time < end_time );
if ( delta != vol )
last_amp -= delta;
}
this->phase = (ph - duty_offset) & 7;
}
delay = time - end_time;
}
// Quickly runs LFSR for a large number of clocks. For use when noise is generating
// no sound.
static unsigned run_lfsr( unsigned s, unsigned mask, int count )
{
bool const optimized = true; // set to false to use only unoptimized loop in middle
// optimization used in several places:
// ((s & (1 << b)) << n) ^ ((s & (1 << b)) << (n + 1)) = (s & (1 << b)) * (3 << n)
if ( mask == 0x4000 && optimized )
{
if ( count >= 32767 )
count %= 32767;
// Convert from Fibonacci to Galois configuration,
// shifted left 1 bit
s ^= (s & 1) * 0x8000;
// Each iteration is equivalent to clocking LFSR 255 times
while ( (count -= 255) > 0 )
s ^= ((s & 0xE) << 12) ^ ((s & 0xE) << 11) ^ (s >> 3);
count += 255;
// Each iteration is equivalent to clocking LFSR 15 times
// (interesting similarity to single clocking below)
while ( (count -= 15) > 0 )
s ^= ((s & 2) * (3 << 13)) ^ (s >> 1);
count += 15;
// Remaining singles
while ( --count >= 0 )
s = ((s & 2) * (3 << 13)) ^ (s >> 1);
// Convert back to Fibonacci configuration
s &= 0x7FFF;
}
else if ( count < 8 || !optimized )
{
// won't fully replace upper 8 bits, so have to do the unoptimized way
while ( --count >= 0 )
s = (s >> 1 | mask) ^ (mask & -((s - 1) & 2));
}
else
{
if ( count > 127 )
{
count %= 127;
if ( !count )
count = 127; // must run at least once
}
// Need to keep one extra bit of history
s = s << 1 & 0xFF;
// Convert from Fibonacci to Galois configuration,
// shifted left 2 bits
s ^= (s & 2) * 0x80;
// Each iteration is equivalent to clocking LFSR 7 times
// (interesting similarity to single clocking below)
while ( (count -= 7) > 0 )
s ^= ((s & 4) * (3 << 5)) ^ (s >> 1);
count += 7;
// Remaining singles
while ( --count >= 0 )
s = ((s & 4) * (3 << 5)) ^ (s >> 1);
// Convert back to Fibonacci configuration and
// repeat last 8 bits above significant 7
s = (s << 7 & 0x7F80) | (s >> 1 & 0x7F);
}
return s;
}
void Gb_Noise::run( blip_time_t time, blip_time_t end_time )
{
// Determine what will be generated
int vol = 0;
Blip_Buffer* const out = this->output;
if ( out )
{
int amp = dac_off_amp;
if ( dac_enabled() )
{
if ( enabled )
vol = this->volume;
amp = -dac_bias;
if ( mode == Gb_Apu::mode_agb )
amp = -(vol >> 1);
if ( !(phase & 1) )
{
amp += vol;
vol = -vol;
}
}
// AGB negates final output
if ( mode == Gb_Apu::mode_agb )
{
vol = -vol;
amp = -amp;
}
update_amp( time, amp );
}
// Run timer and calculate time of next LFSR clock
static byte const period1s [8] = { 1, 2, 4, 6, 8, 10, 12, 14 };
int const period1 = period1s [regs [3] & 7] * clk_mul;
{
int extra = (end_time - time) - delay;
int const per2 = this->period2();
time += delay + ((divider ^ (per2 >> 1)) & (per2 - 1)) * period1;
int count = (extra < 0 ? 0 : (extra + period1 - 1) / period1);
divider = (divider - count) & period2_mask;
delay = count * period1 - extra;
}
// Generate wave
if ( time < end_time )
{
unsigned const mask = this->lfsr_mask();
unsigned bits = this->phase;
int per = period2( period1 * 8 );
if ( period2_index() >= 0xE )
{
time = end_time;
}
else if ( !vol )
{
// Maintain phase when not playing
int count = (end_time - time + per - 1) / per;
time += (blip_time_t) count * per;
bits = run_lfsr( bits, ~mask, count );
}
else
{
// Output amplitude transitions
int delta = -vol;
do
{
unsigned changed = bits + 1;
bits = bits >> 1 & mask;
if ( changed & 2 )
{
bits |= ~mask;
delta = -delta;
med_synth->offset_inline( time, delta, out );
}
time += per;
}
while ( time < end_time );
if ( delta == vol )
last_amp += delta;
}
this->phase = bits;
}
}
void Gb_Wave::run( blip_time_t time, blip_time_t end_time )
{
// Calc volume
static byte const volumes [8] = { 0, 4, 2, 1, 3, 3, 3, 3 };
int const volume_shift = 2;
int const volume_idx = regs [2] >> 5 & (agb_mask | 3); // 2 bits on DMG/CGB, 3 on AGB
int const volume_mul = volumes [volume_idx];
// Determine what will be generated
int playing = false;
Blip_Buffer* const out = this->output;
if ( out )
{
int amp = dac_off_amp;
if ( dac_enabled() )
{
// Play inaudible frequencies as constant amplitude
amp = 8 << 4; // really depends on average of all samples in wave
// if delay is larger, constant amplitude won't start yet
if ( frequency() <= 0x7FB || delay > 15 * clk_mul )
{
if ( volume_mul )
playing = (int) enabled;
amp = (sample_buf << (phase << 2 & 4) & 0xF0) * playing;
}
amp = ((amp * volume_mul) >> (volume_shift + 4)) - dac_bias;
}
update_amp( time, amp );
}
// Generate wave
time += delay;
if ( time < end_time )
{
byte const* wave = this->wave_ram;
// wave size and bank
int const size20_mask = 0x20;
int const flags = regs [0] & agb_mask;
int const wave_mask = (flags & size20_mask) | 0x1F;
int swap_banks = 0;
if ( flags & bank40_mask )
{
swap_banks = flags & size20_mask;
wave += bank_size/2 - (swap_banks >> 1);
}
int ph = this->phase ^ swap_banks;
ph = (ph + 1) & wave_mask; // pre-advance
int const per = this->period();
if ( !playing )
{
// Maintain phase when not playing
int count = (end_time - time + per - 1) / per;
ph += count; // will be masked below
time += (blip_time_t) count * per;
}
else
{
// Output amplitude transitions
int lamp = this->last_amp + dac_bias;
do
{
// Extract nybble
int nybble = wave [ph >> 1] << (ph << 2 & 4) & 0xF0;
ph = (ph + 1) & wave_mask;
// Scale by volume
int amp = (nybble * volume_mul) >> (volume_shift + 4);
int delta = amp - lamp;
if ( delta )
{
lamp = amp;
med_synth->offset_inline( time, delta, out );
}
time += per;
}
while ( time < end_time );
this->last_amp = lamp - dac_bias;
}
ph = (ph - 1) & wave_mask; // undo pre-advance and mask position
// Keep track of last byte read
if ( enabled )
sample_buf = wave [ph >> 1];
this->phase = ph ^ swap_banks; // undo swapped banks
}
delay = time - end_time;
}

View File

@ -0,0 +1,190 @@
// Private oscillators used by Gb_Apu
// Gb_Snd_Emu 0.2.0
#ifndef GB_OSCS_H
#define GB_OSCS_H
#include "blargg_common.h"
#include "Blip_Buffer.h"
#ifndef GB_APU_OVERCLOCK
#define GB_APU_OVERCLOCK 1
#endif
#if GB_APU_OVERCLOCK & (GB_APU_OVERCLOCK - 1)
#error "GB_APU_OVERCLOCK must be a power of 2"
#endif
class Gb_Osc {
protected:
// 11-bit frequency in NRx3 and NRx4
int frequency() const { return (regs [4] & 7) * 0x100 + regs [3]; }
void update_amp( blip_time_t, int new_amp );
int write_trig( int frame_phase, int max_len, int old_data );
public:
enum { clk_mul = GB_APU_OVERCLOCK };
enum { dac_bias = 7 };
Blip_Buffer* outputs [4];// NULL, right, left, center
Blip_Buffer* output; // where to output sound
BOOST::uint8_t* regs; // osc's 5 registers
int mode; // mode_dmg, mode_cgb, mode_agb
int dac_off_amp;// amplitude when DAC is off
int last_amp; // current amplitude in Blip_Buffer
typedef Blip_Synth<blip_good_quality,1> Good_Synth;
typedef Blip_Synth<blip_med_quality ,1> Med_Synth;
Good_Synth const* good_synth;
Med_Synth const* med_synth;
int delay; // clocks until frequency timer expires
int length_ctr; // length counter
unsigned phase; // waveform phase (or equivalent)
bool enabled; // internal enabled flag
void clock_length();
void reset();
};
class Gb_Env : public Gb_Osc {
public:
int env_delay;
int volume;
bool env_enabled;
void clock_envelope();
bool write_register( int frame_phase, int reg, int old_data, int data );
void reset()
{
env_delay = 0;
volume = 0;
Gb_Osc::reset();
}
protected:
// Non-zero if DAC is enabled
int dac_enabled() const { return regs [2] & 0xF8; }
private:
void zombie_volume( int old, int data );
int reload_env_timer();
};
class Gb_Square : public Gb_Env {
public:
bool write_register( int frame_phase, int reg, int old_data, int data );
void run( blip_time_t, blip_time_t );
void reset()
{
Gb_Env::reset();
delay = 0x40000000; // TODO: something less hacky (never clocked until first trigger)
}
private:
// Frequency timer period
int period() const { return (2048 - frequency()) * (4 * clk_mul); }
};
class Gb_Sweep_Square : public Gb_Square {
public:
int sweep_freq;
int sweep_delay;
bool sweep_enabled;
bool sweep_neg;
void clock_sweep();
void write_register( int frame_phase, int reg, int old_data, int data );
void reset()
{
sweep_freq = 0;
sweep_delay = 0;
sweep_enabled = false;
sweep_neg = false;
Gb_Square::reset();
}
private:
enum { period_mask = 0x70 };
enum { shift_mask = 0x07 };
void calc_sweep( bool update );
void reload_sweep_timer();
};
class Gb_Noise : public Gb_Env {
public:
int divider; // noise has more complex frequency divider setup
void run( blip_time_t, blip_time_t );
void write_register( int frame_phase, int reg, int old_data, int data );
void reset()
{
divider = 0;
Gb_Env::reset();
delay = 4 * clk_mul; // TODO: remove?
}
private:
enum { period2_mask = 0x1FFFF };
int period2_index() const { return regs [3] >> 4; }
int period2( int base = 8 ) const { return base << period2_index(); }
unsigned lfsr_mask() const { return (regs [3] & 0x08) ? ~0x4040 : ~0x4000; }
};
class Gb_Wave : public Gb_Osc {
public:
int sample_buf; // last wave RAM byte read (hardware has this as well)
void write_register( int frame_phase, int reg, int old_data, int data );
void run( blip_time_t, blip_time_t );
// Reads/writes wave RAM
int read( unsigned addr ) const;
void write( unsigned addr, int data );
void reset()
{
sample_buf = 0;
Gb_Osc::reset();
}
private:
enum { bank40_mask = 0x40 };
enum { bank_size = 32 };
int agb_mask; // 0xFF if AGB features enabled, 0 otherwise
BOOST::uint8_t* wave_ram; // 32 bytes (64 nybbles), stored in APU
friend class Gb_Apu;
// Frequency timer period
int period() const { return (2048 - frequency()) * (2 * clk_mul); }
// Non-zero if DAC is enabled
int dac_enabled() const { return regs [0] & 0x80; }
void corrupt_wave();
BOOST::uint8_t* wave_bank() const { return &wave_ram [(~regs [0] & bank40_mask) >> 2 & agb_mask]; }
// Wave index that would be accessed, or -1 if no access would occur
int access( unsigned addr ) const;
};
inline int Gb_Wave::read( unsigned addr ) const
{
int index = access( addr );
return (index < 0 ? 0xFF : wave_bank() [index]);
}
inline void Gb_Wave::write( unsigned addr, int data )
{
int index = access( addr );
if ( index >= 0 )
wave_bank() [index] = data;;
}
#endif

View File

@ -0,0 +1,281 @@
// Blip_Buffer 0.4.1. http://www.slack.net/~ant/
#include "Multi_Buffer.h"
/* Copyright (C) 2003-2007 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module 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 Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
#ifdef BLARGG_ENABLE_OPTIMIZER
#include BLARGG_ENABLE_OPTIMIZER
#endif
Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf )
{
length_ = 0;
sample_rate_ = 0;
channels_changed_count_ = 1;
channel_types_ = 0;
channel_count_ = 0;
immediate_removal_ = true;
}
Multi_Buffer::channel_t Multi_Buffer::channel( int /*index*/ )
{
static channel_t const ch = { 0, 0, 0 };
return ch;
}
// Silent_Buffer
Silent_Buffer::Silent_Buffer() : Multi_Buffer( 1 ) // 0 channels would probably confuse
{
// TODO: better to use empty Blip_Buffer so caller never has to check for NULL?
chan.left = 0;
chan.center = 0;
chan.right = 0;
}
// Mono_Buffer
Mono_Buffer::Mono_Buffer() : Multi_Buffer( 1 )
{
chan.center = &buf;
chan.left = &buf;
chan.right = &buf;
}
Mono_Buffer::~Mono_Buffer() { }
blargg_err_t Mono_Buffer::set_sample_rate( long rate, int msec )
{
RETURN_ERR( buf.set_sample_rate( rate, msec ) );
return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() );
}
// Tracked_Blip_Buffer
Tracked_Blip_Buffer::Tracked_Blip_Buffer()
{
last_non_silence = 0;
}
void Tracked_Blip_Buffer::clear()
{
last_non_silence = 0;
Blip_Buffer::clear();
}
void Tracked_Blip_Buffer::end_frame( blip_time_t t )
{
Blip_Buffer::end_frame( t );
if ( clear_modified() )
last_non_silence = samples_avail() + blip_buffer_extra_;
}
blip_ulong Tracked_Blip_Buffer::non_silent() const
{
return last_non_silence | unsettled();
}
inline void Tracked_Blip_Buffer::remove_( long n )
{
if ( (last_non_silence -= n) < 0 )
last_non_silence = 0;
}
void Tracked_Blip_Buffer::remove_silence( long n )
{
remove_( n );
Blip_Buffer::remove_silence( n );
}
void Tracked_Blip_Buffer::remove_samples( long n )
{
remove_( n );
Blip_Buffer::remove_samples( n );
}
void Tracked_Blip_Buffer::remove_all_samples()
{
long avail = samples_avail();
if ( !non_silent() )
remove_silence( avail );
else
remove_samples( avail );
}
long Tracked_Blip_Buffer::read_samples( blip_sample_t* out, long count )
{
count = Blip_Buffer::read_samples( out, count );
remove_( count );
return count;
}
// Stereo_Buffer
int const stereo = 2;
Stereo_Buffer::Stereo_Buffer() : Multi_Buffer( 2 )
{
chan.center = mixer.bufs [2] = &bufs [2];
chan.left = mixer.bufs [0] = &bufs [0];
chan.right = mixer.bufs [1] = &bufs [1];
mixer.samples_read = 0;
}
Stereo_Buffer::~Stereo_Buffer() { }
blargg_err_t Stereo_Buffer::set_sample_rate( long rate, int msec )
{
mixer.samples_read = 0;
for ( int i = bufs_size; --i >= 0; )
RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) );
return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() );
}
void Stereo_Buffer::clock_rate( long rate )
{
for ( int i = bufs_size; --i >= 0; )
bufs [i].clock_rate( rate );
}
void Stereo_Buffer::bass_freq( int bass )
{
for ( int i = bufs_size; --i >= 0; )
bufs [i].bass_freq( bass );
}
void Stereo_Buffer::clear()
{
mixer.samples_read = 0;
for ( int i = bufs_size; --i >= 0; )
bufs [i].clear();
}
void Stereo_Buffer::end_frame( blip_time_t time )
{
for ( int i = bufs_size; --i >= 0; )
bufs [i].end_frame( time );
}
long Stereo_Buffer::read_samples( blip_sample_t* out, long out_size )
{
require( (out_size & 1) == 0 ); // must read an even number of samples
out_size = min( out_size, samples_avail() );
int pair_count = int (out_size >> 1);
if ( pair_count )
{
mixer.read_pairs( out, pair_count );
if ( samples_avail() <= 0 || immediate_removal() )
{
for ( int i = bufs_size; --i >= 0; )
{
buf_t& b = bufs [i];
// TODO: might miss non-silence settling since it checks END of last read
if ( !b.non_silent() )
b.remove_silence( mixer.samples_read );
else
b.remove_samples( mixer.samples_read );
}
mixer.samples_read = 0;
}
}
return out_size;
}
// Stereo_Mixer
// mixers use a single index value to improve performance on register-challenged processors
// offset goes from negative to zero
void Stereo_Mixer::read_pairs( blip_sample_t* out, int count )
{
// TODO: if caller never marks buffers as modified, uses mono
// except that buffer isn't cleared, so caller can encounter
// subtle problems and not realize the cause.
samples_read += count;
if ( bufs [0]->non_silent() | bufs [1]->non_silent() )
mix_stereo( out, count );
else
mix_mono( out, count );
}
void Stereo_Mixer::mix_mono( blip_sample_t* out_, int count )
{
int const bass = BLIP_READER_BASS( *bufs [2] );
BLIP_READER_BEGIN( center, *bufs [2] );
BLIP_READER_ADJ_( center, samples_read );
typedef blip_sample_t stereo_blip_sample_t [stereo];
stereo_blip_sample_t* BLIP_RESTRICT out = (stereo_blip_sample_t*) out_ + count;
int offset = -count;
do
{
blargg_long s = BLIP_READER_READ( center );
BLIP_READER_NEXT_IDX_( center, bass, offset );
BLIP_CLAMP( s, s );
out [offset] [0] = (blip_sample_t) s;
out [offset] [1] = (blip_sample_t) s;
}
while ( ++offset );
BLIP_READER_END( center, *bufs [2] );
}
void Stereo_Mixer::mix_stereo( blip_sample_t* out_, int count )
{
blip_sample_t* BLIP_RESTRICT out = out_ + count * stereo;
// do left + center and right + center separately to reduce register load
Tracked_Blip_Buffer* const* buf = &bufs [2];
while ( true ) // loop runs twice
{
--buf;
--out;
int const bass = BLIP_READER_BASS( *bufs [2] );
BLIP_READER_BEGIN( side, **buf );
BLIP_READER_BEGIN( center, *bufs [2] );
BLIP_READER_ADJ_( side, samples_read );
BLIP_READER_ADJ_( center, samples_read );
int offset = -count;
do
{
blargg_long s = BLIP_READER_READ_RAW( center ) + BLIP_READER_READ_RAW( side );
s >>= blip_sample_bits - 16;
BLIP_READER_NEXT_IDX_( side, bass, offset );
BLIP_READER_NEXT_IDX_( center, bass, offset );
BLIP_CLAMP( s, s );
++offset; // before write since out is decremented to slightly before end
out [offset * stereo] = (blip_sample_t) s;
}
while ( offset );
BLIP_READER_END( side, **buf );
if ( buf != bufs )
continue;
// only end center once
BLIP_READER_END( center, *bufs [2] );
break;
}
}

View File

@ -0,0 +1,205 @@
// Multi-channel sound buffer interface, and basic mono and stereo buffers
// Blip_Buffer 0.4.1
#ifndef MULTI_BUFFER_H
#define MULTI_BUFFER_H
#include "blargg_common.h"
#include "Blip_Buffer.h"
// Interface to one or more Blip_Buffers mapped to one or more channels
// consisting of left, center, and right buffers.
class Multi_Buffer {
public:
Multi_Buffer( int samples_per_frame );
virtual ~Multi_Buffer() { }
// Sets the number of channels available and optionally their types
// (type information used by Effects_Buffer)
enum { type_index_mask = 0xFF };
enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type };
virtual blargg_err_t set_channel_count( int, int const* types = 0 );
int channel_count() const { return channel_count_; }
// Gets indexed channel, from 0 to channel count - 1
struct channel_t {
Blip_Buffer* center;
Blip_Buffer* left;
Blip_Buffer* right;
};
virtual channel_t channel( int index ) BLARGG_PURE( ; )
// See Blip_Buffer.h
virtual blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) BLARGG_PURE( ; )
virtual void clock_rate( long ) BLARGG_PURE( { } )
virtual void bass_freq( int ) BLARGG_PURE( { } )
virtual void clear() BLARGG_PURE( { } )
long sample_rate() const;
// Length of buffer, in milliseconds
int length() const;
// See Blip_Buffer.h
virtual void end_frame( blip_time_t ) BLARGG_PURE( { } )
// Number of samples per output frame (1 = mono, 2 = stereo)
int samples_per_frame() const;
// Count of changes to channel configuration. Incremented whenever
// a change is made to any of the Blip_Buffers for any channel.
unsigned channels_changed_count() { return channels_changed_count_; }
// See Blip_Buffer.h
virtual long read_samples( blip_sample_t*, long ) BLARGG_PURE( { return 0; } )
virtual long samples_avail() const BLARGG_PURE( { return 0; } )
public:
BLARGG_DISABLE_NOTHROW
void disable_immediate_removal() { immediate_removal_ = false; }
protected:
bool immediate_removal() const { return immediate_removal_; }
int const* channel_types() const { return channel_types_; }
void channels_changed() { channels_changed_count_++; }
private:
// noncopyable
Multi_Buffer( const Multi_Buffer& );
Multi_Buffer& operator = ( const Multi_Buffer& );
unsigned channels_changed_count_;
long sample_rate_;
int length_;
int channel_count_;
int const samples_per_frame_;
int const* channel_types_;
bool immediate_removal_;
};
// Uses a single buffer and outputs mono samples.
class Mono_Buffer : public Multi_Buffer {
Blip_Buffer buf;
channel_t chan;
public:
// Buffer used for all channels
Blip_Buffer* center() { return &buf; }
public:
Mono_Buffer();
~Mono_Buffer();
blargg_err_t set_sample_rate( long rate, int msec = blip_default_length );
void clock_rate( long rate ) { buf.clock_rate( rate ); }
void bass_freq( int freq ) { buf.bass_freq( freq ); }
void clear() { buf.clear(); }
long samples_avail() const { return buf.samples_avail(); }
long read_samples( blip_sample_t* p, long s ) { return buf.read_samples( p, s ); }
channel_t channel( int ) { return chan; }
void end_frame( blip_time_t t ) { buf.end_frame( t ); }
};
class Tracked_Blip_Buffer : public Blip_Buffer {
public:
// Non-zero if buffer still has non-silent samples in it. Requires that you call
// set_modified() appropriately.
blip_ulong non_silent() const;
// remove_samples( samples_avail() )
void remove_all_samples();
public:
BLARGG_DISABLE_NOTHROW
long read_samples( blip_sample_t*, long );
void remove_silence( long );
void remove_samples( long );
Tracked_Blip_Buffer();
void clear();
void end_frame( blip_time_t );
private:
blip_long last_non_silence;
void remove_( long );
};
class Stereo_Mixer {
public:
Tracked_Blip_Buffer* bufs [3];
blargg_long samples_read;
Stereo_Mixer() : samples_read( 0 ) { }
void read_pairs( blip_sample_t* out, int count );
private:
void mix_mono ( blip_sample_t* out, int pair_count );
void mix_stereo( blip_sample_t* out, int pair_count );
};
// Uses three buffers (one for center) and outputs stereo sample pairs.
class Stereo_Buffer : public Multi_Buffer {
public:
// Buffers used for all channels
Blip_Buffer* center() { return &bufs [2]; }
Blip_Buffer* left() { return &bufs [0]; }
Blip_Buffer* right() { return &bufs [1]; }
public:
Stereo_Buffer();
~Stereo_Buffer();
blargg_err_t set_sample_rate( long, int msec = blip_default_length );
void clock_rate( long );
void bass_freq( int );
void clear();
channel_t channel( int ) { return chan; }
void end_frame( blip_time_t );
long samples_avail() const { return (bufs [0].samples_avail() - mixer.samples_read) * 2; }
long read_samples( blip_sample_t*, long );
private:
enum { bufs_size = 3 };
typedef Tracked_Blip_Buffer buf_t;
buf_t bufs [bufs_size];
Stereo_Mixer mixer;
channel_t chan;
long samples_avail_;
};
// Silent_Buffer generates no samples, useful where no sound is wanted
class Silent_Buffer : public Multi_Buffer {
channel_t chan;
public:
Silent_Buffer();
blargg_err_t set_sample_rate( long rate, int msec = blip_default_length );
void clock_rate( long ) { }
void bass_freq( int ) { }
void clear() { }
channel_t channel( int ) { return chan; }
void end_frame( blip_time_t ) { }
long samples_avail() const { return 0; }
long read_samples( blip_sample_t*, long ) { return 0; }
};
inline blargg_err_t Multi_Buffer::set_sample_rate( long rate, int msec )
{
sample_rate_ = rate;
length_ = msec;
return 0;
}
inline blargg_err_t Silent_Buffer::set_sample_rate( long rate, int msec )
{
return Multi_Buffer::set_sample_rate( rate, msec );
}
inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; }
inline long Multi_Buffer::sample_rate() const { return sample_rate_; }
inline int Multi_Buffer::length() const { return length_; }
inline blargg_err_t Multi_Buffer::set_channel_count( int n, int const* types )
{
channel_count_ = n;
channel_types_ = types;
return 0;
}
#endif

View File

@ -0,0 +1,206 @@
// Sets up common environment for Shay Green's libraries.
// To change configuration options, modify blargg_config.h, not this file.
// Gb_Snd_Emu 0.2.0
#ifndef BLARGG_COMMON_H
#define BLARGG_COMMON_H
#include <stddef.h>
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
#undef BLARGG_COMMON_H
// allow blargg_config.h to #include blargg_common.h
#include "blargg_config.h"
#ifndef BLARGG_COMMON_H
#define BLARGG_COMMON_H
// BLARGG_RESTRICT: equivalent to restrict, where supported
#if __GNUC__ >= 3 || _MSC_VER >= 1100
#define BLARGG_RESTRICT __restrict
#else
#define BLARGG_RESTRICT
#endif
// STATIC_CAST(T,expr): Used in place of static_cast<T> (expr)
// CONST_CAST( T,expr): Used in place of const_cast<T> (expr)
#ifndef STATIC_CAST
#if __GNUC__ >= 4
#define STATIC_CAST(T,expr) static_cast<T> (expr)
#define CONST_CAST( T,expr) const_cast<T> (expr)
#else
#define STATIC_CAST(T,expr) ((T) (expr))
#define CONST_CAST( T,expr) ((T) (expr))
#endif
#endif
// blargg_err_t (0 on success, otherwise error string)
#ifndef blargg_err_t
typedef const char* blargg_err_t;
#endif
// blargg_vector - very lightweight vector of POD types (no constructor/destructor)
template<class T>
class blargg_vector {
T* begin_;
size_t size_;
public:
blargg_vector() : begin_( 0 ), size_( 0 ) { }
~blargg_vector() { free( begin_ ); }
size_t size() const { return size_; }
T* begin() const { return begin_; }
T* end() const { return begin_ + size_; }
blargg_err_t resize( size_t n )
{
// TODO: blargg_common.cpp to hold this as an outline function, ugh
void* p = realloc( begin_, n * sizeof (T) );
if ( p )
begin_ = (T*) p;
else if ( n > size_ ) // realloc failure only a problem if expanding
return "Out of memory";
size_ = n;
return 0;
}
void clear() { void* p = begin_; begin_ = 0; size_ = 0; free( p ); }
T& operator [] ( size_t n ) const
{
assert( n <= size_ ); // <= to allow past-the-end value
return begin_ [n];
}
};
#ifndef BLARGG_DISABLE_NOTHROW
// throw spec mandatory in ISO C++ if operator new can return NULL
#if __cplusplus >= 199711 || __GNUC__ >= 3
#define BLARGG_THROWS( spec ) throw spec
#else
#define BLARGG_THROWS( spec )
#endif
#define BLARGG_DISABLE_NOTHROW \
void* operator new ( size_t s ) BLARGG_THROWS(()) { return malloc( s ); }\
void operator delete ( void* p ) { free( p ); }
#define BLARGG_NEW new
#else
#include <new>
#define BLARGG_NEW new (std::nothrow)
#endif
// BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant)
#define BLARGG_4CHAR( a, b, c, d ) \
((a&0xFF)*0x1000000 + (b&0xFF)*0x10000 + (c&0xFF)*0x100 + (d&0xFF))
// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0.
#ifndef BOOST_STATIC_ASSERT
#ifdef _MSC_VER
// MSVC6 (_MSC_VER < 1300) fails for use of __LINE__ when /Zl is specified
#define BOOST_STATIC_ASSERT( expr ) \
void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] )
#else
// Some other compilers fail when declaring same function multiple times in class,
// so differentiate them by line
#define BOOST_STATIC_ASSERT( expr ) \
void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] )
#endif
#endif
// BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1,
// compiler is assumed to support bool. If undefined, availability is determined.
#ifndef BLARGG_COMPILER_HAS_BOOL
#if defined (__MWERKS__)
#if !__option(bool)
#define BLARGG_COMPILER_HAS_BOOL 0
#endif
#elif defined (_MSC_VER)
#if _MSC_VER < 1100
#define BLARGG_COMPILER_HAS_BOOL 0
#endif
#elif defined (__GNUC__)
// supports bool
#elif __cplusplus < 199711
#define BLARGG_COMPILER_HAS_BOOL 0
#endif
#endif
#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL
// If you get errors here, modify your blargg_config.h file
typedef int bool;
const bool true = 1;
const bool false = 0;
#endif
// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough
#if INT_MAX < 0x7FFFFFFF || LONG_MAX == 0x7FFFFFFF
typedef long blargg_long;
#else
typedef int blargg_long;
#endif
#if UINT_MAX < 0xFFFFFFFF || ULONG_MAX == 0xFFFFFFFF
typedef unsigned long blargg_ulong;
#else
typedef unsigned blargg_ulong;
#endif
// BOOST::int8_t etc.
// HAVE_STDINT_H: If defined, use <stdint.h> for int8_t etc.
#if defined (HAVE_STDINT_H)
#include <stdint.h>
#define BOOST
// HAVE_INTTYPES_H: If defined, use <stdint.h> for int8_t etc.
#elif defined (HAVE_INTTYPES_H)
#include <inttypes.h>
#define BOOST
#else
struct BOOST
{
#if UCHAR_MAX == 0xFF && SCHAR_MAX == 0x7F
typedef signed char int8_t;
typedef unsigned char uint8_t;
#else
// No suitable 8-bit type available
typedef struct see_blargg_common_h int8_t;
typedef struct see_blargg_common_h uint8_t;
#endif
#if USHRT_MAX == 0xFFFF
typedef short int16_t;
typedef unsigned short uint16_t;
#else
// No suitable 16-bit type available
typedef struct see_blargg_common_h int16_t;
typedef struct see_blargg_common_h uint16_t;
#endif
#if ULONG_MAX == 0xFFFFFFFF
typedef long int32_t;
typedef unsigned long uint32_t;
#elif UINT_MAX == 0xFFFFFFFF
typedef int int32_t;
typedef unsigned int uint32_t;
#else
// No suitable 32-bit type available
typedef struct see_blargg_common_h int32_t;
typedef struct see_blargg_common_h uint32_t;
#endif
};
#endif
#if __GNUC__ >= 3
#define BLARGG_DEPRECATED __attribute__ ((deprecated))
#else
#define BLARGG_DEPRECATED
#endif
// Use in place of "= 0;" for a pure virtual, since these cause calls to std C++ lib.
// During development, BLARGG_PURE( x ) expands to = 0;
// virtual int func() BLARGG_PURE( { return 0; } )
#ifndef BLARGG_PURE
#define BLARGG_PURE( def ) def
#endif
#endif
#endif

View File

@ -0,0 +1,33 @@
// $package user configuration file. Don't replace when updating library.
#ifndef BLARGG_CONFIG_H
#define BLARGG_CONFIG_H
// Uncomment to have Gb_Apu run at 4x normal clock rate (16777216 Hz), useful in
// a Game Boy Advance emulator.
#define GB_APU_OVERCLOCK 4
#define GB_APU_CUSTOM_STATE 1
// Uncomment to enable platform-specific (and possibly non-portable) optimizations.
//#define BLARGG_NONPORTABLE 1
// Uncomment if automatic byte-order determination doesn't work
//#define BLARGG_BIG_ENDIAN 1
// Uncomment to use zlib for transparent decompression of gzipped files
//#define HAVE_ZLIB_H
// Uncomment if you get errors in the bool section of blargg_common.h
//#define BLARGG_COMPILER_HAS_BOOL 1
// Uncomment to disable out-of-memory exceptions
//#include <memory>
//#define BLARGG_NEW new (std::nothrow)
// Use standard config.h if present
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#endif

View File

@ -0,0 +1,92 @@
/* Included at the beginning of library source files, AFTER all other #include lines.
Sets up helpful macros and services used in my source code. Since this is only "active"
in my source code, I don't have to worry about polluting the global namespace with
unprefixed names. */
// Gb_Snd_Emu 0.2.0
#ifndef BLARGG_SOURCE_H
#define BLARGG_SOURCE_H
// The following four macros are for debugging only. Some or all might be defined
// to do nothing, depending on the circumstances. Described is what happens when
// a particular macro is defined to do something. When defined to do nothing, the
// macros do NOT evaluate their argument(s).
// If expr is false, prints file and line number, then aborts program. Meant for
// checking internal state and consistency. A failed assertion indicates a bug
// in MY code.
//
// void assert( bool expr );
#include <assert.h>
// If expr is false, prints file and line number, then aborts program. Meant for
// checking caller-supplied parameters and operations that are outside the control
// of the module. A failed requirement probably indicates a bug in YOUR code.
//
// void require( bool expr );
#undef require
#define require( expr ) assert( expr )
// Like printf() except output goes to debugging console/file.
//
// void dprintf( const char* format, ... );
static inline void blargg_dprintf_( const char*, ... ) { }
#undef dprintf
#define dprintf (1) ? (void) 0 : blargg_dprintf_
// If expr is false, prints file and line number to debug console/log, then
// continues execution normally. Meant for flagging potential problems or things
// that should be looked into, but that aren't serious problems.
//
// void check( bool expr );
#undef check
#define check( expr ) ((void) 0)
// If expr yields non-NULL error string, returns it from current function,
// otherwise continues normally.
#undef RETURN_ERR
#define RETURN_ERR( expr ) do { \
blargg_err_t blargg_return_err_ = (expr); \
if ( blargg_return_err_ ) return blargg_return_err_; \
} while ( 0 )
// If ptr is NULL, returns "Out of memory" error string, otherwise continues normally.
#undef CHECK_ALLOC
#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 )
// The usual min/max functions for built-in types.
//
// template<typename T> T min( T x, T y ) { return x < y ? x : y; }
// template<typename T> T max( T x, T y ) { return x > y ? x : y; }
#define BLARGG_DEF_MIN_MAX( type ) \
static inline type blargg_min( type x, type y ) { if ( y < x ) x = y; return x; }\
static inline type blargg_max( type x, type y ) { if ( x < y ) x = y; return x; }
BLARGG_DEF_MIN_MAX( int )
BLARGG_DEF_MIN_MAX( unsigned )
BLARGG_DEF_MIN_MAX( long )
BLARGG_DEF_MIN_MAX( unsigned long )
BLARGG_DEF_MIN_MAX( float )
BLARGG_DEF_MIN_MAX( double )
#undef min
#define min blargg_min
#undef max
#define max blargg_max
// typedef unsigned char byte;
typedef unsigned char blargg_byte;
#undef byte
#define byte blargg_byte
// deprecated
#define BLARGG_CHECK_ALLOC CHECK_ALLOC
#define BLARGG_RETURN_ERR RETURN_ERR
// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf and check
#ifdef BLARGG_SOURCE_BEGIN
#include BLARGG_SOURCE_BEGIN
#endif
#endif