diff --git a/Frameworks/GME/gme/Gb_Apu.cpp b/Frameworks/GME/gme/Gb_Apu.cpp index 35b77e574..8b9f2a884 100644 --- a/Frameworks/GME/gme/Gb_Apu.cpp +++ b/Frameworks/GME/gme/Gb_Apu.cpp @@ -161,6 +161,12 @@ void Gb_Apu::set_tempo( double t ) frame_period = t ? blip_time_t (frame_period / t) : blip_time_t(0); } +void Gb_Apu::set_hacks( unsigned int mask ) +{ + wave.set_volume_hack((mask & 1) != 0); + noise.set_volume_hack((mask & 2) == 0); +} + Gb_Apu::Gb_Apu() { wave.wave_ram = ®s [wave_ram - io_addr]; @@ -187,6 +193,7 @@ Gb_Apu::Gb_Apu() set_tempo( 1.0 ); volume_ = 1.0; reset(); + set_hacks(4); } void Gb_Apu::run_until_( blip_time_t end_time ) diff --git a/Frameworks/GME/gme/Gb_Apu.h b/Frameworks/GME/gme/Gb_Apu.h index ebc72687c..7556c2f79 100644 --- a/Frameworks/GME/gme/Gb_Apu.h +++ b/Frameworks/GME/gme/Gb_Apu.h @@ -84,6 +84,11 @@ public: // Loads state. You should call reset() BEFORE this. blargg_err_t load_state( gb_apu_state_t const& in ); + // Enable hacks (bitmask): + // 0x01 = Double wave channel volume + // 0x02 = Low noise channel volume (disable doubling it) + void set_hacks( unsigned int mask ); + private: // noncopyable Gb_Apu( const Gb_Apu& ); diff --git a/Frameworks/GME/gme/Gb_Oscs.cpp b/Frameworks/GME/gme/Gb_Oscs.cpp index 08e06e31b..77fe3fc23 100644 --- a/Frameworks/GME/gme/Gb_Oscs.cpp +++ b/Frameworks/GME/gme/Gb_Oscs.cpp @@ -571,6 +571,7 @@ void Gb_Noise::run( blip_time_t time, blip_time_t end_time ) Blip_Synth_Fast const* const synth = fast_synth; // cache // Output amplitude transitions + if (volume_hack) vol <<= 1; int delta = -vol; do { @@ -603,11 +604,11 @@ void Gb_Wave::run( blip_time_t time, blip_time_t end_time ) #if GB_APU_NO_AGB static byte const shifts [4] = { 4+4, 0+4, 1+4, 2+4 }; int const volume_idx = regs [2] >> 5 & 3; - int const volume_shift = shifts [volume_idx]; + int const volume_shift = shifts [volume_idx] - (volume_hack ? 1 : 0); int const volume_mul = 1; #else static byte const volumes [8] = { 0, 4, 2, 1, 3, 3, 3, 3 }; - int const volume_shift = 2 + 4; + int const volume_shift = 2 + 4 - (volume_hack ? 1 : 0); int const volume_idx = regs [2] >> 5 & (agb_mask | 3); // 2 bits on DMG/CGB, 3 on AGB int const volume_mul = volumes [volume_idx]; #endif diff --git a/Frameworks/GME/gme/Gb_Oscs.h b/Frameworks/GME/gme/Gb_Oscs.h index fb29853e3..80fb34d25 100644 --- a/Frameworks/GME/gme/Gb_Oscs.h +++ b/Frameworks/GME/gme/Gb_Oscs.h @@ -114,6 +114,8 @@ class Gb_Noise : public Gb_Env { public: int divider; // noise has more complex frequency divider setup + + bool volume_hack; void run( blip_time_t, blip_time_t ); void write_register( int frame_phase, int reg, int old_data, int data ); @@ -123,7 +125,11 @@ public: divider = 0; Gb_Env::reset(); delay = 4 * clk_mul; // TODO: remove? + volume_hack = true; } + + void set_volume_hack( bool enable ); + private: enum { period2_mask = 0x1FFFF }; @@ -132,9 +138,13 @@ private: unsigned lfsr_mask() const { return (regs [3] & 0x08) ? ~0x4040 : ~0x4000; } }; +inline void Gb_Noise::set_volume_hack( bool enable ) { volume_hack = enable; } + class Gb_Wave : public Gb_Osc { public: int sample_buf; // last wave RAM byte read (hardware has this as well) + + bool volume_hack; void write_register( int frame_phase, int reg, int old_data, int data ); void run( blip_time_t, blip_time_t ); @@ -147,6 +157,7 @@ public: { sample_buf = 0; Gb_Osc::reset(); + volume_hack = false; } private: @@ -165,6 +176,8 @@ private: int dac_enabled() const { return regs [0] & 0x80; } void corrupt_wave(); + + void set_volume_hack( bool enable ); BOOST::uint8_t* wave_bank() const { return &wave_ram [(~regs [0] & bank40_mask) >> 2 & agb_mask]; } @@ -172,6 +185,8 @@ private: int access( int addr ) const; }; +inline void Gb_Wave::set_volume_hack( bool enable ) { volume_hack = enable; } + inline int Gb_Wave::read( int addr ) const { int index = access( addr ); diff --git a/Frameworks/GME/gme/Gbs_Emu.cpp b/Frameworks/GME/gme/Gbs_Emu.cpp index 383066e03..5ed815140 100644 --- a/Frameworks/GME/gme/Gbs_Emu.cpp +++ b/Frameworks/GME/gme/Gbs_Emu.cpp @@ -1,17 +1,17 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Gbs_Emu.h" - -/* Copyright (C) 2003-2009 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 */ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Gbs_Emu.h" + +/* Copyright (C) 2003-2009 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" @@ -21,11 +21,11 @@ Gbs_Emu::equalizer_t const Gbs_Emu::headphones_eq = { 0.0, 30, 0,0,0,0,0,0,0 Gbs_Emu::Gbs_Emu() { - sound_hardware = sound_gbs; - enable_clicking( false ); - set_type( gme_gbs_type ); - set_silence_lookahead( 6 ); - set_max_initial_silence( 21 ); + sound_hardware = sound_gbs; + enable_clicking( false ); + set_type( gme_gbs_type ); + set_silence_lookahead( 6 ); + set_max_initial_silence( 21 ); set_gain( 1.2 ); // kind of midway between headphones and speaker @@ -33,135 +33,135 @@ Gbs_Emu::Gbs_Emu() set_equalizer( eq ); } -Gbs_Emu::~Gbs_Emu() { } - -void Gbs_Emu::unload() -{ - core_.unload(); - Music_Emu::unload(); -} - -// Track info - -static void copy_gbs_fields( Gbs_Emu::header_t const& h, track_info_t* out ) -{ - GME_COPY_FIELD( h, out, game ); - GME_COPY_FIELD( h, out, author ); - GME_COPY_FIELD( h, out, copyright ); -} - -static void hash_gbs_file( Gbs_Emu::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out ) -{ - out.hash_( &h.vers, sizeof(h.vers) ); - out.hash_( &h.track_count, sizeof(h.track_count) ); - out.hash_( &h.first_track, sizeof(h.first_track) ); - out.hash_( &h.load_addr[0], sizeof(h.load_addr) ); - out.hash_( &h.init_addr[0], sizeof(h.init_addr) ); - out.hash_( &h.play_addr[0], sizeof(h.play_addr) ); - out.hash_( &h.stack_ptr[0], sizeof(h.stack_ptr) ); - out.hash_( &h.timer_modulo, sizeof(h.timer_modulo) ); - out.hash_( &h.timer_mode, sizeof(h.timer_mode) ); - out.hash_( data, data_size ); -} - -blargg_err_t Gbs_Emu::track_info_( track_info_t* out, int ) const -{ - copy_gbs_fields( header(), out ); - return blargg_ok; -} - -struct Gbs_File : Gme_Info_ -{ - Gbs_Emu::header_t const* h; - - Gbs_File() { set_type( gme_gbs_type ); } - - blargg_err_t load_mem_( byte const begin [], int size ) - { - h = ( Gbs_Emu::header_t * ) begin; - - set_track_count( h->track_count ); - if ( !h->valid_tag() ) - return blargg_err_file_type; - - return blargg_ok; - } - - blargg_err_t track_info_( track_info_t* out, int ) const - { - copy_gbs_fields( Gbs_Emu::header_t( *h ), out ); - return blargg_ok; - } - - blargg_err_t hash_( Hash_Function& out ) const - { - hash_gbs_file( *h, file_begin() + h->size, file_end() - file_begin() - h->size, out ); - return blargg_ok; - } -}; - -static Music_Emu* new_gbs_emu () { return BLARGG_NEW Gbs_Emu ; } -static Music_Emu* new_gbs_file() { return BLARGG_NEW Gbs_File; } - -gme_type_t_ const gme_gbs_type [1] = {{ "Game Boy", 0, &new_gbs_emu, &new_gbs_file, "GBS", 1 }}; - -// Setup - -blargg_err_t Gbs_Emu::load_( Data_Reader& in ) -{ - RETURN_ERR( core_.load( in ) ); - set_warning( core_.warning() ); - set_track_count( header().track_count ); - set_voice_count( Gb_Apu::osc_count ); - core_.apu().volume( gain() ); - - static const char* const names [Gb_Apu::osc_count] = { - "Square 1", "Square 2", "Wave", "Noise" - }; - set_voice_names( names ); - - static int const types [Gb_Apu::osc_count] = { - wave_type+1, wave_type+2, wave_type+3, mixed_type+1 - }; - set_voice_types( types ); - - return setup_buffer( 4194304 ); -} - -void Gbs_Emu::update_eq( blip_eq_t const& eq ) -{ - core_.apu().treble_eq( eq ); -} - -void Gbs_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) -{ - core_.apu().set_output( i, c, l, r ); -} - -void Gbs_Emu::set_tempo_( double t ) -{ - core_.set_tempo( t ); -} - -blargg_err_t Gbs_Emu::start_track_( int track ) -{ - sound_t mode = sound_hardware; - if ( mode == sound_gbs ) - mode = (header().timer_mode & 0x80) ? sound_cgb : sound_dmg; - - RETURN_ERR( core_.start_track( track, (Gb_Apu::mode_t) mode ) ); - - // clear buffer AFTER track is started, eliminating initial click - return Classic_Emu::start_track_( track ); -} - -blargg_err_t Gbs_Emu::run_clocks( blip_time_t& duration, int ) -{ - return core_.end_frame( duration ); -} - -blargg_err_t Gbs_Emu::hash_( Hash_Function& out ) const -{ - hash_gbs_file( header(), core_.rom_().begin(), core_.rom_().file_size(), out ); - return blargg_ok; +Gbs_Emu::~Gbs_Emu() { } + +void Gbs_Emu::unload() +{ + core_.unload(); + Music_Emu::unload(); +} + +// Track info + +static void copy_gbs_fields( Gbs_Emu::header_t const& h, track_info_t* out ) +{ + GME_COPY_FIELD( h, out, game ); + GME_COPY_FIELD( h, out, author ); + GME_COPY_FIELD( h, out, copyright ); +} + +static void hash_gbs_file( Gbs_Emu::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out ) +{ + out.hash_( &h.vers, sizeof(h.vers) ); + out.hash_( &h.track_count, sizeof(h.track_count) ); + out.hash_( &h.first_track, sizeof(h.first_track) ); + out.hash_( &h.load_addr[0], sizeof(h.load_addr) ); + out.hash_( &h.init_addr[0], sizeof(h.init_addr) ); + out.hash_( &h.play_addr[0], sizeof(h.play_addr) ); + out.hash_( &h.stack_ptr[0], sizeof(h.stack_ptr) ); + out.hash_( &h.timer_modulo, sizeof(h.timer_modulo) ); + out.hash_( &h.timer_mode, sizeof(h.timer_mode) ); + out.hash_( data, data_size ); +} + +blargg_err_t Gbs_Emu::track_info_( track_info_t* out, int ) const +{ + copy_gbs_fields( header(), out ); + return blargg_ok; +} + +struct Gbs_File : Gme_Info_ +{ + Gbs_Emu::header_t const* h; + + Gbs_File() { set_type( gme_gbs_type ); } + + blargg_err_t load_mem_( byte const begin [], int size ) + { + h = ( Gbs_Emu::header_t * ) begin; + + set_track_count( h->track_count ); + if ( !h->valid_tag() ) + return blargg_err_file_type; + + return blargg_ok; + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + copy_gbs_fields( Gbs_Emu::header_t( *h ), out ); + return blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_gbs_file( *h, file_begin() + h->size, file_end() - file_begin() - h->size, out ); + return blargg_ok; + } +}; + +static Music_Emu* new_gbs_emu () { return BLARGG_NEW Gbs_Emu ; } +static Music_Emu* new_gbs_file() { return BLARGG_NEW Gbs_File; } + +gme_type_t_ const gme_gbs_type [1] = {{ "Game Boy", 0, &new_gbs_emu, &new_gbs_file, "GBS", 1 }}; + +// Setup + +blargg_err_t Gbs_Emu::load_( Data_Reader& in ) +{ + RETURN_ERR( core_.load( in ) ); + set_warning( core_.warning() ); + set_track_count( header().track_count ); + set_voice_count( Gb_Apu::osc_count ); + core_.apu().volume( gain() ); + + static const char* const names [Gb_Apu::osc_count] = { + "Square 1", "Square 2", "Wave", "Noise" + }; + set_voice_names( names ); + + static int const types [Gb_Apu::osc_count] = { + wave_type+1, wave_type+2, wave_type+3, mixed_type+1 + }; + set_voice_types( types ); + + return setup_buffer( 4194304 ); +} + +void Gbs_Emu::update_eq( blip_eq_t const& eq ) +{ + core_.apu().treble_eq( eq ); +} + +void Gbs_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + core_.apu().set_output( i, c, l, r ); +} + +void Gbs_Emu::set_tempo_( double t ) +{ + core_.set_tempo( t ); +} + +blargg_err_t Gbs_Emu::start_track_( int track ) +{ + sound_t mode = sound_hardware; + if ( mode == sound_gbs ) + mode = (header().timer_mode & 0x80) ? sound_cgb : sound_dmg; + + RETURN_ERR( core_.start_track( track, (Gb_Apu::mode_t) mode ) ); + + // clear buffer AFTER track is started, eliminating initial click + return Classic_Emu::start_track_( track ); +} + +blargg_err_t Gbs_Emu::run_clocks( blip_time_t& duration, int ) +{ + return core_.end_frame( duration ); +} + +blargg_err_t Gbs_Emu::hash_( Hash_Function& out ) const +{ + hash_gbs_file( header(), core_.rom_().begin(), core_.rom_().file_size(), out ); + return blargg_ok; } \ No newline at end of file diff --git a/Frameworks/GME/gme/Vgm_Core.cpp b/Frameworks/GME/gme/Vgm_Core.cpp index 623871b88..68edcc541 100644 --- a/Frameworks/GME/gme/Vgm_Core.cpp +++ b/Frameworks/GME/gme/Vgm_Core.cpp @@ -1,2271 +1,2307 @@ -// 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_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 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); - //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 ); - - // 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(); - - 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(); - } - - 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; - - 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; -} - -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_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 ); - - memset( DacCtrlTime, 0, sizeof(DacCtrlTime) ); - - return pairs * stereo; -} +// 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; +} diff --git a/Frameworks/GME/gme/Vgm_Core.h b/Frameworks/GME/gme/Vgm_Core.h index 213f17994..f2bb6c568 100644 --- a/Frameworks/GME/gme/Vgm_Core.h +++ b/Frameworks/GME/gme/Vgm_Core.h @@ -1,346 +1,353 @@ -// Sega VGM music file emulator core - -// Game_Music_Emu $vers -#ifndef VGM_CORE_H -#define VGM_CORE_H - -#include "Gme_Loader.h" -#include "Ymz280b_Emu.h" -#include "Ymf262_Emu.h" -#include "Ym2612_Emu.h" -#include "Ym2610b_Emu.h" -#include "Ym2608_Emu.h" -#include "Ym3812_Emu.h" -#include "Ym2413_Emu.h" -#include "Ym2151_Emu.h" -#include "C140_Emu.h" -#include "SegaPcm_Emu.h" -#include "Rf5C68_Emu.h" -#include "Rf5C164_Emu.h" -#include "Pwm_Emu.h" -#include "Okim6258_Emu.h" -#include "Okim6295_Emu.h" -#include "K051649_Emu.h" -#include "K053260_Emu.h" -#include "K054539_Emu.h" -#include "Qsound_Apu.h" -#include "Ym2203_Emu.h" -#include "Ay_Apu.h" -#include "Hes_Apu.h" -#include "Sms_Apu.h" -#include "Multi_Buffer.h" -#include "Chip_Resampler.h" - - template - class Chip_Emu : public Emu { - int last_time; - short* out; - enum { disabled_time = -1 }; - public: - Chip_Emu() { last_time = disabled_time; out = NULL; } - void enable( bool b = true ) { last_time = b ? 0 : disabled_time; } - bool enabled() const { return last_time != disabled_time; } - void begin_frame( short* buf ) { out = buf; last_time = 0; } - - int run_until( int time ) - { - int count = time - last_time; - if ( count > 0 ) - { - if ( last_time < 0 ) - return false; - last_time = time; - short* p = out; - out += count * Emu::out_chan_count; - Emu::run( count, p ); - } - return true; - } - }; - -class Vgm_Core : public Gme_Loader { -public: - - // VGM file header - struct header_t - { - enum { size_min = 0x40 }; - enum { size_151 = 0x80 }; - enum { size_max = 0xC0 }; - - char tag [4]; // 0x00 - byte data_size [4]; // 0x04 - byte version [4]; // 0x08 - byte psg_rate [4]; // 0x0C - byte ym2413_rate [4]; // 0x10 - byte gd3_offset [4]; // 0x14 - byte track_duration [4]; // 0x18 - byte loop_offset [4]; // 0x1C - byte loop_duration [4]; // 0x20 - byte frame_rate [4]; // 0x24 v1.01 V - byte noise_feedback [2]; // 0x28 v1.10 V - byte noise_width; // 0x2A - byte sn76489_flags; // 0x2B v1.51 < - byte ym2612_rate [4]; // 0x2C v1.10 V - byte ym2151_rate [4]; // 0x30 - byte data_offset [4]; // 0x34 v1.50 V - byte segapcm_rate [4]; // 0x38 v1.51 V - byte segapcm_reg [4]; // 0x3C - byte rf5c68_rate [4]; // 0x40 - byte ym2203_rate [4]; // 0x44 - byte ym2608_rate [4]; // 0x48 - byte ym2610_rate [4]; // 0x4C - byte ym3812_rate [4]; // 0x50 - byte ym3526_rate [4]; // 0x54 - byte y8950_rate [4]; // 0x58 - byte ymf262_rate [4]; // 0x5C - byte ymf278b_rate [4]; // 0x60 - byte ymf271_rate [4]; // 0x64 - byte ymz280b_rate [4]; // 0x68 - byte rf5c164_rate [4]; // 0x6C - byte pwm_rate [4]; // 0x70 - byte ay8910_rate [4]; // 0x74 - byte ay8910_type; // 0x78 - byte ay8910_flags; // 0x79 - byte ym2203_ay8910_flags;// 0x7A - byte ym2608_ay8910_flags;// 0x7B - byte volume_modifier; // 0x7C v1.60 V - byte reserved; // 0x7D - byte loop_base; // 0x7E - byte loop_modifier; // 0x7F v1.51 < - byte gbdmg_rate [4]; // 0x80 v1.61 V - byte nesapu_rate [4]; // 0x84 - byte multipcm_rate [4]; // 0x88 - byte upd7759_rate [4]; // 0x8C - byte okim6258_rate [4]; // 0x90 - byte okim6258_flags; // 0x94 - byte k054539_flags; // 0x95 - byte c140_type; // 0x96 - byte reserved_flags; // 0x97 - byte okim6295_rate [4]; // 0x98 - byte k051649_rate [4]; // 0x9C - byte k054539_rate [4]; // 0xA0 - byte huc6280_rate [4]; // 0xA4 - byte c140_rate [4]; // 0xA8 - byte k053260_rate [4]; // 0xAC - byte pokey_rate [4]; // 0xB0 - byte qsound_rate [4]; // 0xB4 - byte reserved2 [4]; // 0xB8 - byte extra_offset [4]; // 0xBC - - // True if header has valid file signature - bool valid_tag() const; - int size() const; - void cleanup(); - }; - - // Header for currently loaded file - header_t const& header() const { return _header; } - - // Raw file data, for parsing GD3 tags - byte const* file_begin() const { return Gme_Loader::file_begin(); } - byte const* file_end () const { return Gme_Loader::file_end(); } - - // If file uses FM, initializes FM sound emulator using *sample_rate. If - // *sample_rate is zero, sets *sample_rate to the proper accurate rate and - // uses that. The output of the FM sound emulator is resampled to the - // final sampling rate. - blargg_err_t init_chips( double* fm_rate, bool reinit = false ); - - // True if any FM chips are used by file. Always false until init_fm() - // is called. - bool uses_fm() const { return ym2612[0].enabled() || ym2413[0].enabled() || ym2151[0].enabled() || c140.enabled() || - segapcm.enabled() || rf5c68.enabled() || rf5c164.enabled() || pwm.enabled() || okim6258[0].enabled() || okim6295[0].enabled() || - k051649.enabled() || k053260.enabled() || k054539.enabled() || ym2203[0].enabled() || ym3812[0].enabled() || ymf262[0].enabled() || - ymz280b.enabled() || ym2610[0].enabled() || ym2608[0].enabled() || qsound[0].enabled() || - (header().ay8910_rate[0] | header().ay8910_rate[1] | header().ay8910_rate[2] | header().ay8910_rate[3]) || - (header().huc6280_rate[0] | header().huc6280_rate[1] | header().huc6280_rate[2] | header().huc6280_rate[3]); } - - // Adjusts music tempo, where 1.0 is normal. Can be changed while playing. - // Loading a file resets tempo to 1.0. - void set_tempo( double ); - - void set_sample_rate( int r ) { sample_rate = r; } - - // Starts track - void start_track(); - - // Runs PSG-only VGM for msec and returns number of clocks it ran for - blip_time_t run_psg( int msec ); - - // Plays FM for at most count samples into *out, and returns number of - // samples actually generated (always even). Also runs PSG for blip_time. - int play_frame( blip_time_t blip_time, int count, blip_sample_t out [] ); - - // True if all of file data has been played - bool track_ended() const { return pos >= file_end(); } - - // 0 for PSG and YM2612 DAC, 1 for AY, 2 for HuC6280 - Stereo_Buffer stereo_buf[3]; - - // PCM sound is always generated here - Blip_Buffer * blip_buf[2]; - - // PSG sound chips, for assigning to Blip_Buffer, and setting volume and EQ - Sms_Apu psg[2]; - Ay_Apu ay[2]; - Hes_Apu huc6280[2]; - - // PCM synth, for setting volume and EQ - Blip_Synth_Fast pcm; - - // FM sound chips - Chip_Resampler_Emu ymf262[2]; - Chip_Resampler_Emu ym3812[2]; - Chip_Resampler_Emu ym2612[2]; - Chip_Resampler_Emu ym2610[2]; - Chip_Resampler_Emu ym2608[2]; - Chip_Resampler_Emu ym2413[2]; - Chip_Resampler_Emu ym2151[2]; - Chip_Resampler_Emu ym2203[2]; - - // PCM sound chips - Chip_Resampler_Emu c140; - Chip_Resampler_Emu segapcm; - Chip_Resampler_Emu rf5c68; - Chip_Resampler_Emu rf5c164; - Chip_Resampler_Emu pwm; - Chip_Resampler_Emu okim6258[2]; int okim6258_hz[2]; - Chip_Resampler_Emu okim6295[2]; int okim6295_hz; - Chip_Resampler_Emu k051649; - Chip_Resampler_Emu k053260; - Chip_Resampler_Emu k054539; - Chip_Resampler_Emu ymz280b; int ymz280b_hz; - Chip_Resampler_Emu qsound[2]; - - // DAC control - typedef struct daccontrol_data - { - bool Enable; - byte Bank; - } DACCTRL_DATA; - - byte DacCtrlUsed; - byte DacCtrlUsg[0xFF]; - DACCTRL_DATA DacCtrl[0xFF]; - byte DacCtrlMap[0xFF]; - int DacCtrlTime[0xFF]; - void ** dac_control; - - void dac_control_grow(byte chip_id); - - int dac_control_recursion; - - int run_dac_control( int time ); - -public: - void chip_reg_write(unsigned Sample, byte ChipType, byte ChipID, byte Port, byte Offset, byte Data); - -// Implementation -public: - Vgm_Core(); - ~Vgm_Core(); - -protected: - virtual blargg_err_t load_mem_( byte const [], int ); - -private: - // blip_time_t // PSG clocks - typedef int vgm_time_t; // 44100 per second, REGARDLESS of sample rate - typedef int fm_time_t; // FM sample count - - int sample_rate; - int vgm_rate; // rate of log, 44100 normally, adjusted by tempo - double fm_rate; // FM samples per second - - header_t _header; - - // VGM to FM time - int fm_time_factor; - int fm_time_offset; - fm_time_t to_fm_time( vgm_time_t ) const; - - // VGM to PSG time - int blip_time_factor; - blip_time_t to_psg_time( vgm_time_t ) const; - - int blip_ay_time_factor; - int ay_time_offset; - blip_time_t to_ay_time( vgm_time_t ) const; - - int blip_huc6280_time_factor; - int huc6280_time_offset; - blip_time_t to_huc6280_time( vgm_time_t ) const; - - // Current time and position in log - vgm_time_t vgm_time; - byte const* pos; - byte const* loop_begin; - bool has_looped; - - // PCM - enum { PCM_BANK_COUNT = 0x40 }; - typedef struct _vgm_pcm_bank_data - { - unsigned DataSize; - byte* Data; - unsigned DataStart; - } VGM_PCM_DATA; - typedef struct _vgm_pcm_bank - { - unsigned BankCount; - VGM_PCM_DATA* Bank; - unsigned DataSize; - byte* Data; - unsigned DataPos; - unsigned BnkPos; - } VGM_PCM_BANK; - - typedef struct pcmbank_table - { - byte ComprType; - byte CmpSubType; - byte BitDec; - byte BitCmp; - unsigned EntryCount; - void* Entries; - } PCMBANK_TBL; - - VGM_PCM_BANK PCMBank[PCM_BANK_COUNT]; - PCMBANK_TBL PCMTbl; - - void ReadPCMTable(unsigned DataSize, const byte* Data); - void AddPCMData(byte Type, unsigned DataSize, const byte* Data); - bool DecompressDataBlk(VGM_PCM_DATA* Bank, unsigned DataSize, const byte* Data); - const byte* GetPointerFromPCMBank(byte Type, unsigned DataPos); - - byte const* pcm_pos; // current position in PCM data - int dac_amp[2]; - int dac_disabled[2]; // -1 if disabled - void write_pcm( vgm_time_t, int chip, int amp ); - - blip_time_t run( vgm_time_t ); - int run_ym2151( int chip, int time ); - int run_ym2203( int chip, int time ); - int run_ym2413( int chip, int time ); - int run_ym2612( int chip, int time ); - int run_ym3812( int chip, int time ); - int run_ymf262( int chip, int time ); - int run_ym2610( int chip, int time ); - int run_ym2608( int chip, int time ); - int run_ymz280b( int time ); - int run_c140( int time ); - int run_segapcm( int time ); - int run_rf5c68( int time ); - int run_rf5c164( int time ); - int run_pwm( int time ); - int run_okim6258( int chip, int time ); - int run_okim6295( int chip, int time ); - int run_k051649( int time ); - int run_k053260( int time ); - int run_k054539( int time ); - int run_qsound( int chip, int time ); - void update_fm_rates( int* ym2151_rate, int* ym2413_rate, int* ym2612_rate ) const; -}; - -#endif +// Sega VGM music file emulator core + +// Game_Music_Emu $vers +#ifndef VGM_CORE_H +#define VGM_CORE_H + +#include "Gme_Loader.h" +#include "Ymz280b_Emu.h" +#include "Ymf262_Emu.h" +#include "Ym2612_Emu.h" +#include "Ym2610b_Emu.h" +#include "Ym2608_Emu.h" +#include "Ym3812_Emu.h" +#include "Ym2413_Emu.h" +#include "Ym2151_Emu.h" +#include "C140_Emu.h" +#include "SegaPcm_Emu.h" +#include "Rf5C68_Emu.h" +#include "Rf5C164_Emu.h" +#include "Pwm_Emu.h" +#include "Okim6258_Emu.h" +#include "Okim6295_Emu.h" +#include "K051649_Emu.h" +#include "K053260_Emu.h" +#include "K054539_Emu.h" +#include "Qsound_Apu.h" +#include "Ym2203_Emu.h" +#include "Ay_Apu.h" +#include "Gb_Apu.h" +#include "Hes_Apu.h" +#include "Sms_Apu.h" +#include "Multi_Buffer.h" +#include "Chip_Resampler.h" + + template + class Chip_Emu : public Emu { + int last_time; + short* out; + enum { disabled_time = -1 }; + public: + Chip_Emu() { last_time = disabled_time; out = NULL; } + void enable( bool b = true ) { last_time = b ? 0 : disabled_time; } + bool enabled() const { return last_time != disabled_time; } + void begin_frame( short* buf ) { out = buf; last_time = 0; } + + int run_until( int time ) + { + int count = time - last_time; + if ( count > 0 ) + { + if ( last_time < 0 ) + return false; + last_time = time; + short* p = out; + out += count * Emu::out_chan_count; + Emu::run( count, p ); + } + return true; + } + }; + +class Vgm_Core : public Gme_Loader { +public: + + // VGM file header + struct header_t + { + enum { size_min = 0x40 }; + enum { size_151 = 0x80 }; + enum { size_max = 0xC0 }; + + char tag [4]; // 0x00 + byte data_size [4]; // 0x04 + byte version [4]; // 0x08 + byte psg_rate [4]; // 0x0C + byte ym2413_rate [4]; // 0x10 + byte gd3_offset [4]; // 0x14 + byte track_duration [4]; // 0x18 + byte loop_offset [4]; // 0x1C + byte loop_duration [4]; // 0x20 + byte frame_rate [4]; // 0x24 v1.01 V + byte noise_feedback [2]; // 0x28 v1.10 V + byte noise_width; // 0x2A + byte sn76489_flags; // 0x2B v1.51 < + byte ym2612_rate [4]; // 0x2C v1.10 V + byte ym2151_rate [4]; // 0x30 + byte data_offset [4]; // 0x34 v1.50 V + byte segapcm_rate [4]; // 0x38 v1.51 V + byte segapcm_reg [4]; // 0x3C + byte rf5c68_rate [4]; // 0x40 + byte ym2203_rate [4]; // 0x44 + byte ym2608_rate [4]; // 0x48 + byte ym2610_rate [4]; // 0x4C + byte ym3812_rate [4]; // 0x50 + byte ym3526_rate [4]; // 0x54 + byte y8950_rate [4]; // 0x58 + byte ymf262_rate [4]; // 0x5C + byte ymf278b_rate [4]; // 0x60 + byte ymf271_rate [4]; // 0x64 + byte ymz280b_rate [4]; // 0x68 + byte rf5c164_rate [4]; // 0x6C + byte pwm_rate [4]; // 0x70 + byte ay8910_rate [4]; // 0x74 + byte ay8910_type; // 0x78 + byte ay8910_flags; // 0x79 + byte ym2203_ay8910_flags;// 0x7A + byte ym2608_ay8910_flags;// 0x7B + byte volume_modifier; // 0x7C v1.60 V + byte reserved; // 0x7D + byte loop_base; // 0x7E + byte loop_modifier; // 0x7F v1.51 < + byte gbdmg_rate [4]; // 0x80 v1.61 V + byte nesapu_rate [4]; // 0x84 + byte multipcm_rate [4]; // 0x88 + byte upd7759_rate [4]; // 0x8C + byte okim6258_rate [4]; // 0x90 + byte okim6258_flags; // 0x94 + byte k054539_flags; // 0x95 + byte c140_type; // 0x96 + byte reserved_flags; // 0x97 + byte okim6295_rate [4]; // 0x98 + byte k051649_rate [4]; // 0x9C + byte k054539_rate [4]; // 0xA0 + byte huc6280_rate [4]; // 0xA4 + byte c140_rate [4]; // 0xA8 + byte k053260_rate [4]; // 0xAC + byte pokey_rate [4]; // 0xB0 + byte qsound_rate [4]; // 0xB4 + byte reserved2 [4]; // 0xB8 + byte extra_offset [4]; // 0xBC + + // True if header has valid file signature + bool valid_tag() const; + int size() const; + void cleanup(); + }; + + // Header for currently loaded file + header_t const& header() const { return _header; } + + // Raw file data, for parsing GD3 tags + byte const* file_begin() const { return Gme_Loader::file_begin(); } + byte const* file_end () const { return Gme_Loader::file_end(); } + + // If file uses FM, initializes FM sound emulator using *sample_rate. If + // *sample_rate is zero, sets *sample_rate to the proper accurate rate and + // uses that. The output of the FM sound emulator is resampled to the + // final sampling rate. + blargg_err_t init_chips( double* fm_rate, bool reinit = false ); + + // True if any FM chips are used by file. Always false until init_fm() + // is called. + bool uses_fm() const { return ym2612[0].enabled() || ym2413[0].enabled() || ym2151[0].enabled() || c140.enabled() || + segapcm.enabled() || rf5c68.enabled() || rf5c164.enabled() || pwm.enabled() || okim6258[0].enabled() || okim6295[0].enabled() || + k051649.enabled() || k053260.enabled() || k054539.enabled() || ym2203[0].enabled() || ym3812[0].enabled() || ymf262[0].enabled() || + ymz280b.enabled() || ym2610[0].enabled() || ym2608[0].enabled() || qsound[0].enabled() || + (header().ay8910_rate[0] | header().ay8910_rate[1] | header().ay8910_rate[2] | header().ay8910_rate[3]) || + (header().huc6280_rate[0] | header().huc6280_rate[1] | header().huc6280_rate[2] | header().huc6280_rate[3]) || + (header().gbdmg_rate[0] | header().gbdmg_rate[1] | header().gbdmg_rate[2] | header().gbdmg_rate[3]); } + + // Adjusts music tempo, where 1.0 is normal. Can be changed while playing. + // Loading a file resets tempo to 1.0. + void set_tempo( double ); + + void set_sample_rate( int r ) { sample_rate = r; } + + // Starts track + void start_track(); + + // Runs PSG-only VGM for msec and returns number of clocks it ran for + blip_time_t run_psg( int msec ); + + // Plays FM for at most count samples into *out, and returns number of + // samples actually generated (always even). Also runs PSG for blip_time. + int play_frame( blip_time_t blip_time, int count, blip_sample_t out [] ); + + // True if all of file data has been played + bool track_ended() const { return pos >= file_end(); } + + // 0 for PSG and YM2612 DAC, 1 for AY, 2 for HuC6280, 3 for GB DMG + Stereo_Buffer stereo_buf[4]; + + // PCM sound is always generated here + Blip_Buffer * blip_buf[2]; + + // PSG sound chips, for assigning to Blip_Buffer, and setting volume and EQ + Sms_Apu psg[2]; + Ay_Apu ay[2]; + Hes_Apu huc6280[2]; + Gb_Apu gbdmg[2]; + + // PCM synth, for setting volume and EQ + Blip_Synth_Fast pcm; + + // FM sound chips + Chip_Resampler_Emu ymf262[2]; + Chip_Resampler_Emu ym3812[2]; + Chip_Resampler_Emu ym2612[2]; + Chip_Resampler_Emu ym2610[2]; + Chip_Resampler_Emu ym2608[2]; + Chip_Resampler_Emu ym2413[2]; + Chip_Resampler_Emu ym2151[2]; + Chip_Resampler_Emu ym2203[2]; + + // PCM sound chips + Chip_Resampler_Emu c140; + Chip_Resampler_Emu segapcm; + Chip_Resampler_Emu rf5c68; + Chip_Resampler_Emu rf5c164; + Chip_Resampler_Emu pwm; + Chip_Resampler_Emu okim6258[2]; int okim6258_hz[2]; + Chip_Resampler_Emu okim6295[2]; int okim6295_hz; + Chip_Resampler_Emu k051649; + Chip_Resampler_Emu k053260; + Chip_Resampler_Emu k054539; + Chip_Resampler_Emu ymz280b; int ymz280b_hz; + Chip_Resampler_Emu qsound[2]; + + // DAC control + typedef struct daccontrol_data + { + bool Enable; + byte Bank; + } DACCTRL_DATA; + + byte DacCtrlUsed; + byte DacCtrlUsg[0xFF]; + DACCTRL_DATA DacCtrl[0xFF]; + byte DacCtrlMap[0xFF]; + int DacCtrlTime[0xFF]; + void ** dac_control; + + void dac_control_grow(byte chip_id); + + int dac_control_recursion; + + int run_dac_control( int time ); + +public: + void chip_reg_write(unsigned Sample, byte ChipType, byte ChipID, byte Port, byte Offset, byte Data); + +// Implementation +public: + Vgm_Core(); + ~Vgm_Core(); + +protected: + virtual blargg_err_t load_mem_( byte const [], int ); + +private: + // blip_time_t // PSG clocks + typedef int vgm_time_t; // 44100 per second, REGARDLESS of sample rate + typedef int fm_time_t; // FM sample count + + int sample_rate; + int vgm_rate; // rate of log, 44100 normally, adjusted by tempo + double fm_rate; // FM samples per second + + header_t _header; + + // VGM to FM time + int fm_time_factor; + int fm_time_offset; + fm_time_t to_fm_time( vgm_time_t ) const; + + // VGM to PSG time + int blip_time_factor; + blip_time_t to_psg_time( vgm_time_t ) const; + + int blip_ay_time_factor; + int ay_time_offset; + blip_time_t to_ay_time( vgm_time_t ) const; + + int blip_huc6280_time_factor; + int huc6280_time_offset; + blip_time_t to_huc6280_time( vgm_time_t ) const; + + int blip_gbdmg_time_factor; + int gbdmg_time_offset; + blip_time_t to_gbdmg_time( vgm_time_t ) const; + + // Current time and position in log + vgm_time_t vgm_time; + byte const* pos; + byte const* loop_begin; + bool has_looped; + + // PCM + enum { PCM_BANK_COUNT = 0x40 }; + typedef struct _vgm_pcm_bank_data + { + unsigned DataSize; + byte* Data; + unsigned DataStart; + } VGM_PCM_DATA; + typedef struct _vgm_pcm_bank + { + unsigned BankCount; + VGM_PCM_DATA* Bank; + unsigned DataSize; + byte* Data; + unsigned DataPos; + unsigned BnkPos; + } VGM_PCM_BANK; + + typedef struct pcmbank_table + { + byte ComprType; + byte CmpSubType; + byte BitDec; + byte BitCmp; + unsigned EntryCount; + void* Entries; + } PCMBANK_TBL; + + VGM_PCM_BANK PCMBank[PCM_BANK_COUNT]; + PCMBANK_TBL PCMTbl; + + void ReadPCMTable(unsigned DataSize, const byte* Data); + void AddPCMData(byte Type, unsigned DataSize, const byte* Data); + bool DecompressDataBlk(VGM_PCM_DATA* Bank, unsigned DataSize, const byte* Data); + const byte* GetPointerFromPCMBank(byte Type, unsigned DataPos); + + byte const* pcm_pos; // current position in PCM data + int dac_amp[2]; + int dac_disabled[2]; // -1 if disabled + void write_pcm( vgm_time_t, int chip, int amp ); + + blip_time_t run( vgm_time_t ); + int run_ym2151( int chip, int time ); + int run_ym2203( int chip, int time ); + int run_ym2413( int chip, int time ); + int run_ym2612( int chip, int time ); + int run_ym3812( int chip, int time ); + int run_ymf262( int chip, int time ); + int run_ym2610( int chip, int time ); + int run_ym2608( int chip, int time ); + int run_ymz280b( int time ); + int run_c140( int time ); + int run_segapcm( int time ); + int run_rf5c68( int time ); + int run_rf5c164( int time ); + int run_pwm( int time ); + int run_okim6258( int chip, int time ); + int run_okim6295( int chip, int time ); + int run_k051649( int time ); + int run_k053260( int time ); + int run_k054539( int time ); + int run_qsound( int chip, int time ); + void update_fm_rates( int* ym2151_rate, int* ym2413_rate, int* ym2612_rate ) const; +}; + +#endif diff --git a/Frameworks/GME/gme/Vgm_Emu.cpp b/Frameworks/GME/gme/Vgm_Emu.cpp index 4b8523a18..14c7364c4 100644 --- a/Frameworks/GME/gme/Vgm_Emu.cpp +++ b/Frameworks/GME/gme/Vgm_Emu.cpp @@ -1,33 +1,33 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Vgm_Emu.h" - -#include "blargg_endian.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" - -// FM emulators are internally quieter to avoid 16-bit overflow -double const fm_gain = 3.0; -double const rolloff = 0.990; -double const oversample_factor = 1.5; - -Vgm_Emu::Vgm_Emu() -{ - resampler.set_callback( play_frame_, this ); - disable_oversampling_ = false; - muted_voices = 0; - set_type( gme_vgm_type ); +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Vgm_Emu.h" + +#include "blargg_endian.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" + +// FM emulators are internally quieter to avoid 16-bit overflow +double const fm_gain = 3.0; +double const rolloff = 0.990; +double const oversample_factor = 1.5; + +Vgm_Emu::Vgm_Emu() +{ + resampler.set_callback( play_frame_, this ); + disable_oversampling_ = false; + muted_voices = 0; + set_type( gme_vgm_type ); set_max_initial_silence( 1 ); set_silence_lookahead( 1 ); // tracks should already be trimmed @@ -35,504 +35,517 @@ Vgm_Emu::Vgm_Emu() set_equalizer( eq ); } -Vgm_Emu::~Vgm_Emu() { } - -void Vgm_Emu::unload() -{ - core.unload(); - Classic_Emu::unload(); -} - -// Track info - -static byte const* skip_gd3_str( byte const in [], byte const* end ) -{ - while ( end - in >= 2 ) - { - in += 2; - if ( !(in [-2] | in [-1]) ) - break; - } - return in; -} - -static byte const* get_gd3_str( byte const* in, byte const* end, char field [] ) -{ - byte const* mid = skip_gd3_str( in, end ); - int len = (mid - in) / 2 - 1; - if ( len > 0 ) - { - len = min( len, (int) Gme_File::max_field_ ); - field [len] = 0; - for ( int i = 0; i < len; i++ ) - field [i] = (in [i * 2 + 1] ? '?' : in [i * 2]); // TODO: convert to utf-8 - } - return mid; -} - -static byte const* get_gd3_pair( byte const* in, byte const* end, char field [] ) -{ - return skip_gd3_str( get_gd3_str( in, end, field ), end ); -} - -static void parse_gd3( byte const in [], byte const* end, track_info_t* out ) -{ - in = get_gd3_pair( in, end, out->song ); - in = get_gd3_pair( in, end, out->game ); - in = get_gd3_pair( in, end, out->system ); - in = get_gd3_pair( in, end, out->author ); - in = get_gd3_str ( in, end, out->copyright ); - in = get_gd3_pair( in, end, out->dumper ); - in = get_gd3_str ( in, end, out->comment ); -} - -int const gd3_header_size = 12; - -static int check_gd3_header( byte const h [], int remain ) -{ - if ( remain < gd3_header_size ) return 0; - if ( memcmp( h, "Gd3 ", 4 ) ) return 0; - if ( get_le32( h + 4 ) >= 0x200 ) return 0; - - int gd3_size = get_le32( h + 8 ); - if ( gd3_size > remain - gd3_header_size ) return 0; - - return gd3_size; -} - -static void get_vgm_length( Vgm_Emu::header_t const& h, track_info_t* out ) -{ - int length = get_le32( h.track_duration ) * 10 / 441; // 1000 / 44100 - if ( length > 0 ) - { - int loop = get_le32( h.loop_duration ); - if ( loop > 0 && get_le32( h.loop_offset ) ) - { - out->loop_length = loop * 10 / 441; - out->intro_length = length - out->loop_length; - check( out->loop_length <= length ); - // TODO: Also set out->length? We now have play_length for suggested play time. - } - else - { - out->length = length; - out->intro_length = length; - out->loop_length = 0; - } - } -} - -blargg_err_t Vgm_Emu::track_info_( track_info_t* out, int ) const -{ - get_vgm_length( header(), out ); - - int gd3_offset = get_le32( header().gd3_offset ); - if ( gd3_offset <= 0 ) - return blargg_ok; - - byte const* gd3 = core.file_begin() + gd3_offset + offsetof( header_t, gd3_offset ); - int gd3_size = check_gd3_header( gd3, core.file_end() - gd3 ); - if ( gd3_size ) - { - byte const* gd3_data = gd3 + gd3_header_size; - parse_gd3( gd3_data, gd3_data + gd3_size, out ); - } - - return blargg_ok; -} - -blargg_err_t Vgm_Emu::gd3_data( const unsigned char ** data, int * size ) -{ - *data = 0; - *size = 0; - - int gd3_offset = get_le32( header().gd3_offset ); - if ( gd3_offset <= 0 ) - return blargg_ok; - - byte const* gd3 = core.file_begin() + gd3_offset + offsetof( header_t, gd3_offset ); - int gd3_size = check_gd3_header( gd3, core.file_end() - gd3 ); - if ( gd3_size ) - { - *data = gd3; - *size = gd3_size + gd3_header_size; - } - - return blargg_ok; -} - -static void hash_vgm_file( Vgm_Emu::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out ) -{ - out.hash_( &h.data_size[0], sizeof(h.data_size) ); - out.hash_( &h.version[0], sizeof(h.version) ); - out.hash_( &h.psg_rate[0], sizeof(h.psg_rate) ); - out.hash_( &h.ym2413_rate[0], sizeof(h.ym2413_rate) ); - out.hash_( &h.track_duration[0], sizeof(h.track_duration) ); - out.hash_( &h.loop_offset[0], sizeof(h.loop_offset) ); - out.hash_( &h.loop_duration[0], sizeof(h.loop_duration) ); - out.hash_( &h.frame_rate[0], sizeof(h.frame_rate) ); - out.hash_( &h.noise_feedback[0], sizeof(h.noise_feedback) ); - out.hash_( &h.noise_width, sizeof(h.noise_width) ); - out.hash_( &h.sn76489_flags, sizeof(h.sn76489_flags) ); - out.hash_( &h.ym2612_rate[0], sizeof(h.ym2612_rate) ); - out.hash_( &h.ym2151_rate[0], sizeof(h.ym2151_rate) ); - out.hash_( &h.data_offset[0], sizeof(h.data_offset) ); - out.hash_( &h.segapcm_rate[0], sizeof(h.segapcm_rate) ); - out.hash_( &h.segapcm_reg[0], sizeof(h.segapcm_reg) ); - out.hash_( &h.rf5c68_rate[0], sizeof(h.rf5c68_rate) ); - out.hash_( &h.ym2203_rate[0], sizeof(h.ym2203_rate) ); - out.hash_( &h.ym2608_rate[0], sizeof(h.ym2608_rate) ); - out.hash_( &h.ym2610_rate[0], sizeof(h.ym2610_rate) ); - out.hash_( &h.ym3812_rate[0], sizeof(h.ym3812_rate) ); - out.hash_( &h.ym3526_rate[0], sizeof(h.ym3526_rate) ); - out.hash_( &h.y8950_rate[0], sizeof(h.y8950_rate) ); - out.hash_( &h.ymf262_rate[0], sizeof(h.ymf262_rate) ); - out.hash_( &h.ymf278b_rate[0], sizeof(h.ymf278b_rate) ); - out.hash_( &h.ymf271_rate[0], sizeof(h.ymf271_rate) ); - out.hash_( &h.ymz280b_rate[0], sizeof(h.ymz280b_rate) ); - out.hash_( &h.rf5c164_rate[0], sizeof(h.rf5c164_rate) ); - out.hash_( &h.pwm_rate[0], sizeof(h.pwm_rate) ); - out.hash_( &h.ay8910_rate[0], sizeof(h.ay8910_rate) ); - out.hash_( &h.ay8910_type, sizeof(h.ay8910_type) ); - out.hash_( &h.ay8910_flags, sizeof(h.ay8910_flags) ); - out.hash_( &h.ym2203_ay8910_flags, sizeof(h.ym2203_ay8910_flags) ); - out.hash_( &h.ym2608_ay8910_flags, sizeof(h.ym2608_ay8910_flags) ); - out.hash_( &h.reserved, sizeof(h.reserved) ); - out.hash_( &h.gbdmg_rate[0], sizeof(h.gbdmg_rate) ); - out.hash_( &h.nesapu_rate[0], sizeof(h.nesapu_rate) ); - out.hash_( &h.multipcm_rate[0], sizeof(h.multipcm_rate) ); - out.hash_( &h.upd7759_rate[0], sizeof(h.upd7759_rate) ); - out.hash_( &h.okim6258_rate[0], sizeof(h.okim6258_rate) ); - out.hash_( &h.okim6258_flags, sizeof(h.okim6258_flags) ); - out.hash_( &h.k054539_flags, sizeof(h.k054539_flags) ); - out.hash_( &h.c140_type, sizeof(h.c140_type) ); - out.hash_( &h.reserved_flags, sizeof(h.reserved_flags) ); - out.hash_( &h.okim6295_rate[0], sizeof(h.okim6295_rate) ); - out.hash_( &h.k051649_rate[0], sizeof(h.k051649_rate) ); - out.hash_( &h.k054539_rate[0], sizeof(h.k054539_rate) ); - out.hash_( &h.huc6280_rate[0], sizeof(h.huc6280_rate) ); - out.hash_( &h.c140_rate[0], sizeof(h.c140_rate) ); - out.hash_( &h.k053260_rate[0], sizeof(h.k053260_rate) ); - out.hash_( &h.pokey_rate[0], sizeof(h.pokey_rate) ); - out.hash_( &h.qsound_rate[0], sizeof(h.qsound_rate) ); - out.hash_( &h.reserved2[0], sizeof(h.reserved2) ); - out.hash_( &h.extra_offset[0], sizeof(h.extra_offset) ); - out.hash_( data, data_size ); -} - -struct Vgm_File : Gme_Info_ -{ - Vgm_Emu::header_t h; - blargg_vector data; - blargg_vector gd3; - - Vgm_File() { set_type( gme_vgm_type ); } - - blargg_err_t load_( Data_Reader& in ) - { - int file_size = in.remain(); - if ( file_size <= h.size_min ) - return blargg_err_file_type; - - RETURN_ERR( in.read( &h, h.size_min ) ); - if ( !h.valid_tag() ) - return blargg_err_file_type; - - if ( h.size() > h.size_min ) - RETURN_ERR( in.read( &h.rf5c68_rate, h.size() - h.size_min ) ); - - h.cleanup(); - - int data_offset = get_le32( h.data_offset ) + offsetof( Vgm_Core::header_t, data_offset ); - int data_size = file_size - offsetof( Vgm_Core::header_t, data_offset ) - data_offset; - int gd3_offset = get_le32( h.gd3_offset ); - if ( gd3_offset > 0 ) - gd3_offset += offsetof( Vgm_Core::header_t, gd3_offset ); - - int amount_to_skip = gd3_offset - h.size(); - - if ( gd3_offset > 0 && gd3_offset > data_offset ) - { - data_size = gd3_offset - data_offset; - amount_to_skip = 0; - - RETURN_ERR( data.resize( data_size ) ); - RETURN_ERR( in.skip( data_offset - h.size() ) ); - RETURN_ERR( in.read( data.begin(), data_size ) ); - } - - int remain = file_size - gd3_offset; - byte gd3_h [gd3_header_size]; - if ( gd3_offset > 0 && remain >= gd3_header_size ) - { - RETURN_ERR( in.skip( amount_to_skip ) ); - RETURN_ERR( in.read( gd3_h, sizeof gd3_h ) ); - int gd3_size = check_gd3_header( gd3_h, remain ); - if ( gd3_size ) - { - RETURN_ERR( gd3.resize( gd3_size ) ); - RETURN_ERR( in.read( gd3.begin(), gd3.size() ) ); - } - - if ( data_offset > gd3_offset ) - { - RETURN_ERR( data.resize( data_size ) ); - RETURN_ERR( in.skip( data_offset - gd3_offset - sizeof gd3_h - gd3.size() ) ); - RETURN_ERR( in.read( data.begin(), data.end() - data.begin() ) ); - } - } - - return blargg_ok; - } - - blargg_err_t track_info_( track_info_t* out, int ) const - { - get_vgm_length( h, out ); - if ( gd3.size() ) - parse_gd3( gd3.begin(), gd3.end(), out ); - return blargg_ok; - } - - blargg_err_t hash_( Hash_Function& out ) const - { - hash_vgm_file( h, data.begin(), data.end() - data.begin(), out ); - return blargg_ok; - } -}; - -static Music_Emu* new_vgm_emu () { return BLARGG_NEW Vgm_Emu ; } -static Music_Emu* new_vgm_file() { return BLARGG_NEW Vgm_File; } - -gme_type_t_ const gme_vgm_type [1] = {{ "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGM", 1 }}; - -gme_type_t_ const gme_vgz_type [1] = {{ "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGZ", 1 }}; - -// Setup - -void Vgm_Emu::set_tempo_( double t ) -{ - core.set_tempo( t ); -} - -blargg_err_t Vgm_Emu::set_sample_rate_( int sample_rate ) -{ - RETURN_ERR( core.stereo_buf[0].set_sample_rate( sample_rate, 1000 / 30 ) ); - RETURN_ERR( core.stereo_buf[1].set_sample_rate( sample_rate, 1000 / 30 ) ); - RETURN_ERR( core.stereo_buf[2].set_sample_rate( sample_rate, 1000 / 30 ) ); - core.set_sample_rate( sample_rate ); - return Classic_Emu::set_sample_rate_( sample_rate ); -} - -void Vgm_Emu::update_eq( blip_eq_t const& eq ) -{ - core.psg[0].treble_eq( eq ); - core.psg[1].treble_eq( eq ); - core.ay[0].treble_eq( eq ); - core.ay[1].treble_eq( eq ); - core.huc6280[0].treble_eq( eq ); - core.huc6280[1].treble_eq( eq ); - core.pcm.treble_eq( eq ); -} - -void Vgm_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) -{ - if ( i < core.psg[0].osc_count ) - { - core.psg[0].set_output( i, c, l, r ); - core.psg[1].set_output( i, c, l, r ); - } -} - -void Vgm_Emu::mute_voices_( int mask ) -{ - muted_voices = mask; - - Classic_Emu::mute_voices_( mask ); - - // TODO: what was this for? - //core.pcm.output( &core.blip_buf ); - - // TODO: silence PCM if FM isn't used? - if ( core.uses_fm() ) - { - core.psg[0].set_output( ( mask & 0x80 ) ? 0 : core.stereo_buf[0].center() ); - core.psg[1].set_output( ( mask & 0x80 ) ? 0 : core.stereo_buf[0].center() ); - core.ay[0].set_output( ( mask & 0x80 ) ? 0 : core.stereo_buf[1].center() ); - core.ay[1].set_output( ( mask & 0x80 ) ? 0 : core.stereo_buf[1].center() ); - for ( unsigned i = 0, j = 1; i < core.huc6280[0].osc_count; i++, j <<= 1) - { - Blip_Buffer * center = ( mask & j ) ? 0 : core.stereo_buf[2].center(); - Blip_Buffer * left = ( mask & j ) ? 0 : core.stereo_buf[2].left(); - Blip_Buffer * right = ( mask & j ) ? 0 : core.stereo_buf[2].right(); - core.huc6280[0].set_output( i, center, left, right ); - core.huc6280[1].set_output( i, center, left, right ); - } - if ( core.ym2612[0].enabled() ) - { - core.pcm.volume( (mask & 0x40) ? 0.0 : 0.1115 / 256 * fm_gain * gain() ); - core.ym2612[0].mute_voices( mask ); - if ( core.ym2612[1].enabled() ) - core.ym2612[1].mute_voices( mask ); - } - - if ( core.ym2413[0].enabled() ) - { - int m = mask & 0x3F; - if ( mask & 0x20 ) - m |= 0x01E0; // channels 5-8 - if ( mask & 0x40 ) - m |= 0x3E00; - core.ym2413[0].mute_voices( m ); - if ( core.ym2413[1].enabled() ) - core.ym2413[1].mute_voices( m ); - } - - if ( core.ym2151[0].enabled() ) - { - core.ym2151[0].mute_voices( mask ); - if ( core.ym2151[1].enabled() ) - core.ym2151[1].mute_voices( mask ); - } - - if ( core.c140.enabled() ) - { - int m = 0; - int m_add = 7; - for ( unsigned i = 0; i < 8; i++, m_add <<= 3 ) - { - if ( mask & ( 1 << i ) ) m += m_add; - } - core.c140.mute_voices( m ); - } - - if ( core.rf5c68.enabled() ) - { - core.rf5c68.mute_voices( mask ); - } - - if ( core.rf5c164.enabled() ) - { - core.rf5c164.mute_voices( mask ); - } - } -} - -blargg_err_t Vgm_Emu::load_mem_( byte const data [], int size ) -{ - RETURN_ERR( core.load_mem( data, size ) ); - - set_voice_count( core.psg[0].osc_count ); - - double fm_rate = 0.0; - if ( !disable_oversampling_ ) - fm_rate = sample_rate() * oversample_factor; - RETURN_ERR( core.init_chips( &fm_rate ) ); - - double psg_gain = ( ( core.header().psg_rate[3] & 0xC0 ) == 0x40 ) ? 0.5 : 1.0; - - if ( core.uses_fm() ) - { - set_voice_count( 8 ); - RETURN_ERR( resampler.setup( fm_rate / sample_rate(), rolloff, gain() ) ); - RETURN_ERR( resampler.reset( core.stereo_buf[0].length() * sample_rate() / 1000 ) ); - core.psg[0].volume( 0.135 * fm_gain * psg_gain * gain() ); - core.psg[1].volume( 0.135 * fm_gain * psg_gain * gain() ); - core.ay[0].volume( 0.135 * fm_gain * gain() ); - core.ay[1].volume( 0.135 * fm_gain * gain() ); - core.huc6280[0].volume( 0.135 * fm_gain * gain() ); - core.huc6280[1].volume( 0.135 * fm_gain * gain() ); - } - else - { - core.psg[0].volume( psg_gain * gain() ); - core.psg[1].volume( psg_gain * gain() ); - } - - static const char* const fm_names [] = { - "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG" - }; - static const char* const psg_names [] = { "Square 1", "Square 2", "Square 3", "Noise" }; - set_voice_names( core.uses_fm() ? fm_names : psg_names ); - - static int const types [8] = { - wave_type+1, wave_type+2, wave_type+3, noise_type+1, - 0, 0, 0, 0 - }; - set_voice_types( types ); - - return Classic_Emu::setup_buffer( core.stereo_buf[0].center()->clock_rate() ); -} - -// Emulation - -blargg_err_t Vgm_Emu::start_track_( int track ) -{ - RETURN_ERR( Classic_Emu::start_track_( track ) ); - - core.start_track(); - - mute_voices_(muted_voices); - - if ( core.uses_fm() ) - resampler.clear(); - - return blargg_ok; -} - -inline void Vgm_Emu::check_end() -{ - if ( core.track_ended() ) - set_track_ended(); -} - -inline void Vgm_Emu::check_warning() -{ - const char* w = core.warning(); - if ( w ) - set_warning( w ); -} - -blargg_err_t Vgm_Emu::run_clocks( blip_time_t& time_io, int msec ) -{ - check_end(); - time_io = core.run_psg( msec ); - check_warning(); - return blargg_ok; -} - -inline int Vgm_Emu::play_frame( blip_time_t blip_time, int sample_count, sample_t buf [] ) -{ - check_end(); - int result = core.play_frame( blip_time, sample_count, buf ); - check_warning(); - return result; -} - -int Vgm_Emu::play_frame_( void* p, blip_time_t a, int b, sample_t c [] ) -{ - return STATIC_CAST(Vgm_Emu*,p)->play_frame( a, b, c ); -} - -blargg_err_t Vgm_Emu::play_( int count, sample_t out [] ) -{ - if ( !core.uses_fm() ) - return Classic_Emu::play_( count, out ); - - Stereo_Buffer * secondaries[] = { &core.stereo_buf[1], &core.stereo_buf[2] }; - resampler.dual_play( count, out, core.stereo_buf[0], secondaries, 2 ); - return blargg_ok; -} - -blargg_err_t Vgm_Emu::hash_( Hash_Function& out ) const -{ - byte const* p = file_begin() + header().size(); - byte const* e = file_end(); - int data_offset = get_le32( header().data_offset ); - if ( data_offset ) - p += data_offset + offsetof( header_t, data_offset ) - header().size(); - int gd3_offset = get_le32( header().gd3_offset ); - if ( gd3_offset > 0 && gd3_offset + offsetof( header_t, gd3_offset ) > data_offset + offsetof( header_t, data_offset ) ) - e = file_begin() + gd3_offset + offsetof( header_t, gd3_offset ); - hash_vgm_file( header(), p, e - p, out ); - return blargg_ok; -} +Vgm_Emu::~Vgm_Emu() { } + +void Vgm_Emu::unload() +{ + core.unload(); + Classic_Emu::unload(); +} + +// Track info + +static byte const* skip_gd3_str( byte const in [], byte const* end ) +{ + while ( end - in >= 2 ) + { + in += 2; + if ( !(in [-2] | in [-1]) ) + break; + } + return in; +} + +static byte const* get_gd3_str( byte const* in, byte const* end, char field [] ) +{ + byte const* mid = skip_gd3_str( in, end ); + int len = (mid - in) / 2 - 1; + if ( len > 0 ) + { + len = min( len, (int) Gme_File::max_field_ ); + field [len] = 0; + for ( int i = 0; i < len; i++ ) + field [i] = (in [i * 2 + 1] ? '?' : in [i * 2]); // TODO: convert to utf-8 + } + return mid; +} + +static byte const* get_gd3_pair( byte const* in, byte const* end, char field [] ) +{ + return skip_gd3_str( get_gd3_str( in, end, field ), end ); +} + +static void parse_gd3( byte const in [], byte const* end, track_info_t* out ) +{ + in = get_gd3_pair( in, end, out->song ); + in = get_gd3_pair( in, end, out->game ); + in = get_gd3_pair( in, end, out->system ); + in = get_gd3_pair( in, end, out->author ); + in = get_gd3_str ( in, end, out->copyright ); + in = get_gd3_pair( in, end, out->dumper ); + in = get_gd3_str ( in, end, out->comment ); +} + +int const gd3_header_size = 12; + +static int check_gd3_header( byte const h [], int remain ) +{ + if ( remain < gd3_header_size ) return 0; + if ( memcmp( h, "Gd3 ", 4 ) ) return 0; + if ( get_le32( h + 4 ) >= 0x200 ) return 0; + + int gd3_size = get_le32( h + 8 ); + if ( gd3_size > remain - gd3_header_size ) return 0; + + return gd3_size; +} + +static void get_vgm_length( Vgm_Emu::header_t const& h, track_info_t* out ) +{ + int length = get_le32( h.track_duration ) * 10 / 441; // 1000 / 44100 + if ( length > 0 ) + { + int loop = get_le32( h.loop_duration ); + if ( loop > 0 && get_le32( h.loop_offset ) ) + { + out->loop_length = loop * 10 / 441; + out->intro_length = length - out->loop_length; + check( out->loop_length <= length ); + // TODO: Also set out->length? We now have play_length for suggested play time. + } + else + { + out->length = length; + out->intro_length = length; + out->loop_length = 0; + } + } +} + +blargg_err_t Vgm_Emu::track_info_( track_info_t* out, int ) const +{ + get_vgm_length( header(), out ); + + int gd3_offset = get_le32( header().gd3_offset ); + if ( gd3_offset <= 0 ) + return blargg_ok; + + byte const* gd3 = core.file_begin() + gd3_offset + offsetof( header_t, gd3_offset ); + int gd3_size = check_gd3_header( gd3, core.file_end() - gd3 ); + if ( gd3_size ) + { + byte const* gd3_data = gd3 + gd3_header_size; + parse_gd3( gd3_data, gd3_data + gd3_size, out ); + } + + return blargg_ok; +} + +blargg_err_t Vgm_Emu::gd3_data( const unsigned char ** data, int * size ) +{ + *data = 0; + *size = 0; + + int gd3_offset = get_le32( header().gd3_offset ); + if ( gd3_offset <= 0 ) + return blargg_ok; + + byte const* gd3 = core.file_begin() + gd3_offset + offsetof( header_t, gd3_offset ); + int gd3_size = check_gd3_header( gd3, core.file_end() - gd3 ); + if ( gd3_size ) + { + *data = gd3; + *size = gd3_size + gd3_header_size; + } + + return blargg_ok; +} + +static void hash_vgm_file( Vgm_Emu::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out ) +{ + out.hash_( &h.data_size[0], sizeof(h.data_size) ); + out.hash_( &h.version[0], sizeof(h.version) ); + out.hash_( &h.psg_rate[0], sizeof(h.psg_rate) ); + out.hash_( &h.ym2413_rate[0], sizeof(h.ym2413_rate) ); + out.hash_( &h.track_duration[0], sizeof(h.track_duration) ); + out.hash_( &h.loop_offset[0], sizeof(h.loop_offset) ); + out.hash_( &h.loop_duration[0], sizeof(h.loop_duration) ); + out.hash_( &h.frame_rate[0], sizeof(h.frame_rate) ); + out.hash_( &h.noise_feedback[0], sizeof(h.noise_feedback) ); + out.hash_( &h.noise_width, sizeof(h.noise_width) ); + out.hash_( &h.sn76489_flags, sizeof(h.sn76489_flags) ); + out.hash_( &h.ym2612_rate[0], sizeof(h.ym2612_rate) ); + out.hash_( &h.ym2151_rate[0], sizeof(h.ym2151_rate) ); + out.hash_( &h.data_offset[0], sizeof(h.data_offset) ); + out.hash_( &h.segapcm_rate[0], sizeof(h.segapcm_rate) ); + out.hash_( &h.segapcm_reg[0], sizeof(h.segapcm_reg) ); + out.hash_( &h.rf5c68_rate[0], sizeof(h.rf5c68_rate) ); + out.hash_( &h.ym2203_rate[0], sizeof(h.ym2203_rate) ); + out.hash_( &h.ym2608_rate[0], sizeof(h.ym2608_rate) ); + out.hash_( &h.ym2610_rate[0], sizeof(h.ym2610_rate) ); + out.hash_( &h.ym3812_rate[0], sizeof(h.ym3812_rate) ); + out.hash_( &h.ym3526_rate[0], sizeof(h.ym3526_rate) ); + out.hash_( &h.y8950_rate[0], sizeof(h.y8950_rate) ); + out.hash_( &h.ymf262_rate[0], sizeof(h.ymf262_rate) ); + out.hash_( &h.ymf278b_rate[0], sizeof(h.ymf278b_rate) ); + out.hash_( &h.ymf271_rate[0], sizeof(h.ymf271_rate) ); + out.hash_( &h.ymz280b_rate[0], sizeof(h.ymz280b_rate) ); + out.hash_( &h.rf5c164_rate[0], sizeof(h.rf5c164_rate) ); + out.hash_( &h.pwm_rate[0], sizeof(h.pwm_rate) ); + out.hash_( &h.ay8910_rate[0], sizeof(h.ay8910_rate) ); + out.hash_( &h.ay8910_type, sizeof(h.ay8910_type) ); + out.hash_( &h.ay8910_flags, sizeof(h.ay8910_flags) ); + out.hash_( &h.ym2203_ay8910_flags, sizeof(h.ym2203_ay8910_flags) ); + out.hash_( &h.ym2608_ay8910_flags, sizeof(h.ym2608_ay8910_flags) ); + out.hash_( &h.reserved, sizeof(h.reserved) ); + out.hash_( &h.gbdmg_rate[0], sizeof(h.gbdmg_rate) ); + out.hash_( &h.nesapu_rate[0], sizeof(h.nesapu_rate) ); + out.hash_( &h.multipcm_rate[0], sizeof(h.multipcm_rate) ); + out.hash_( &h.upd7759_rate[0], sizeof(h.upd7759_rate) ); + out.hash_( &h.okim6258_rate[0], sizeof(h.okim6258_rate) ); + out.hash_( &h.okim6258_flags, sizeof(h.okim6258_flags) ); + out.hash_( &h.k054539_flags, sizeof(h.k054539_flags) ); + out.hash_( &h.c140_type, sizeof(h.c140_type) ); + out.hash_( &h.reserved_flags, sizeof(h.reserved_flags) ); + out.hash_( &h.okim6295_rate[0], sizeof(h.okim6295_rate) ); + out.hash_( &h.k051649_rate[0], sizeof(h.k051649_rate) ); + out.hash_( &h.k054539_rate[0], sizeof(h.k054539_rate) ); + out.hash_( &h.huc6280_rate[0], sizeof(h.huc6280_rate) ); + out.hash_( &h.c140_rate[0], sizeof(h.c140_rate) ); + out.hash_( &h.k053260_rate[0], sizeof(h.k053260_rate) ); + out.hash_( &h.pokey_rate[0], sizeof(h.pokey_rate) ); + out.hash_( &h.qsound_rate[0], sizeof(h.qsound_rate) ); + out.hash_( &h.reserved2[0], sizeof(h.reserved2) ); + out.hash_( &h.extra_offset[0], sizeof(h.extra_offset) ); + out.hash_( data, data_size ); +} + +struct Vgm_File : Gme_Info_ +{ + Vgm_Emu::header_t h; + blargg_vector data; + blargg_vector gd3; + + Vgm_File() { set_type( gme_vgm_type ); } + + blargg_err_t load_( Data_Reader& in ) + { + int file_size = in.remain(); + if ( file_size <= h.size_min ) + return blargg_err_file_type; + + RETURN_ERR( in.read( &h, h.size_min ) ); + if ( !h.valid_tag() ) + return blargg_err_file_type; + + if ( h.size() > h.size_min ) + RETURN_ERR( in.read( &h.rf5c68_rate, h.size() - h.size_min ) ); + + h.cleanup(); + + int data_offset = get_le32( h.data_offset ) + offsetof( Vgm_Core::header_t, data_offset ); + int data_size = file_size - offsetof( Vgm_Core::header_t, data_offset ) - data_offset; + int gd3_offset = get_le32( h.gd3_offset ); + if ( gd3_offset > 0 ) + gd3_offset += offsetof( Vgm_Core::header_t, gd3_offset ); + + int amount_to_skip = gd3_offset - h.size(); + + if ( gd3_offset > 0 && gd3_offset > data_offset ) + { + data_size = gd3_offset - data_offset; + amount_to_skip = 0; + + RETURN_ERR( data.resize( data_size ) ); + RETURN_ERR( in.skip( data_offset - h.size() ) ); + RETURN_ERR( in.read( data.begin(), data_size ) ); + } + + int remain = file_size - gd3_offset; + byte gd3_h [gd3_header_size]; + if ( gd3_offset > 0 && remain >= gd3_header_size ) + { + RETURN_ERR( in.skip( amount_to_skip ) ); + RETURN_ERR( in.read( gd3_h, sizeof gd3_h ) ); + int gd3_size = check_gd3_header( gd3_h, remain ); + if ( gd3_size ) + { + RETURN_ERR( gd3.resize( gd3_size ) ); + RETURN_ERR( in.read( gd3.begin(), gd3.size() ) ); + } + + if ( data_offset > gd3_offset ) + { + RETURN_ERR( data.resize( data_size ) ); + RETURN_ERR( in.skip( data_offset - gd3_offset - sizeof gd3_h - gd3.size() ) ); + RETURN_ERR( in.read( data.begin(), data.end() - data.begin() ) ); + } + } + + return blargg_ok; + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + get_vgm_length( h, out ); + if ( gd3.size() ) + parse_gd3( gd3.begin(), gd3.end(), out ); + return blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_vgm_file( h, data.begin(), data.end() - data.begin(), out ); + return blargg_ok; + } +}; + +static Music_Emu* new_vgm_emu () { return BLARGG_NEW Vgm_Emu ; } +static Music_Emu* new_vgm_file() { return BLARGG_NEW Vgm_File; } + +gme_type_t_ const gme_vgm_type [1] = {{ "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGM", 1 }}; + +gme_type_t_ const gme_vgz_type [1] = {{ "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGZ", 1 }}; + +// Setup + +void Vgm_Emu::set_tempo_( double t ) +{ + core.set_tempo( t ); +} + +blargg_err_t Vgm_Emu::set_sample_rate_( int sample_rate ) +{ + RETURN_ERR( core.stereo_buf[0].set_sample_rate( sample_rate, 1000 / 30 ) ); + RETURN_ERR( core.stereo_buf[1].set_sample_rate( sample_rate, 1000 / 30 ) ); + RETURN_ERR( core.stereo_buf[2].set_sample_rate( sample_rate, 1000 / 30 ) ); + RETURN_ERR( core.stereo_buf[3].set_sample_rate( sample_rate, 1000 / 30 ) ); + core.set_sample_rate(sample_rate); + return Classic_Emu::set_sample_rate_( sample_rate ); +} + +void Vgm_Emu::update_eq( blip_eq_t const& eq ) +{ + core.psg[0].treble_eq( eq ); + core.psg[1].treble_eq( eq ); + core.ay[0].treble_eq( eq ); + core.ay[1].treble_eq( eq ); + core.huc6280[0].treble_eq( eq ); + core.huc6280[1].treble_eq( eq ); + core.gbdmg[0].treble_eq( eq ); + core.gbdmg[1].treble_eq( eq ); + core.pcm.treble_eq( eq ); +} + +void Vgm_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + if ( i < core.psg[0].osc_count ) + { + core.psg[0].set_output( i, c, l, r ); + core.psg[1].set_output( i, c, l, r ); + } +} + +void Vgm_Emu::mute_voices_( int mask ) +{ + muted_voices = mask; + + Classic_Emu::mute_voices_( mask ); + + // TODO: what was this for? + //core.pcm.output( &core.blip_buf ); + + // TODO: silence PCM if FM isn't used? + if ( core.uses_fm() ) + { + core.psg[0].set_output( ( mask & 0x80 ) ? 0 : core.stereo_buf[0].center() ); + core.psg[1].set_output( ( mask & 0x80 ) ? 0 : core.stereo_buf[0].center() ); + core.ay[0].set_output( ( mask & 0x80 ) ? 0 : core.stereo_buf[1].center() ); + core.ay[1].set_output( ( mask & 0x80 ) ? 0 : core.stereo_buf[1].center() ); + for ( unsigned i = 0, j = 1; i < core.huc6280[0].osc_count; i++, j <<= 1) + { + Blip_Buffer * center = ( mask & j ) ? 0 : core.stereo_buf[2].center(); + Blip_Buffer * left = ( mask & j ) ? 0 : core.stereo_buf[2].left(); + Blip_Buffer * right = ( mask & j ) ? 0 : core.stereo_buf[2].right(); + core.huc6280[0].set_output( i, center, left, right ); + core.huc6280[1].set_output( i, center, left, right ); + } + for (unsigned i = 0, j = 1; i < core.gbdmg[0].osc_count; i++, j <<= 1) + { + Blip_Buffer * center = (mask & j) ? 0 : core.stereo_buf[3].center(); + Blip_Buffer * left = (mask & j) ? 0 : core.stereo_buf[3].left(); + Blip_Buffer * right = (mask & j) ? 0 : core.stereo_buf[3].right(); + core.gbdmg[0].set_output(i, center, left, right); + core.gbdmg[1].set_output(i, center, left, right); + } + if (core.ym2612[0].enabled()) + { + core.pcm.volume( (mask & 0x40) ? 0.0 : 0.1115 / 256 * fm_gain * gain() ); + core.ym2612[0].mute_voices( mask ); + if ( core.ym2612[1].enabled() ) + core.ym2612[1].mute_voices( mask ); + } + + if ( core.ym2413[0].enabled() ) + { + int m = mask & 0x3F; + if ( mask & 0x20 ) + m |= 0x01E0; // channels 5-8 + if ( mask & 0x40 ) + m |= 0x3E00; + core.ym2413[0].mute_voices( m ); + if ( core.ym2413[1].enabled() ) + core.ym2413[1].mute_voices( m ); + } + + if ( core.ym2151[0].enabled() ) + { + core.ym2151[0].mute_voices( mask ); + if ( core.ym2151[1].enabled() ) + core.ym2151[1].mute_voices( mask ); + } + + if ( core.c140.enabled() ) + { + int m = 0; + int m_add = 7; + for ( unsigned i = 0; i < 8; i++, m_add <<= 3 ) + { + if ( mask & ( 1 << i ) ) m += m_add; + } + core.c140.mute_voices( m ); + } + + if ( core.rf5c68.enabled() ) + { + core.rf5c68.mute_voices( mask ); + } + + if ( core.rf5c164.enabled() ) + { + core.rf5c164.mute_voices( mask ); + } + } +} + +blargg_err_t Vgm_Emu::load_mem_( byte const data [], int size ) +{ + RETURN_ERR( core.load_mem( data, size ) ); + + set_voice_count( core.psg[0].osc_count ); + + double fm_rate = 0.0; + if ( !disable_oversampling_ ) + fm_rate = sample_rate() * oversample_factor; + RETURN_ERR( core.init_chips( &fm_rate ) ); + + double psg_gain = ( ( core.header().psg_rate[3] & 0xC0 ) == 0x40 ) ? 0.5 : 1.0; + + if ( core.uses_fm() ) + { + set_voice_count( 8 ); + RETURN_ERR( resampler.setup( fm_rate / sample_rate(), rolloff, gain() ) ); + RETURN_ERR( resampler.reset( core.stereo_buf[0].length() * sample_rate() / 1000 ) ); + core.psg[0].volume( 0.135 * fm_gain * psg_gain * gain() ); + core.psg[1].volume( 0.135 * fm_gain * psg_gain * gain() ); + core.ay[0].volume( 0.135 * fm_gain * gain() ); + core.ay[1].volume( 0.135 * fm_gain * gain() ); + core.huc6280[0].volume( 0.135 * fm_gain * gain() ); + core.huc6280[1].volume( 0.135 * fm_gain * gain() ); + core.gbdmg[0].volume( 0.135 * fm_gain * gain() ); + core.gbdmg[1].volume( 0.135 * fm_gain * gain() ); + } + else + { + core.psg[0].volume( psg_gain * gain() ); + core.psg[1].volume( psg_gain * gain() ); + } + + static const char* const fm_names [] = { + "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG" + }; + static const char* const psg_names [] = { "Square 1", "Square 2", "Square 3", "Noise" }; + set_voice_names( core.uses_fm() ? fm_names : psg_names ); + + static int const types [8] = { + wave_type+1, wave_type+2, wave_type+3, noise_type+1, + 0, 0, 0, 0 + }; + set_voice_types( types ); + + return Classic_Emu::setup_buffer( core.stereo_buf[0].center()->clock_rate() ); +} + +// Emulation + +blargg_err_t Vgm_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + + core.start_track(); + + mute_voices_(muted_voices); + + if ( core.uses_fm() ) + resampler.clear(); + + return blargg_ok; +} + +inline void Vgm_Emu::check_end() +{ + if ( core.track_ended() ) + set_track_ended(); +} + +inline void Vgm_Emu::check_warning() +{ + const char* w = core.warning(); + if ( w ) + set_warning( w ); +} + +blargg_err_t Vgm_Emu::run_clocks( blip_time_t& time_io, int msec ) +{ + check_end(); + time_io = core.run_psg( msec ); + check_warning(); + return blargg_ok; +} + +inline int Vgm_Emu::play_frame( blip_time_t blip_time, int sample_count, sample_t buf [] ) +{ + check_end(); + int result = core.play_frame( blip_time, sample_count, buf ); + check_warning(); + return result; +} + +int Vgm_Emu::play_frame_( void* p, blip_time_t a, int b, sample_t c [] ) +{ + return STATIC_CAST(Vgm_Emu*,p)->play_frame( a, b, c ); +} + +blargg_err_t Vgm_Emu::play_( int count, sample_t out [] ) +{ + if ( !core.uses_fm() ) + return Classic_Emu::play_( count, out ); + + Stereo_Buffer * secondaries[] = { &core.stereo_buf[1], &core.stereo_buf[2], &core.stereo_buf[3] }; + resampler.dual_play( count, out, core.stereo_buf[0], secondaries, 3 ); + return blargg_ok; +} + +blargg_err_t Vgm_Emu::hash_( Hash_Function& out ) const +{ + byte const* p = file_begin() + header().size(); + byte const* e = file_end(); + int data_offset = get_le32( header().data_offset ); + if ( data_offset ) + p += data_offset + offsetof( header_t, data_offset ) - header().size(); + int gd3_offset = get_le32( header().gd3_offset ); + if ( gd3_offset > 0 && gd3_offset + offsetof( header_t, gd3_offset ) > data_offset + offsetof( header_t, data_offset ) ) + e = file_begin() + gd3_offset + offsetof( header_t, gd3_offset ); + hash_vgm_file( header(), p, e - p, out ); + return blargg_ok; +}