// 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.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; int okim6258_hz; 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 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