summaryrefslogtreecommitdiff
path: root/plugins/gme/game-music-emu-0.6pre/gme/Vgm_Core.h
blob: 7f83610c1a0779f3ce769aec6b7fe6f3eb925d6d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// Sega VGM music file emulator core

// Game_Music_Emu 0.6-pre
#ifndef VGM_CORE_H
#define VGM_CORE_H

#include "Gme_Loader.h"
#include "Ym2612_Emu.h"
#include "Ym2413_Emu.h"
#include "Sms_Apu.h"
#include "Multi_Buffer.h"

	template<class Emu>
	class Ym_Emu : public Emu {
		int last_time;
		short* out;
		enum { disabled_time = -1 };
	public:
		Ym_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 = 0x40 };
		
		char tag            [4];
		byte data_size      [4];
		byte version        [4];
		byte psg_rate       [4];
		byte ym2413_rate    [4];
		byte gd3_offset     [4];
		byte track_duration [4];
		byte loop_offset    [4];
		byte loop_duration  [4];
		byte frame_rate     [4];
		byte noise_feedback [2];
		byte noise_width;
		byte unused1;
		byte ym2612_rate    [4];
		byte ym2151_rate    [4];
		byte data_offset    [4];
		byte unused2        [8];
		
		// True if header has valid file signature
		bool valid_tag() const;
	};
	
	// Header for currently loaded file
	header_t const& header() const      { return *(header_t const*) file_begin(); }
	
	// 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_fm( double* sample_rate );
	
	// True if any FM chips are used by file. Always false until init_fm()
	// is called.
	bool uses_fm() const                { return ym2612.enabled() || ym2413.enabled(); }
	
	// 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 );
	
	// 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(); }
	
	// PCM sound is always generated here
	Stereo_Buffer stereo_buf;
	Blip_Buffer * blip_buf;
	
	// PSG sound chip, for assigning to Blip_Buffer, and setting volume and EQ
	Sms_Apu psg;
	
	// PCM synth, for setting volume and EQ
	Blip_Synth_Fast pcm;
	
	// FM sound chips
	Ym_Emu<Ym2612_Emu> ym2612;
	Ym_Emu<Ym2413_Emu> ym2413;

// Implementation
public:
	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 vgm_rate;   // rate of log, 44100 normally, adjusted by tempo
	double fm_rate; // FM samples per second
	
	// 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;
	
	// Current time and position in log
	vgm_time_t vgm_time;
	byte const* pos;
	byte const* loop_begin;
	
	// PCM
	byte const* pcm_data;   // location of PCM data in log
	byte const* pcm_pos;    // current position in PCM data
	int dac_amp;
	int dac_disabled;       // -1 if disabled
	void write_pcm( vgm_time_t, int amp );
	
	blip_time_t run( vgm_time_t );
	int run_ym2413( int time );
	int run_ym2612( int time );
	void update_fm_rates( int* ym2413_rate, int* ym2612_rate ) const;
};

#endif