cog/Frameworks/GME/gme/Vgm_Core.cpp

2308 lines
55 KiB
C++

// Game_Music_Emu $vers. http://www.slack.net/~ant/
#include "Vgm_Core.h"
#include "dac_control.h"
#include "blargg_endian.h"
#include <math.h>
// Needed for OKIM6295 system detection
#include "Vgm_Emu.h"
/* Copyright (C) 2003-2008 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 stereo = 2;
int const fm_time_bits = 12;
int const blip_time_bits = 12;
enum {
cmd_gg_stereo = 0x4F,
cmd_gg_stereo_2 = 0x3F,
cmd_psg = 0x50,
cmd_psg_2 = 0x30,
cmd_ym2413 = 0x51,
cmd_ym2413_2 = 0xA1,
cmd_ym2612_port0 = 0x52,
cmd_ym2612_2_port0 = 0xA2,
cmd_ym2612_port1 = 0x53,
cmd_ym2612_2_port1 = 0xA3,
cmd_ym2151 = 0x54,
cmd_ym2151_2 = 0xA4,
cmd_ym2203 = 0x55,
cmd_ym2203_2 = 0xA5,
cmd_ym2608_port0 = 0x56,
cmd_ym2608_2_port0 = 0xA6,
cmd_ym2608_port1 = 0x57,
cmd_ym2608_2_port1 = 0xA7,
cmd_ym2610_port0 = 0x58,
cmd_ym2610_2_port0 = 0xA8,
cmd_ym2610_port1 = 0x59,
cmd_ym2610_2_port1 = 0xA9,
cmd_ym3812 = 0x5A,
cmd_ym3812_2 = 0xAA,
cmd_ymz280b = 0x5D,
cmd_ymf262_port0 = 0x5E,
cmd_ymf262_2_port0 = 0xAE,
cmd_ymf262_port1 = 0x5F,
cmd_ymf262_2_port1 = 0xAF,
cmd_delay = 0x61,
cmd_delay_735 = 0x62,
cmd_delay_882 = 0x63,
cmd_byte_delay = 0x64,
cmd_end = 0x66,
cmd_data_block = 0x67,
cmd_ram_block = 0x68,
cmd_short_delay = 0x70,
cmd_pcm_delay = 0x80,
cmd_dacctl_setup = 0x90,
cmd_dacctl_data = 0x91,
cmd_dacctl_freq = 0x92,
cmd_dacctl_play = 0x93,
cmd_dacctl_stop = 0x94,
cmd_dacctl_playblock= 0x95,
cmd_ay8910 = 0xA0,
cmd_rf5c68 = 0xB0,
cmd_rf5c164 = 0xB1,
cmd_pwm = 0xB2,
cmd_gbdmg_write = 0xB3,
cmd_okim6258_write = 0xB7,
cmd_okim6295_write = 0xB8,
cmd_huc6280_write = 0xB9,
cmd_k053260_write = 0xBA,
cmd_segapcm_write = 0xC0,
cmd_rf5c68_mem = 0xC1,
cmd_rf5c164_mem = 0xC2,
cmd_qsound_write = 0xC4,
cmd_k051649_write = 0xD2,
cmd_k054539_write = 0xD3,
cmd_c140 = 0xD4,
cmd_pcm_seek = 0xE0,
rf5c68_ram_block = 0x01,
rf5c164_ram_block = 0x02,
pcm_block_type = 0x00,
pcm_aux_block_type = 0x40,
rom_block_type = 0x80,
ram_block_type = 0xC0,
rom_segapcm = 0x80,
rom_ym2608_deltat = 0x81,
rom_ym2610_adpcm = 0x82,
rom_ym2610_deltat = 0x83,
rom_ymz280b = 0x86,
rom_okim6295 = 0x8B,
rom_k054539 = 0x8C,
rom_c140 = 0x8D,
rom_k053260 = 0x8E,
rom_qsound = 0x8F,
ram_rf5c68 = 0xC0,
ram_rf5c164 = 0xC1,
ram_nesapu = 0xC2,
ym2612_dac_port = 0x2A,
ym2612_dac_pan_port = 0xB6
};
inline int command_len( int command )
{
static byte const lens [0x10] = {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
1,1,1,2,2,3,1,1,1,1,3,3,4,4,5,5
};
int len = lens [command >> 4];
check( len != 1 );
return len;
}
int Vgm_Core::run_ym2151( int chip, int time )
{
return ym2151[!!chip].run_until( time );
}
int Vgm_Core::run_ym2203( int chip, int time )
{
return ym2203[!!chip].run_until( time );
}
int Vgm_Core::run_ym2413( int chip, int time )
{
return ym2413[!!chip].run_until( time );
}
int Vgm_Core::run_ym2612( int chip, int time )
{
return ym2612[!!chip].run_until( time );
}
int Vgm_Core::run_ym2610( int chip, int time )
{
return ym2610[!!chip].run_until( time );
}
int Vgm_Core::run_ym2608( int chip, int time )
{
return ym2608[!!chip].run_until( time );
}
int Vgm_Core::run_ym3812( int chip, int time )
{
return ym3812[!!chip].run_until( time );
}
int Vgm_Core::run_ymf262( int chip, int time )
{
return ymf262[!!chip].run_until( time );
}
int Vgm_Core::run_ymz280b( int time )
{
return ymz280b.run_until( time );
}
int Vgm_Core::run_c140( int time )
{
return c140.run_until( time );
}
int Vgm_Core::run_segapcm( int time )
{
return segapcm.run_until( time );
}
int Vgm_Core::run_rf5c68( int time )
{
return rf5c68.run_until( time );
}
int Vgm_Core::run_rf5c164( int time )
{
return rf5c164.run_until( time );
}
int Vgm_Core::run_pwm( int time )
{
return pwm.run_until( time );
}
int Vgm_Core::run_okim6258( int chip, int time )
{
chip = !!chip;
if ( okim6258[chip].enabled() )
{
int current_clock = okim6258[chip].get_clock();
if ( okim6258_hz[chip] != current_clock )
{
okim6258_hz[chip] = current_clock;
okim6258[chip].setup( (double)okim6258_hz[chip] / vgm_rate, 0.85, 1.0 );
}
}
return okim6258[chip].run_until( time );
}
int Vgm_Core::run_okim6295( int chip, int time )
{
return okim6295[!!chip].run_until( time );
}
int Vgm_Core::run_k051649( int time )
{
return k051649.run_until( time );
}
int Vgm_Core::run_k053260( int time )
{
return k053260.run_until( time );
}
int Vgm_Core::run_k054539( int time )
{
return k054539.run_until( time );
}
int Vgm_Core::run_qsound( int chip, int time )
{
return qsound[!!chip].run_until( time );
}
/* Recursive fun starts here! */
int Vgm_Core::run_dac_control( int time )
{
if (dac_control_recursion) return 1;
++dac_control_recursion;
for ( unsigned i = 0; i < DacCtrlUsed; i++ )
{
int time_start = DacCtrlTime[DacCtrlMap[i]];
if ( time > time_start )
{
DacCtrlTime[DacCtrlMap[i]] = time;
daccontrol_update( dac_control [i], time_start, time - time_start );
}
}
--dac_control_recursion;
return 1;
}
Vgm_Core::Vgm_Core()
{
blip_buf[0] = stereo_buf[0].center();
blip_buf[1] = blip_buf[0];
has_looped = false;
DacCtrlUsed = 0;
dac_control = NULL;
memset( PCMBank, 0, sizeof( PCMBank ) );
memset( &PCMTbl, 0, sizeof( PCMTbl ) );
memset( DacCtrl, 0, sizeof( DacCtrl ) );
memset( DacCtrlTime, 0, sizeof( DacCtrlTime ) );
}
Vgm_Core::~Vgm_Core()
{
for (unsigned i = 0; i < DacCtrlUsed; i++) device_stop_daccontrol( dac_control [i] );
if ( dac_control ) free( dac_control );
for (unsigned i = 0; i < PCM_BANK_COUNT; i++)
{
if ( PCMBank [i].Bank ) free( PCMBank [i].Bank );
if ( PCMBank [i].Data ) free( PCMBank [i].Data );
}
if ( PCMTbl.Entries ) free( PCMTbl.Entries );
}
typedef unsigned int FUINT8;
typedef unsigned int FUINT16;
void Vgm_Core::ReadPCMTable(unsigned DataSize, const byte* Data)
{
byte ValSize;
unsigned TblSize;
PCMTbl.ComprType = Data[0x00];
PCMTbl.CmpSubType = Data[0x01];
PCMTbl.BitDec = Data[0x02];
PCMTbl.BitCmp = Data[0x03];
PCMTbl.EntryCount = get_le16( Data + 0x04 );
ValSize = (PCMTbl.BitDec + 7) / 8;
TblSize = PCMTbl.EntryCount * ValSize;
PCMTbl.Entries = realloc(PCMTbl.Entries, TblSize);
memcpy(PCMTbl.Entries, Data + 0x06, TblSize);
}
bool Vgm_Core::DecompressDataBlk(VGM_PCM_DATA* Bank, unsigned DataSize, const byte* Data)
{
UINT8 ComprType;
UINT8 BitDec;
FUINT8 BitCmp;
UINT8 CmpSubType;
UINT16 AddVal;
const UINT8* InPos;
const UINT8* InDataEnd;
UINT8* OutPos;
const UINT8* OutDataEnd;
FUINT16 InVal;
FUINT16 OutVal = 0;
FUINT8 ValSize;
FUINT8 InShift;
FUINT8 OutShift;
UINT8* Ent1B;
UINT16* Ent2B;
// ReadBits Variables
FUINT8 BitsToRead;
FUINT8 BitReadVal;
FUINT8 InValB;
FUINT8 BitMask;
FUINT8 OutBit;
// Variables for DPCM
UINT16 OutMask;
ComprType = Data[0x00];
Bank->DataSize = get_le32( Data + 0x01 );
switch(ComprType)
{
case 0x00: // n-Bit compression
BitDec = Data[0x05];
BitCmp = Data[0x06];
CmpSubType = Data[0x07];
AddVal = get_le16( Data + 0x08 );
if (CmpSubType == 0x02)
{
Ent1B = (UINT8*)PCMTbl.Entries;
Ent2B = (UINT16*)PCMTbl.Entries;
if (! PCMTbl.EntryCount)
{
Bank->DataSize = 0x00;
return false;
}
else if (BitDec != PCMTbl.BitDec || BitCmp != PCMTbl.BitCmp)
{
Bank->DataSize = 0x00;
return false;
}
}
ValSize = (BitDec + 7) / 8;
InPos = Data + 0x0A;
InDataEnd = Data + DataSize;
InShift = 0;
OutShift = BitDec - BitCmp;
OutDataEnd = Bank->Data + Bank->DataSize;
for (OutPos = Bank->Data; OutPos < OutDataEnd && InPos < InDataEnd; OutPos += ValSize)
{
//InVal = ReadBits(Data, InPos, &InShift, BitCmp);
// inlined - is 30% faster
OutBit = 0x00;
InVal = 0x0000;
BitsToRead = BitCmp;
while(BitsToRead)
{
BitReadVal = (BitsToRead >= 8) ? 8 : BitsToRead;
BitsToRead -= BitReadVal;
BitMask = (1 << BitReadVal) - 1;
InShift += BitReadVal;
InValB = (*InPos << InShift >> 8) & BitMask;
if (InShift >= 8)
{
InShift -= 8;
InPos ++;
if (InShift)
InValB |= (*InPos << InShift >> 8) & BitMask;
}
InVal |= InValB << OutBit;
OutBit += BitReadVal;
}
switch(CmpSubType)
{
case 0x00: // Copy
OutVal = InVal + AddVal;
break;
case 0x01: // Shift Left
OutVal = (InVal << OutShift) + AddVal;
break;
case 0x02: // Table
switch(ValSize)
{
case 0x01:
OutVal = Ent1B[InVal];
break;
case 0x02:
OutVal = Ent2B[InVal];
break;
}
break;
}
//memcpy(OutPos, &OutVal, ValSize);
if (ValSize == 0x01)
*((UINT8*)OutPos) = (UINT8)OutVal;
else //if (ValSize == 0x02)
*((UINT16*)OutPos) = (UINT16)OutVal;
}
break;
case 0x01: // Delta-PCM
BitDec = Data[0x05];
BitCmp = Data[0x06];
OutVal = get_le16( Data + 0x08 );
Ent1B = (UINT8*)PCMTbl.Entries;
Ent2B = (UINT16*)PCMTbl.Entries;
if (! PCMTbl.EntryCount)
{
Bank->DataSize = 0x00;
return false;
}
else if (BitDec != PCMTbl.BitDec || BitCmp != PCMTbl.BitCmp)
{
Bank->DataSize = 0x00;
return false;
}
ValSize = (BitDec + 7) / 8;
OutMask = (1 << BitDec) - 1;
InPos = Data + 0x0A;
InDataEnd = Data + DataSize;
InShift = 0;
OutShift = BitDec - BitCmp;
OutDataEnd = Bank->Data + Bank->DataSize;
AddVal = 0x0000;
for (OutPos = Bank->Data; OutPos < OutDataEnd && InPos < InDataEnd; OutPos += ValSize)
{
//InVal = ReadBits(Data, InPos, &InShift, BitCmp);
// inlined - is 30% faster
OutBit = 0x00;
InVal = 0x0000;
BitsToRead = BitCmp;
while(BitsToRead)
{
BitReadVal = (BitsToRead >= 8) ? 8 : BitsToRead;
BitsToRead -= BitReadVal;
BitMask = (1 << BitReadVal) - 1;
InShift += BitReadVal;
InValB = (*InPos << InShift >> 8) & BitMask;
if (InShift >= 8)
{
InShift -= 8;
InPos ++;
if (InShift)
InValB |= (*InPos << InShift >> 8) & BitMask;
}
InVal |= InValB << OutBit;
OutBit += BitReadVal;
}
switch(ValSize)
{
case 0x01:
AddVal = Ent1B[InVal];
OutVal += AddVal;
OutVal &= OutMask;
*((UINT8*)OutPos) = (UINT8)OutVal;
break;
case 0x02:
AddVal = Ent2B[InVal];
OutVal += AddVal;
OutVal &= OutMask;
*((UINT16*)OutPos) = (UINT16)OutVal;
break;
}
}
break;
default:
return false;
}
return true;
}
void Vgm_Core::AddPCMData(byte Type, unsigned DataSize, const byte* Data)
{
unsigned CurBnk;
VGM_PCM_BANK* TempPCM;
VGM_PCM_DATA* TempBnk;
unsigned BankSize;
bool RetVal;
if ((Type & 0x3F) >= PCM_BANK_COUNT || has_looped)
return;
if (Type == 0x7F)
{
ReadPCMTable( DataSize, Data );
return;
}
TempPCM = &PCMBank[Type & 0x3F];
CurBnk = TempPCM->BankCount;
TempPCM->BankCount ++;
TempPCM->BnkPos ++;
if (TempPCM->BnkPos < TempPCM->BankCount)
return; // Speed hack (for restarting playback)
TempPCM->Bank = (VGM_PCM_DATA*)realloc(TempPCM->Bank,
sizeof(VGM_PCM_DATA) * TempPCM->BankCount);
if (! (Type & 0x40))
BankSize = DataSize;
else
BankSize = get_le32( Data + 1 );
TempPCM->Data = ( byte * ) realloc(TempPCM->Data, TempPCM->DataSize + BankSize);
TempBnk = &TempPCM->Bank[CurBnk];
TempBnk->DataStart = TempPCM->DataSize;
if (! (Type & 0x40))
{
TempBnk->DataSize = DataSize;
TempBnk->Data = TempPCM->Data + TempBnk->DataStart;
memcpy(TempBnk->Data, Data, DataSize);
}
else
{
TempBnk->Data = TempPCM->Data + TempBnk->DataStart;
RetVal = DecompressDataBlk(TempBnk, DataSize, Data);
if (! RetVal)
{
TempBnk->Data = NULL;
TempBnk->DataSize = 0x00;
return;
}
}
TempPCM->DataSize += BankSize;
}
const byte* Vgm_Core::GetPointerFromPCMBank(byte Type, unsigned DataPos)
{
if (Type >= PCM_BANK_COUNT)
return NULL;
if (DataPos >= PCMBank[Type].DataSize)
return NULL;
return &PCMBank[Type].Data[DataPos];
}
void Vgm_Core::dac_control_grow(byte chip_id)
{
for ( unsigned i = 0; i < DacCtrlUsed; i++ )
{
if ( DacCtrlUsg [i] == chip_id )
{
device_reset_daccontrol( dac_control [i] );
return;
}
}
unsigned chip_mapped = DacCtrlUsed;
DacCtrlUsg [DacCtrlUsed++] = chip_id;
DacCtrlMap [chip_id] = chip_mapped;
dac_control = (void**) realloc( dac_control, DacCtrlUsed * sizeof(void*) );
dac_control [chip_mapped] = device_start_daccontrol( vgm_rate, this );
device_reset_daccontrol( dac_control [chip_mapped] );
}
extern "C" void chip_reg_write(void * context, UINT32 Sample, UINT8 ChipType, UINT8 ChipID, UINT8 Port, UINT8 Offset, UINT8 Data)
{
Vgm_Core * core = (Vgm_Core *) context;
core->chip_reg_write(Sample, ChipType, ChipID, Port, Offset, Data);
}
void Vgm_Core::chip_reg_write(unsigned Sample, byte ChipType, byte ChipID, byte Port, byte Offset, byte Data)
{
run_dac_control( Sample ); /* Let's get recursive! */
ChipID = !!ChipID;
switch (ChipType)
{
case 0x02:
switch (Port)
{
case 0:
if ( Offset == ym2612_dac_port )
{
write_pcm( Sample, ChipID, Data );
}
else if ( run_ym2612( ChipID, to_fm_time( Sample ) ) )
{
if ( Offset == 0x2B )
{
dac_disabled[ChipID] = (Data >> 7 & 1) - 1;
dac_amp[ChipID] |= dac_disabled[ChipID];
}
ym2612[ChipID].write0( Offset, Data );
}
break;
case 1:
if ( run_ym2612( ChipID, to_fm_time( Sample ) ) )
{
if ( Offset == ym2612_dac_pan_port )
{
Blip_Buffer * blip_buf = NULL;
switch ( Data >> 6 )
{
case 0: blip_buf = NULL; break;
case 1: blip_buf = stereo_buf[0].right(); break;
case 2: blip_buf = stereo_buf[0].left(); break;
case 3: blip_buf = stereo_buf[0].center(); break;
}
/*if ( this->blip_buf != blip_buf )
{
blip_time_t blip_time = to_psg_time( vgm_time );
if ( this->blip_buf ) pcm.offset_inline( blip_time, -dac_amp, this->blip_buf );
if ( blip_buf ) pcm.offset_inline( blip_time, dac_amp, blip_buf );
}*/
this->blip_buf[ChipID] = blip_buf;
}
ym2612[ChipID].write1( Offset, Data );
}
break;
}
break;
case 0x11:
if ( run_pwm( to_fm_time( Sample ) ) )
pwm.write( Port, ( ( Offset ) << 8 ) + Data );
break;
case 0x00:
psg[ChipID].write_data( to_psg_time( Sample ), Data );
break;
case 0x01:
if ( run_ym2413( ChipID, to_fm_time( Sample ) ) )
ym2413[ChipID].write( Offset, Data );
break;
case 0x03:
if ( run_ym2151( ChipID, to_fm_time( Sample ) ) )
ym2151[ChipID].write( Offset, Data );
break;
case 0x06:
if ( run_ym2203( ChipID, to_fm_time( Sample ) ) )
ym2203[ChipID].write( Offset, Data );
break;
case 0x07:
if ( run_ym2608( ChipID, to_fm_time( Sample ) ) )
{
switch (Port)
{
case 0: ym2608[ChipID].write0( Offset, Data ); break;
case 1: ym2608[ChipID].write1( Offset, Data ); break;
}
}
break;
case 0x08:
if ( run_ym2610( ChipID, to_fm_time( Sample ) ) )
{
switch (Port)
{
case 0: ym2610[ChipID].write0( Offset, Data ); break;
case 1: ym2610[ChipID].write1( Offset, Data ); break;
}
}
break;
case 0x09:
if ( run_ym3812( ChipID, to_fm_time( Sample ) ) )
ym3812[ChipID].write( Offset, Data );
break;
case 0x0C:
if ( run_ymf262( ChipID, to_fm_time( Sample ) ) )
{
switch (Port)
{
case 0: ymf262[ChipID].write0( Offset, Data ); break;
case 1: ymf262[ChipID].write1( Offset, Data ); break;
}
}
break;
case 0x0F:
if ( run_ymz280b( to_fm_time( Sample ) ) )
ymz280b.write( Offset, Data );
break;
case 0x12:
ay[ChipID].write_addr( Offset );
ay[ChipID].write_data( to_ay_time( Sample ), Data );
break;
case 0x13:
gbdmg[ChipID].write_register( to_gbdmg_time( Sample ), 0xFF10 + Offset, Data );
break;
case 0x17:
if ( run_okim6258( ChipID, to_fm_time( Sample ) ) )
okim6258[ChipID].write( Offset, Data );
break;
case 0x18:
if ( run_okim6295( ChipID, to_fm_time( Sample ) ) )
okim6295[ChipID].write( Offset, Data );
break;
case 0x19:
if ( run_k051649( to_fm_time( Sample ) ) )
k051649.write( Port, Offset, Data );
break;
case 0x1A:
if ( run_k054539( to_fm_time( Sample ) ) )
k054539.write( ( Port << 8 ) | Offset, Data );
break;
case 0x1B:
huc6280[ChipID].write_data( to_huc6280_time( Sample ), 0x800 + Offset, Data );
break;
case 0x1D:
if ( run_k053260( to_fm_time( Sample ) ) )
k053260.write( Offset, Data );
break;
case 0x1F:
if ( run_qsound( ChipID, Sample ) )
qsound[ ChipID ].write( Data, ( Port << 8 ) + Offset );
break;
}
}
void Vgm_Core::set_tempo( double t )
{
if ( file_begin() )
{
vgm_rate = (int) (44100 * t + 0.5);
blip_time_factor = (int) ((double)
(1 << blip_time_bits) / vgm_rate * stereo_buf[0].center()->clock_rate() + 0.5);
blip_ay_time_factor = (int) ((double)
(1 << blip_time_bits) / vgm_rate * stereo_buf[1].center()->clock_rate() + 0.5);
blip_huc6280_time_factor = (int) ((double)
(1 << blip_time_bits) / vgm_rate * stereo_buf[2].center()->clock_rate() + 0.5);
blip_gbdmg_time_factor = (int)((double)
(1 << blip_time_bits) / vgm_rate * stereo_buf[3].center()->clock_rate() + 0.5);
//dprintf( "blip_time_factor: %ld\n", blip_time_factor );
//dprintf( "vgm_rate: %ld\n", vgm_rate );
// TODO: remove? calculates vgm_rate more accurately (above differs at most by one Hz only)
//blip_time_factor = (int) floor( double (1 << blip_time_bits) * psg_rate_ / 44100 / t + 0.5 );
//vgm_rate = (int) floor( double (1 << blip_time_bits) * psg_rate_ / blip_time_factor + 0.5 );
fm_time_factor = 2 + (int) (fm_rate * (1 << fm_time_bits) / vgm_rate + 0.5);
}
}
bool Vgm_Core::header_t::valid_tag() const
{
return !memcmp( tag, "Vgm ", 4 );
}
int Vgm_Core::header_t::size() const
{
unsigned int version = get_le32( this->version );
unsigned int data_offset;
if ( version >= 0x150 )
{
data_offset = get_le32( this->data_offset );
if ( data_offset ) data_offset += offsetof( header_t, data_offset );
}
else data_offset = 0x40;
unsigned expected_size = ( version > 0x150 ) ? ( ( version > 0x160 ) ? unsigned(size_max) : unsigned(size_151) ) : unsigned(size_min);
if ( expected_size > data_offset ) expected_size = data_offset ? (data_offset > unsigned(size_max) ? unsigned(size_max) : data_offset) : unsigned(size_min);
return expected_size;
}
void Vgm_Core::header_t::cleanup()
{
unsigned int version = get_le32( this->version );
if ( size() < size_max ) memset( ((byte*)this) + size(), 0, size_max - size() );
if ( version < 0x161 )
{
memset( this->gbdmg_rate, 0, size_max - offsetof(header_t, gbdmg_rate) );
}
if ( version < 0x160 )
{
volume_modifier = 0;
reserved = 0;
loop_base = 0;
}
if ( version < 0x151 ) memset( this->rf5c68_rate, 0, size_max - size_min );
if ( version < 0x150 )
{
set_le32( data_offset, size_min - offsetof(header_t, data_offset) );
sn76489_flags = 0;
set_le32( segapcm_rate, 0 );
set_le32( segapcm_reg, 0 );
}
if ( version < 0x110 )
{
set_le16( noise_feedback, 0 );
noise_width = 0;
unsigned int rate = get_le32( ym2413_rate );
set_le32( ym2612_rate, rate );
set_le32( ym2151_rate, rate );
}
if ( version < 0x101 )
{
set_le32( frame_rate, 0 );
}
}
blargg_err_t Vgm_Core::load_mem_( byte const data [], int size )
{
assert( offsetof (header_t, rf5c68_rate) == header_t::size_min );
assert( offsetof (header_t, extra_offset[4]) == header_t::size_max );
if ( size <= header_t::size_min )
return blargg_err_file_type;
memcpy( &_header, data, header_t::size_min );
header_t const& h = header();
if ( !h.valid_tag() )
return blargg_err_file_type;
int version = get_le32( h.version );
check( version < 0x100 );
if ( version > 0x150 )
{
if ( size < header().size() )
return "Invalid header";
memcpy( &_header.rf5c68_rate, data + offsetof (header_t, rf5c68_rate), header().size() - header_t::size_min );
}
_header.cleanup();
// Get loop
loop_begin = file_end();
if ( get_le32( h.loop_offset ) )
loop_begin = &data [get_le32( h.loop_offset ) + offsetof (header_t,loop_offset)];
// PSG rate
int psg_rate = get_le32( h.psg_rate ) & 0x3FFFFFFF;
if ( !psg_rate )
psg_rate = 3579545;
stereo_buf[0].clock_rate( psg_rate );
int ay_rate = get_le32( h.ay8910_rate ) & 0xBFFFFFFF;
if ( !ay_rate )
ay_rate = 2000000;
stereo_buf[1].clock_rate( ay_rate * 2 );
ay[0].set_type( (Ay_Apu::Ay_Apu_Type) header().ay8910_type );
ay[1].set_type( (Ay_Apu::Ay_Apu_Type) header().ay8910_type );
int huc6280_rate = get_le32( h.huc6280_rate ) & 0xBFFFFFFF;
if ( !huc6280_rate )
huc6280_rate = 3579545;
stereo_buf[2].clock_rate( huc6280_rate * 2 );
int gbdmg_rate = get_le32( h.gbdmg_rate ) & 0xBFFFFFFF;
if ( !gbdmg_rate )
gbdmg_rate = Gb_Apu::clock_rate;
stereo_buf[3].clock_rate( gbdmg_rate );
const int gbdmg_hacks = 3;
gbdmg[0].set_hacks( gbdmg_hacks );
gbdmg[1].set_hacks( gbdmg_hacks );
// Disable FM
fm_rate = 0;
ymz280b.enable( false );
ymf262[0].enable( false );
ymf262[1].enable( false );
ym3812[0].enable( false );
ym3812[1].enable( false );
ym2612[0].enable( false );
ym2612[1].enable( false );
ym2610[0].enable( false );
ym2610[1].enable( false );
ym2608[0].enable( false );
ym2608[1].enable( false );
ym2413[0].enable( false );
ym2413[1].enable( false );
ym2203[0].enable( false );
ym2203[1].enable( false );
ym2151[0].enable( false );
ym2151[1].enable( false );
c140.enable( false );
segapcm.enable( false );
rf5c68.enable( false );
rf5c164.enable( false );
pwm.enable( false );
okim6258[0].enable( false );
okim6258[1].enable( false );
okim6295[0].enable( false );
okim6295[1].enable( false );
k051649.enable( false );
k053260.enable( false );
k054539.enable( false );
qsound[0].enable( false );
qsound[1].enable( false );
set_tempo( 1 );
return blargg_ok;
}
// Update pre-1.10 header FM rates by scanning commands
void Vgm_Core::update_fm_rates( int* ym2151_rate, int* ym2413_rate, int* ym2612_rate ) const
{
byte const* p = file_begin() + header().size();
int data_offset = get_le32( header().data_offset );
check( data_offset );
if ( data_offset )
p += data_offset + offsetof( header_t, data_offset ) - header().size();
while ( p < file_end() )
{
switch ( *p )
{
case cmd_end:
return;
case cmd_psg:
case cmd_byte_delay:
p += 2;
break;
case cmd_delay:
p += 3;
break;
case cmd_data_block:
p += 7 + get_le32( p + 3 );
break;
case cmd_ram_block:
p += 12;
break;
case cmd_ym2413:
*ym2151_rate = 0;
*ym2612_rate = 0;
return;
case cmd_ym2612_port0:
case cmd_ym2612_port1:
*ym2612_rate = *ym2413_rate;
*ym2413_rate = 0;
*ym2151_rate = 0;
return;
case cmd_ym2151:
*ym2151_rate = *ym2413_rate;
*ym2413_rate = 0;
*ym2612_rate = 0;
return;
default:
p += command_len( *p );
}
}
}
blargg_err_t Vgm_Core::init_chips( double* rate, bool reinit )
{
int ymz280b_rate = get_le32( header().ymz280b_rate ) & 0xBFFFFFFF;
int ymf262_rate = get_le32( header().ymf262_rate ) & 0xBFFFFFFF;
int ym3812_rate = get_le32( header().ym3812_rate ) & 0xBFFFFFFF;
int ym2612_rate = get_le32( header().ym2612_rate ) & 0xBFFFFFFF;
int ym2610_rate = get_le32( header().ym2610_rate ) & 0x3FFFFFFF;
int ym2608_rate = get_le32( header().ym2608_rate ) & 0x3FFFFFFF;
int ym2413_rate = get_le32( header().ym2413_rate ) & 0xBFFFFFFF;
int ym2203_rate = get_le32( header().ym2203_rate ) & 0xBFFFFFFF;
int ym2151_rate = get_le32( header().ym2151_rate ) & 0xBFFFFFFF;
int c140_rate = get_le32( header().c140_rate ) & 0xBFFFFFFF;
int segapcm_rate = get_le32( header().segapcm_rate ) & 0xBFFFFFFF;
int rf5c68_rate = get_le32( header().rf5c68_rate ) & 0xBFFFFFFF;
int rf5c164_rate = get_le32( header().rf5c164_rate ) & 0xBFFFFFFF;
int pwm_rate = get_le32( header().pwm_rate ) & 0xBFFFFFFF;
int okim6258_rate = get_le32( header().okim6258_rate ) & 0xBFFFFFFF;
int okim6295_rate = get_le32( header().okim6295_rate ) & 0xBFFFFFFF;
int k051649_rate = get_le32( header().k051649_rate ) & 0xBFFFFFFF;
int k053260_rate = get_le32( header().k053260_rate ) & 0xBFFFFFFF;
int k054539_rate = get_le32( header().k054539_rate ) & 0xBFFFFFFF;
int qsound_rate = get_le32( header().qsound_rate ) & 0xBFFFFFFF;
if ( ym2413_rate && get_le32( header().version ) < 0x110 )
update_fm_rates( &ym2151_rate, &ym2413_rate, &ym2612_rate );
*rate = vgm_rate;
if ( ymf262_rate )
{
bool dual_chip = !!(header().ymf262_rate[3] & 0x40);
double gain = dual_chip ? 0.5 : 1.0;
double fm_rate = ymf262_rate / 288.0;
int result;
if ( !reinit )
{
result = ymf262[0].set_rate( fm_rate, ymf262_rate );
CHECK_ALLOC( !result );
}
RETURN_ERR( ymf262[0].setup( fm_rate / vgm_rate, 0.85, gain ) );
ymf262[0].enable();
if ( dual_chip )
{
if ( !reinit )
{
result = ymf262[1].set_rate( fm_rate, ymf262_rate );
CHECK_ALLOC( !result );
}
RETURN_ERR( ymf262[1].setup( fm_rate / vgm_rate, 0.85, gain ) );
ymf262[1].enable();
}
}
if ( ym3812_rate )
{
bool dual_chip = !!(header().ym3812_rate[3] & 0x40);
double gain = dual_chip ? 0.5 : 1.0;
double fm_rate = ym3812_rate / 72.0;
int result;
if ( !reinit )
{
result = ym3812[0].set_rate( fm_rate, ym3812_rate );
CHECK_ALLOC( !result );
}
RETURN_ERR( ym3812[0].setup( fm_rate / vgm_rate, 0.85, gain ) );
ym3812[0].enable();
if ( dual_chip )
{
if ( !reinit )
{
result = ym3812[1].set_rate( fm_rate, ym3812_rate );
CHECK_ALLOC( !result );
}
RETURN_ERR( ym3812[1].setup( fm_rate / vgm_rate, 0.85, gain ) );
ym3812[1].enable();
}
}
if ( ym2612_rate )
{
bool dual_chip = !!(header().ym2612_rate[3] & 0x40);
double gain = dual_chip ? 0.5 : 1.0;
double fm_rate = ym2612_rate / 144.0;
if ( !reinit )
{
RETURN_ERR( ym2612[0].set_rate( fm_rate, ym2612_rate ) );
}
RETURN_ERR( ym2612[0].setup( fm_rate / vgm_rate, 0.85, gain ) );
ym2612[0].enable();
if ( dual_chip )
{
if ( !reinit )
{
RETURN_ERR( ym2612[1].set_rate( fm_rate, ym2612_rate ) );
}
RETURN_ERR( ym2612[1].setup( fm_rate / vgm_rate, 0.85, gain ) );
ym2612[1].enable();
}
}
if ( ym2610_rate )
{
bool dual_chip = !!(header().ym2610_rate[3] & 0x40);
bool is_2610b = !!(header().ym2610_rate[3] & 0x80);
double gain = dual_chip ? 0.5 : 1.0;
double fm_rate = ym2610_rate / 72.0;
int result;
if ( !reinit )
{
result = ym2610[0].set_rate( fm_rate, ym2610_rate, is_2610b );
CHECK_ALLOC( !result );
}
RETURN_ERR( ym2610[0].setup( fm_rate / vgm_rate, 0.85, gain ) );
ym2610[0].enable();
if ( dual_chip )
{
if ( !reinit )
{
result = ym2610[1].set_rate( fm_rate, ym2610_rate, is_2610b );
CHECK_ALLOC( !result );
}
RETURN_ERR( ym2610[1].setup( fm_rate / vgm_rate, 0.85, gain ) );
ym2610[1].enable();
}
}
if ( ym2608_rate )
{
bool dual_chip = !!(header().ym2610_rate[3] & 0x40);
double gain = dual_chip ? 1.0 : 2.0;
double fm_rate = ym2608_rate / 72.0;
int result;
if ( !reinit )
{
result = ym2608[0].set_rate( fm_rate, ym2608_rate );
CHECK_ALLOC( !result );
}
RETURN_ERR( ym2608[0].setup( fm_rate / vgm_rate, 0.85, gain ) );
ym2608[0].enable();
if ( dual_chip )
{
if ( !reinit )
{
result = ym2608[1].set_rate( fm_rate, ym2608_rate );
CHECK_ALLOC( !result );
}
RETURN_ERR( ym2608[1].setup( fm_rate / vgm_rate, 0.85, gain ) );
ym2608[1].enable();
}
}
if ( ym2413_rate )
{
bool dual_chip = !!(header().ym2413_rate[3] & 0x40);
double gain = dual_chip ? 0.5 : 1.0;
double fm_rate = ym2413_rate / 72.0;
int result;
if ( !reinit )
{
result = ym2413[0].set_rate( fm_rate, ym2413_rate );
if ( result == 2 )
return "YM2413 FM sound not supported";
CHECK_ALLOC( !result );
}
RETURN_ERR( ym2413[0].setup( fm_rate / vgm_rate, 0.85, gain ) );
ym2413[0].enable();
if ( dual_chip )
{
if ( !reinit )
{
result = ym2413[1].set_rate( fm_rate, ym2413_rate );
CHECK_ALLOC( !result );
}
RETURN_ERR( ym2413[1].setup( fm_rate / vgm_rate, 0.85, gain ) );
ym2413[1].enable();
}
}
if ( ym2151_rate )
{
bool dual_chip = !!(header().ym2151_rate[3] & 0x40);
double gain = dual_chip ? 0.5 : 1.0;
double fm_rate = ym2151_rate / 64.0;
int result;
if ( !reinit )
{
result = ym2151[0].set_rate( fm_rate, ym2151_rate );
CHECK_ALLOC( !result );
}
RETURN_ERR( ym2151[0].setup( fm_rate / vgm_rate, 0.85, gain ) );
ym2151[0].enable();
if ( dual_chip )
{
if ( !reinit )
{
result = ym2151[1].set_rate( fm_rate, ym2151_rate );
CHECK_ALLOC( !result );
}
RETURN_ERR( ym2151[1].setup( fm_rate / vgm_rate, 0.85, gain ) );
ym2151[1].enable();
}
}
if ( ym2203_rate )
{
bool dual_chip = !!(header().ym2203_rate[3] & 0x40);
double gain = dual_chip ? 0.5 : 1.0;
double fm_rate = ym2203_rate / 72.0;
int result;
if ( !reinit )
{
result = ym2203[0].set_rate( fm_rate, ym2203_rate );
CHECK_ALLOC ( !result );
}
RETURN_ERR( ym2203[0].setup( fm_rate / vgm_rate, 0.85, gain ) );
ym2203[0].enable();
if ( dual_chip )
{
if ( !reinit )
{
result = ym2203[1].set_rate( fm_rate, ym2203_rate );
CHECK_ALLOC ( !result );
}
RETURN_ERR( ym2203[1].setup( fm_rate / vgm_rate, 0.85, gain ) );
ym2203[1].enable();
}
}
if ( segapcm_rate )
{
double pcm_rate = segapcm_rate / 128.0;
if ( !reinit )
{
int result = segapcm.set_rate( get_le32( header().segapcm_reg ) );
CHECK_ALLOC( !result );
}
RETURN_ERR( segapcm.setup( pcm_rate / vgm_rate, 0.85, 1.5 ) );
segapcm.enable();
}
if ( rf5c68_rate )
{
double pcm_rate = rf5c68_rate / 384.0;
if ( !reinit )
{
int result = rf5c68.set_rate();
CHECK_ALLOC( !result );
}
RETURN_ERR( rf5c68.setup( pcm_rate / vgm_rate, 0.85, 0.6875 ) );
rf5c68.enable();
}
if ( rf5c164_rate )
{
double pcm_rate = rf5c164_rate / 384.0;
if ( !reinit )
{
int result = rf5c164.set_rate( rf5c164_rate );
CHECK_ALLOC( !result );
}
RETURN_ERR( rf5c164.setup( pcm_rate / vgm_rate, 0.85, 0.5 ) );
rf5c164.enable();
}
if ( pwm_rate )
{
double pcm_rate = 22020.0;
if ( !reinit )
{
int result = pwm.set_rate( pwm_rate );
CHECK_ALLOC( !result );
}
RETURN_ERR( pwm.setup( pcm_rate / vgm_rate, 0.85, 0.875 ) );
pwm.enable();
}
if ( okim6258_rate )
{
bool dual_chip = !!( header().okim6258_rate[3] & 0x40 );
if ( !reinit )
{
okim6258_hz[0] = okim6258[0].set_rate( okim6258_rate, header().okim6258_flags & 0x03, ( header().okim6258_flags & 0x04 ) >> 2, ( header().okim6258_flags & 0x08 ) >> 3 );
CHECK_ALLOC( okim6258_hz[0] );
}
RETURN_ERR( okim6258[0].setup( (double)okim6258_hz[0] / vgm_rate, 0.85, 1.0 ) );
okim6258[0].enable();
if ( dual_chip )
{
if ( !reinit )
{
okim6258_hz[1] = okim6258[1].set_rate( okim6258_rate, header().okim6258_flags & 0x03, ( header().okim6258_flags & 0x04 ) >> 2, ( header().okim6258_flags & 0x08 ) >> 3 );
CHECK_ALLOC( okim6258_hz[1] );
}
RETURN_ERR( okim6258[1].setup( (double)okim6258_hz[1] / vgm_rate, 0.85, 1.0 ) );
okim6258[1].enable();
}
}
if ( okim6295_rate )
{
// moo
Mem_File_Reader rdr( file_begin(), file_size() );
Music_Emu * vgm = gme_vgm_type->new_info();
track_info_t info;
vgm->load( rdr );
vgm->track_info( &info, 0 );
delete vgm;
bool is_cp_system = strncmp( info.system, "CP", 2 ) == 0;
bool dual_chip = !!( header().okim6295_rate[3] & 0x40 );
double gain = is_cp_system ? 0.4296875 : 1.0;
if ( dual_chip ) gain *= 0.5;
if ( !reinit )
{
okim6295_hz = okim6295[0].set_rate( okim6295_rate );
CHECK_ALLOC( okim6295_hz );
}
RETURN_ERR( okim6295[0].setup( (double)okim6295_hz / vgm_rate, 0.85, gain ) );
okim6295[0].enable();
if ( dual_chip )
{
if ( !reinit )
{
int result = okim6295[1].set_rate( okim6295_rate );
CHECK_ALLOC( result );
}
RETURN_ERR( okim6295[1].setup( (double)okim6295_hz / vgm_rate, 0.85, gain ) );
okim6295[1].enable();
}
}
if ( c140_rate )
{
double pcm_rate = c140_rate;
if ( !reinit )
{
int result = c140.set_rate( header().c140_type, c140_rate, c140_rate );
CHECK_ALLOC( !result );
}
RETURN_ERR( c140.setup( pcm_rate / vgm_rate, 0.85, 1.0 ) );
c140.enable();
}
if ( k051649_rate )
{
double pcm_rate = k051649_rate / 16.0;
if ( !reinit )
{
int result = k051649.set_rate( k051649_rate );
CHECK_ALLOC( !result );
}
RETURN_ERR( k051649.setup( pcm_rate / vgm_rate, 0.85, 1.0 ) );
k051649.enable();
}
if ( k053260_rate )
{
double pcm_rate = k053260_rate / 32.0;
if ( !reinit )
{
int result = k053260.set_rate( k053260_rate );
CHECK_ALLOC( !result );
}
RETURN_ERR( k053260.setup( pcm_rate / vgm_rate, 0.85, 1.0 ) );
k053260.enable();
}
if ( k054539_rate )
{
double pcm_rate = k054539_rate;
if ( !reinit )
{
int result = k054539.set_rate( k054539_rate, header().k054539_flags );
CHECK_ALLOC( !result );
}
RETURN_ERR( k054539.setup( pcm_rate / vgm_rate, 0.85, 1.0 ) );
k054539.enable();
}
if ( ymz280b_rate )
{
if ( !reinit )
{
ymz280b_hz = ymz280b.set_rate( ymz280b_rate );
CHECK_ALLOC( ymz280b_hz );
}
RETURN_ERR( ymz280b.setup( (double)ymz280b_hz / vgm_rate, 0.85, 0.59375 ) );
ymz280b.enable();
}
if ( qsound_rate )
{
/*double pcm_rate = (double)qsound_rate / 166.0;*/
if ( !reinit )
{
int result = qsound[0].set_rate( qsound_rate );
CHECK_ALLOC( result );
}
qsound[0].set_sample_rate( vgm_rate );
RETURN_ERR( qsound[0].setup( 1.0, 0.85, 1.0 ) );
qsound[0].enable();
}
fm_rate = *rate;
return blargg_ok;
}
void Vgm_Core::start_track()
{
psg[0].reset( get_le16( header().noise_feedback ), header().noise_width );
psg[1].reset( get_le16( header().noise_feedback ), header().noise_width );
ay[0].reset();
ay[1].reset();
huc6280[0].reset();
huc6280[1].reset();
gbdmg[0].reset();
gbdmg[1].reset();
blip_buf[0] = stereo_buf[0].center();
blip_buf[1] = blip_buf[0];
dac_disabled[0] = -1;
dac_disabled[1] = -1;
pos = file_begin() + header().size();
dac_amp[0] = -1;
dac_amp[1] = -1;
vgm_time = 0;
int data_offset = get_le32( header().data_offset );
check( data_offset );
if ( data_offset )
pos += data_offset + offsetof (header_t,data_offset) - header().size();
pcm_pos = pos;
if ( uses_fm() )
{
if ( rf5c68.enabled() )
rf5c68.reset();
if ( rf5c164.enabled() )
rf5c164.reset();
if ( segapcm.enabled() )
segapcm.reset();
if ( pwm.enabled() )
pwm.reset();
if ( okim6258[0].enabled() )
okim6258[0].reset();
if ( okim6258[1].enabled() )
okim6258[1].reset();
if ( okim6295[0].enabled() )
okim6295[0].reset();
if ( okim6295[1].enabled() )
okim6295[1].reset();
if ( k051649.enabled() )
k051649.reset();
if ( k053260.enabled() )
k053260.reset();
if ( k054539.enabled() )
k054539.reset();
if ( c140.enabled() )
c140.reset();
if ( ym2151[0].enabled() )
ym2151[0].reset();
if ( ym2151[1].enabled() )
ym2151[1].reset();
if ( ym2203[0].enabled() )
ym2203[0].reset();
if ( ym2203[1].enabled() )
ym2203[1].reset();
if ( ym2413[0].enabled() )
ym2413[0].reset();
if ( ym2413[1].enabled() )
ym2413[1].reset();
if ( ym2612[0].enabled() )
ym2612[0].reset();
if ( ym2612[1].enabled() )
ym2612[1].reset();
if ( ym2610[0].enabled() )
ym2610[0].reset();
if ( ym2610[1].enabled() )
ym2610[1].reset();
if ( ym2608[0].enabled() )
ym2608[0].reset();
if ( ym2608[1].enabled() )
ym2608[0].reset();
if ( ym3812[0].enabled() )
ym3812[0].reset();
if ( ym3812[1].enabled() )
ym3812[1].reset();
if ( ymf262[0].enabled() )
ymf262[0].reset();
if ( ymf262[1].enabled() )
ymf262[1].reset();
if ( ymz280b.enabled() )
ymz280b.reset();
if ( qsound[0].enabled() )
qsound[0].reset();
if ( qsound[1].enabled() )
qsound[1].reset();
stereo_buf[0].clear();
stereo_buf[1].clear();
stereo_buf[2].clear();
stereo_buf[3].clear();
}
for ( unsigned i = 0; i < DacCtrlUsed; i++ )
{
device_reset_daccontrol( dac_control [i] );
DacCtrlTime[DacCtrlMap[i]] = 0;
}
for ( unsigned i = 0; i < PCM_BANK_COUNT; i++)
{
// reset PCM Bank, but not the data
// (this way I don't need to decompress the data again when restarting)
PCMBank [i].DataPos = 0;
PCMBank [i].BnkPos = 0;
}
PCMTbl.EntryCount = 0;
fm_time_offset = 0;
ay_time_offset = 0;
huc6280_time_offset = 0;
gbdmg_time_offset = 0;
dac_control_recursion = 0;
}
inline Vgm_Core::fm_time_t Vgm_Core::to_fm_time( vgm_time_t t ) const
{
return (t * fm_time_factor + fm_time_offset) >> fm_time_bits;
}
inline blip_time_t Vgm_Core::to_psg_time( vgm_time_t t ) const
{
return (t * blip_time_factor) >> blip_time_bits;
}
inline blip_time_t Vgm_Core::to_ay_time( vgm_time_t t ) const
{
return (t * blip_ay_time_factor) >> blip_time_bits;
}
inline blip_time_t Vgm_Core::to_huc6280_time( vgm_time_t t ) const
{
return (t * blip_huc6280_time_factor) >> blip_time_bits;
}
inline blip_time_t Vgm_Core::to_gbdmg_time( vgm_time_t t ) const
{
return (t * blip_gbdmg_time_factor) >> blip_time_bits;
}
void Vgm_Core::write_pcm( vgm_time_t vgm_time, int chip, int amp )
{
chip = !!chip;
if ( blip_buf[chip] )
{
check( amp >= 0 );
blip_time_t blip_time = to_psg_time( vgm_time );
int old = dac_amp[chip];
int delta = amp - old;
dac_amp[chip] = amp;
blip_buf[chip]->set_modified();
if ( old >= 0 ) // first write is ignored, to avoid click
pcm.offset_inline( blip_time, delta, blip_buf[chip] );
else
dac_amp[chip] |= dac_disabled[chip];
}
}
blip_time_t Vgm_Core::run( vgm_time_t end_time )
{
vgm_time_t vgm_time = this->vgm_time;
vgm_time_t vgm_loop_time = ~0;
int ChipID;
byte const* pos = this->pos;
if ( pos > file_end() )
set_warning( "Stream lacked end event" );
while ( vgm_time < end_time && pos < file_end() )
{
// TODO: be sure there are enough bytes left in stream for particular command
// so we don't read past end
switch ( *pos++ )
{
case cmd_end:
if ( vgm_loop_time == ~0 ) vgm_loop_time = vgm_time;
else if ( vgm_loop_time == vgm_time ) loop_begin = file_end(); // XXX some files may loop forever on a region without any delay commands
pos = loop_begin; // if not looped, loop_begin == file_end()
if ( pos != file_end() ) has_looped = true;
break;
case cmd_delay_735:
vgm_time += 735;
break;
case cmd_delay_882:
vgm_time += 882;
break;
case cmd_gg_stereo:
psg[0].write_ggstereo( to_psg_time( vgm_time ), *pos++ );
break;
case cmd_gg_stereo_2:
psg[1].write_ggstereo( to_psg_time( vgm_time ), *pos++ );
break;
case cmd_psg:
psg[0].write_data( to_psg_time( vgm_time ), *pos++ );
break;
case cmd_psg_2:
psg[1].write_data( to_psg_time( vgm_time ), *pos++ );
break;
case cmd_ay8910:
ChipID = !!(pos [0] & 0x80);
chip_reg_write( vgm_time, 0x12, ChipID, 0x00, pos [0] & 0x7F, pos [1] );
pos += 2;
break;
case cmd_delay:
vgm_time += pos [1] * 0x100 + pos [0];
pos += 2;
break;
case cmd_byte_delay:
vgm_time += *pos++;
break;
case cmd_segapcm_write:
if ( get_le32( header().segapcm_rate ) > 0 )
if ( run_segapcm( to_fm_time( vgm_time ) ) )
segapcm.write( get_le16( pos ), pos [2] );
pos += 3;
break;
case cmd_rf5c68:
if ( run_rf5c68( to_fm_time( vgm_time ) ) )
rf5c68.write( pos [0], pos [1] );
pos += 2;
break;
case cmd_rf5c68_mem:
if ( run_rf5c68( to_fm_time( vgm_time ) ) )
rf5c68.write_mem( get_le16( pos ), pos [2] );
pos += 3;
break;
case cmd_rf5c164:
if ( run_rf5c164( to_fm_time( vgm_time ) ) )
rf5c164.write( pos [0], pos [1] );
pos += 2;
break;
case cmd_rf5c164_mem:
if ( run_rf5c164( to_fm_time( vgm_time ) ) )
rf5c164.write_mem( get_le16( pos ), pos [2] );
pos += 3;
break;
case cmd_pwm:
chip_reg_write( vgm_time, 0x11, 0x00, pos [0] >> 4, pos [0] & 0x0F, pos [1] );
pos += 2;
break;
case cmd_c140:
if ( get_le32( header().c140_rate ) > 0 )
if ( run_c140( to_fm_time( vgm_time ) ) )
c140.write( get_be16( pos ), pos [2] );
pos += 3;
break;
case cmd_ym2151:
chip_reg_write( vgm_time, 0x03, 0x00, 0x00, pos [0], pos [1] );
pos += 2;
break;
case cmd_ym2151_2:
chip_reg_write( vgm_time, 0x03, 0x01, 0x00, pos [0], pos [1] );
pos += 2;
break;
case cmd_ym2203:
chip_reg_write( vgm_time, 0x06, 0x00, 0x00, pos [0], pos [1] );
pos += 2;
break;
case cmd_ym2203_2:
chip_reg_write( vgm_time, 0x06, 0x01, 0x00, pos [0], pos [1] );
pos += 2;
break;
case cmd_ym2413:
chip_reg_write( vgm_time, 0x01, 0x00, 0x00, pos [0], pos [1] );
pos += 2;
break;
case cmd_ym2413_2:
chip_reg_write( vgm_time, 0x01, 0x01, 0x00, pos [0], pos [1] );
pos += 2;
break;
case cmd_ym3812:
chip_reg_write( vgm_time, 0x09, 0x00, 0x00, pos [0], pos [1] );
pos += 2;
break;
case cmd_ym3812_2:
chip_reg_write( vgm_time, 0x09, 0x01, 0x00, pos [0], pos [1] );
pos += 2;
break;
case cmd_ymf262_port0:
chip_reg_write( vgm_time, 0x0C, 0x00, 0x00, pos [0], pos [1] );
pos += 2;
break;
case cmd_ymf262_2_port0:
chip_reg_write( vgm_time, 0x0C, 0x01, 0x00, pos [0], pos [1] );
pos += 2;
break;
case cmd_ymf262_port1:
chip_reg_write( vgm_time, 0x0C, 0x00, 0x01, pos [0], pos [1] );
pos += 2;
break;
case cmd_ymf262_2_port1:
chip_reg_write( vgm_time, 0x0C, 0x01, 0x01, pos [0], pos [1] );
pos += 2;
break;
case cmd_ymz280b:
chip_reg_write( vgm_time, 0x0F, 0x00, 0x00, pos [0], pos [1] );
pos += 2;
break;
case cmd_ym2612_port0:
chip_reg_write( vgm_time, 0x02, 0x00, 0x00, pos [0], pos [1] );
pos += 2;
break;
case cmd_ym2612_2_port0:
chip_reg_write( vgm_time, 0x02, 0x01, 0x00, pos [0], pos [1] );
pos += 2;
break;
case cmd_ym2612_port1:
chip_reg_write( vgm_time, 0x02, 0x00, 0x01, pos [0], pos [1] );
pos += 2;
break;
case cmd_ym2612_2_port1:
chip_reg_write( vgm_time, 0x02, 0x01, 0x01, pos [0], pos [1] );
pos += 2;
break;
case cmd_ym2610_port0:
chip_reg_write( vgm_time, 0x08, 0x00, 0x00, pos [0], pos [1] );
pos += 2;
break;
case cmd_ym2610_2_port0:
chip_reg_write( vgm_time, 0x08, 0x01, 0x00, pos [0], pos [1] );
pos += 2;
break;
case cmd_ym2610_port1:
chip_reg_write( vgm_time, 0x08, 0x00, 0x01, pos [0], pos [1] );
pos += 2;
break;
case cmd_ym2610_2_port1:
chip_reg_write( vgm_time, 0x08, 0x01, 0x01, pos [0], pos [1] );
pos += 2;
break;
case cmd_ym2608_port0:
chip_reg_write( vgm_time, 0x07, 0x00, 0x00, pos [0], pos [1] );
pos += 2;
break;
case cmd_ym2608_2_port0:
chip_reg_write( vgm_time, 0x07, 0x01, 0x00, pos [0], pos [1] );
pos += 2;
break;
case cmd_ym2608_port1:
chip_reg_write( vgm_time, 0x07, 0x00, 0x01, pos [0], pos [1] );
pos += 2;
break;
case cmd_ym2608_2_port1:
chip_reg_write( vgm_time, 0x07, 0x01, 0x01, pos [0], pos [1] );
pos += 2;
break;
case cmd_okim6258_write:
chip_reg_write( vgm_time, 0x17, ChipID, 0x00, pos [0] & 0x7F, pos [1] );
pos += 2;
break;
case cmd_okim6295_write:
ChipID = (pos [0] & 0x80) ? 1 : 0;
chip_reg_write( vgm_time, 0x18, ChipID, 0x00, pos [0] & 0x7F, pos [1] );
pos += 2;
break;
case cmd_huc6280_write:
ChipID = (pos [0] & 0x80) ? 1 : 0;
chip_reg_write( vgm_time, 0x1B, ChipID, 0x00, pos [0] & 0x7F, pos [1] );
pos += 2;
break;
case cmd_gbdmg_write:
ChipID = (pos [0] & 0x80) ? 1 : 0;
chip_reg_write( vgm_time, 0x13, ChipID, 0x00, pos [0] & 0x7F, pos [1] );
pos += 2;
break;
case cmd_k051649_write:
chip_reg_write( vgm_time, 0x19, 0x00, pos [0] & 0x7F, pos [1], pos [2] );
pos += 3;
break;
case cmd_k053260_write:
chip_reg_write( vgm_time, 0x1D, 0x00, 0x00, pos [0] & 0x7F, pos [1] );
pos += 2;
break;
case cmd_k054539_write:
chip_reg_write( vgm_time, 0x1A, 0x00, pos [0] & 0x7F, pos [1], pos [2] );
pos += 3;
break;
case cmd_qsound_write:
chip_reg_write( vgm_time, 0x1F, 0x00, pos [0], pos [1], pos [2] );
pos += 3;
break;
case cmd_dacctl_setup:
if ( run_dac_control( vgm_time ) )
{
unsigned chip = pos [0];
if ( chip < 0xFF )
{
if ( ! DacCtrl [chip].Enable )
{
dac_control_grow( chip );
DacCtrl [chip].Enable = true;
}
daccontrol_setup_chip( dac_control [DacCtrlMap [chip]], pos [1] & 0x7F, ( pos [1] & 0x80 ) >> 7, get_be16( pos + 2 ) );
}
}
pos += 4;
break;
case cmd_dacctl_data:
if ( run_dac_control( vgm_time ) )
{
unsigned chip = pos [0];
if ( chip < 0xFF && DacCtrl [chip].Enable )
{
DacCtrl [chip].Bank = pos [1];
if ( DacCtrl [chip].Bank >= 0x40 )
DacCtrl [chip].Bank = 0x00;
VGM_PCM_BANK * TempPCM = &PCMBank [DacCtrl [chip].Bank];
daccontrol_set_data( dac_control [DacCtrlMap [chip]], TempPCM->Data, TempPCM->DataSize, pos [2], pos [3] );
}
}
pos += 4;
break;
case cmd_dacctl_freq:
if ( run_dac_control( vgm_time ) )
{
unsigned chip = pos [0];
if ( chip < 0xFF && DacCtrl [chip].Enable )
{
daccontrol_set_frequency( dac_control [DacCtrlMap [chip]], get_le32( pos + 1 ) );
}
}
pos += 5;
break;
case cmd_dacctl_play:
if ( run_dac_control( vgm_time ) )
{
unsigned chip = pos [0];
if ( chip < 0xFF && DacCtrl [chip].Enable && PCMBank [DacCtrl [chip].Bank].BankCount )
{
daccontrol_start( dac_control [DacCtrlMap [chip]], get_le32( pos + 1 ), pos [5], get_le32( pos + 6 ) );
}
}
pos += 10;
break;
case cmd_dacctl_stop:
if ( run_dac_control( vgm_time ) )
{
unsigned chip = pos [0];
if ( chip < 0xFF && DacCtrl [chip].Enable )
{
daccontrol_stop( dac_control [DacCtrlMap [chip]] );
}
else if ( chip == 0xFF )
{
for ( unsigned i = 0; i < DacCtrlUsed; i++ )
{
daccontrol_stop( dac_control [i] );
}
}
}
pos++;
break;
case cmd_dacctl_playblock:
if ( run_dac_control( vgm_time ) )
{
unsigned chip = pos [0];
if ( chip < 0xFF && DacCtrl [chip].Enable && PCMBank [DacCtrl [chip].Bank].BankCount )
{
VGM_PCM_BANK * TempPCM = &PCMBank [DacCtrl [chip].Bank];
unsigned block_number = get_le16( pos + 1 );
if ( block_number >= TempPCM->BankCount )
block_number = 0;
VGM_PCM_DATA * TempBnk = &TempPCM->Bank [block_number];
unsigned flags = DCTRL_LMODE_BYTES | ((pos [4] & 1) << 7);
daccontrol_start( dac_control [DacCtrlMap [chip]], TempBnk->DataStart, flags, TempBnk->DataSize );
}
}
pos += 4;
break;
case cmd_data_block: {
check( *pos == cmd_end );
int type = pos [1];
int size = get_le32( pos + 2 );
int chipid = 0;
if ( size & 0x80000000 )
{
size &= 0x7FFFFFFF;
chipid = 1;
}
pos += 6;
switch ( type & 0xC0 )
{
case pcm_block_type:
case pcm_aux_block_type:
AddPCMData( type, size, pos );
break;
case rom_block_type:
if ( size >= 8 )
{
int rom_size = get_le32( pos );
int data_start = get_le32( pos + 4 );
int data_size = size - 8;
void * rom_data = ( void * ) ( pos + 8 );
switch ( type )
{
case rom_segapcm:
if ( segapcm.enabled() )
segapcm.write_rom( rom_size, data_start, data_size, rom_data );
break;
case rom_ym2608_deltat:
if ( ym2608[chipid].enabled() )
{
ym2608[chipid].write_rom( 0x02, rom_size, data_start, data_size, rom_data );
}
break;
case rom_ym2610_adpcm:
case rom_ym2610_deltat:
if ( ym2610[chipid].enabled() )
{
int rom_id = 0x01 + ( type - rom_ym2610_adpcm );
ym2610[chipid].write_rom( rom_id, rom_size, data_start, data_size, rom_data );
}
break;
case rom_ymz280b:
if ( ymz280b.enabled() )
ymz280b.write_rom( rom_size, data_start, data_size, rom_data );
break;
case rom_okim6295:
if ( okim6295[chipid].enabled() )
okim6295[chipid].write_rom( rom_size, data_start, data_size, rom_data );
break;
case rom_k054539:
if ( k054539.enabled() )
k054539.write_rom( rom_size, data_start, data_size, rom_data );
break;
case rom_c140:
if ( c140.enabled() )
c140.write_rom( rom_size, data_start, data_size, rom_data );
break;
case rom_k053260:
if ( k053260.enabled() )
k053260.write_rom( rom_size, data_start, data_size, rom_data );
break;
case rom_qsound:
if ( qsound[chipid].enabled() )
qsound[chipid].write_rom( rom_size, data_start, data_size, rom_data );
break;
}
}
break;
case ram_block_type:
if ( size >= 2 )
{
int data_start = get_le16( pos );
int data_size = size - 2;
void * ram_data = ( void * ) ( pos + 2 );
switch ( type )
{
case ram_rf5c68:
if ( rf5c68.enabled() )
rf5c68.write_ram( data_start, data_size, ram_data );
break;
case ram_rf5c164:
if ( rf5c164.enabled() )
rf5c164.write_ram( data_start, data_size, ram_data );
break;
}
}
break;
}
pos += size;
break;
}
case cmd_ram_block: {
check( *pos == cmd_end );
int type = pos[ 1 ];
int data_start = get_le24( pos + 2 );
int data_addr = get_le24( pos + 5 );
int data_size = get_le24( pos + 8 );
if ( !data_size ) data_size += 0x01000000;
void * data_ptr = (void *) GetPointerFromPCMBank( type, data_start );
switch ( type )
{
case rf5c68_ram_block:
if ( rf5c68.enabled() )
rf5c68.write_ram( data_addr, data_size, data_ptr );
break;
case rf5c164_ram_block:
if ( rf5c164.enabled() )
rf5c164.write_ram( data_addr, data_size, data_ptr );
break;
}
pos += 11;
break;
}
case cmd_pcm_seek:
pcm_pos = GetPointerFromPCMBank( 0, get_le32( pos ) );
pos += 4;
break;
default:
int cmd = pos [-1];
switch ( cmd & 0xF0 )
{
case cmd_pcm_delay:
chip_reg_write( vgm_time, 0x02, 0x00, 0x00, ym2612_dac_port, *pcm_pos++ );
vgm_time += cmd & 0x0F;
break;
case cmd_short_delay:
vgm_time += (cmd & 0x0F) + 1;
break;
case 0x50:
pos += 2;
break;
default:
pos += command_len( cmd ) - 1;
set_warning( "Unknown stream event" );
}
}
}
vgm_time -= end_time;
this->pos = pos;
this->vgm_time = vgm_time;
return to_psg_time( end_time );
}
blip_time_t Vgm_Core::run_psg( int msec )
{
blip_time_t t = run( msec * vgm_rate / 1000 );
psg[0].end_frame( t );
psg[1].end_frame( t );
return t;
}
int Vgm_Core::play_frame( blip_time_t blip_time, int sample_count, blip_sample_t out [] )
{
// to do: timing is working mostly by luck
int min_pairs = (unsigned) sample_count / 2;
int vgm_time = (min_pairs << fm_time_bits) / fm_time_factor - 1;
assert( to_fm_time( vgm_time ) <= min_pairs );
int pairs;
while ( (pairs = to_fm_time( vgm_time )) < min_pairs )
vgm_time++;
//dprintf( "pairs: %d, min_pairs: %d\n", pairs, min_pairs );
memset( out, 0, pairs * stereo * sizeof *out );
if ( ymf262[0].enabled() )
{
ymf262[0].begin_frame( out );
if ( ymf262[1].enabled() )
{
ymf262[1].begin_frame( out );
}
}
if ( ym3812[0].enabled() )
{
ym3812[0].begin_frame( out );
if ( ym3812[1].enabled() )
{
ym3812[1].begin_frame( out );
}
}
if ( ym2612[0].enabled() )
{
ym2612[0].begin_frame( out );
if ( ym2612[1].enabled() )
{
ym2612[1].begin_frame( out );
}
}
if ( ym2610[0].enabled() )
{
ym2610[0].begin_frame( out );
if ( ym2610[1].enabled() )
{
ym2610[1].begin_frame( out );
}
}
if ( ym2608[0].enabled() )
{
ym2608[0].begin_frame( out );
if ( ym2608[1].enabled() )
{
ym2608[1].begin_frame( out );
}
}
if ( ym2413[0].enabled() )
{
ym2413[0].begin_frame( out );
if ( ym2413[1].enabled() )
{
ym2413[1].begin_frame( out );
}
}
if ( ym2203[0].enabled() )
{
ym2203[0].begin_frame( out );
if ( ym2203[1].enabled() )
{
ym2203[1].begin_frame( out );
}
}
if ( ym2151[0].enabled() )
{
ym2151[0].begin_frame( out );
if ( ym2151[1].enabled() )
{
ym2151[1].begin_frame( out );
}
}
if ( c140.enabled() )
{
c140.begin_frame( out );
}
if ( segapcm.enabled() )
{
segapcm.begin_frame( out );
}
if ( rf5c68.enabled() )
{
rf5c68.begin_frame( out );
}
if ( rf5c164.enabled() )
{
rf5c164.begin_frame( out );
}
if ( pwm.enabled() )
{
pwm.begin_frame( out );
}
if ( okim6258[0].enabled() )
{
okim6258[0].begin_frame( out );
if ( okim6258[1].enabled() )
{
okim6258[1].begin_frame( out );
}
}
if ( okim6295[0].enabled() )
{
okim6295[0].begin_frame( out );
if ( okim6295[1].enabled() )
{
okim6295[1].begin_frame( out );
}
}
if ( k051649.enabled() )
{
k051649.begin_frame( out );
}
if ( k053260.enabled() )
{
k053260.begin_frame( out );
}
if ( k054539.enabled() )
{
k054539.begin_frame( out );
}
if ( ymz280b.enabled() )
{
ymz280b.begin_frame( out );
}
if ( qsound[0].enabled() )
{
qsound[0].begin_frame( out );
if ( qsound[1].enabled() )
{
qsound[1].begin_frame( out );
}
}
run( vgm_time );
run_dac_control( vgm_time );
run_ymf262( 0, pairs ); run_ymf262( 1, pairs );
run_ym3812( 0, pairs ); run_ym3812( 1, pairs );
run_ym2612( 0, pairs ); run_ym2612( 1, pairs );
run_ym2610( 0, pairs ); run_ym2610( 1, pairs );
run_ym2608( 0, pairs ); run_ym2608( 1, pairs );
run_ym2413( 0, pairs ); run_ym2413( 1, pairs );
run_ym2203( 0, pairs ); run_ym2203( 1, pairs );
run_ym2151( 0, pairs ); run_ym2151( 1, pairs );
run_c140( pairs );
run_segapcm( pairs );
run_rf5c68( pairs );
run_rf5c164( pairs );
run_pwm( pairs );
run_okim6258( 0, pairs ); run_okim6258( 1, pairs );
run_okim6295( 0, pairs ); run_okim6295( 1, pairs );
run_k051649( pairs );
run_k053260( pairs );
run_k054539( pairs );
run_ymz280b( pairs );
run_qsound( 0, pairs ); run_qsound( 1, pairs );
fm_time_offset = (vgm_time * fm_time_factor + fm_time_offset) - (pairs << fm_time_bits);
psg[0].end_frame( blip_time );
psg[1].end_frame( blip_time );
ay_time_offset = (vgm_time * blip_ay_time_factor + ay_time_offset) - (pairs << blip_time_bits);
blip_time_t ay_end_time = to_ay_time( vgm_time );
ay[0].end_frame( ay_end_time );
ay[1].end_frame( ay_end_time );
huc6280_time_offset = (vgm_time * blip_huc6280_time_factor + huc6280_time_offset) - (pairs << blip_time_bits);
blip_time_t huc6280_end_time = to_huc6280_time( vgm_time );
huc6280[0].end_frame( huc6280_end_time );
huc6280[1].end_frame( huc6280_end_time );
gbdmg_time_offset = (vgm_time * blip_gbdmg_time_factor + gbdmg_time_offset) - (pairs << blip_time_bits);
blip_time_t gbdmg_end_time = to_gbdmg_time( vgm_time );
gbdmg[0].end_frame( gbdmg_end_time );
gbdmg[1].end_frame( gbdmg_end_time );
memset( DacCtrlTime, 0, sizeof(DacCtrlTime) );
return pairs * stereo;
}