347 lines
11 KiB
C
347 lines
11 KiB
C
|
// 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 Emu>
|
||
|
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_Emu> ymf262[2];
|
||
|
Chip_Resampler_Emu<Ym3812_Emu> ym3812[2];
|
||
|
Chip_Resampler_Emu<Ym2612_Emu> ym2612[2];
|
||
|
Chip_Resampler_Emu<Ym2610b_Emu> ym2610[2];
|
||
|
Chip_Resampler_Emu<Ym2608_Emu> ym2608[2];
|
||
|
Chip_Resampler_Emu<Ym2413_Emu> ym2413[2];
|
||
|
Chip_Resampler_Emu<Ym2151_Emu> ym2151[2];
|
||
|
Chip_Resampler_Emu<Ym2203_Emu> ym2203[2];
|
||
|
|
||
|
// PCM sound chips
|
||
|
Chip_Resampler_Emu<C140_Emu> c140;
|
||
|
Chip_Resampler_Emu<SegaPcm_Emu> segapcm;
|
||
|
Chip_Resampler_Emu<Rf5C68_Emu> rf5c68;
|
||
|
Chip_Resampler_Emu<Rf5C164_Emu> rf5c164;
|
||
|
Chip_Resampler_Emu<Pwm_Emu> pwm;
|
||
|
Chip_Resampler_Emu<Okim6258_Emu> okim6258; int okim6258_hz;
|
||
|
Chip_Resampler_Emu<Okim6295_Emu> okim6295[2]; int okim6295_hz;
|
||
|
Chip_Resampler_Emu<K051649_Emu> k051649;
|
||
|
Chip_Resampler_Emu<K053260_Emu> k053260;
|
||
|
Chip_Resampler_Emu<K054539_Emu> k054539;
|
||
|
Chip_Resampler_Emu<Ymz280b_Emu> ymz280b; int ymz280b_hz;
|
||
|
Chip_Resampler_Emu<Qsound_Apu> 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
|