// Game_Music_Emu $vers. http://www.slack.net/~ant/ #include "Vgm_Core.h" #include "dac_control.h" #include "blargg_endian.h" #include // 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; }