summaryrefslogtreecommitdiff
path: root/plugins/gme/game-music-emu-0.5.5/gme/Spc_Dsp.h
blob: 1e79870aab6a6cbb9eefa792f5ac3cb55ae6a973 (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
// Super Nintendo (SNES) SPC DSP emulator

// Game_Music_Emu 0.5.5
#ifndef SPC_DSP_H
#define SPC_DSP_H

#include "blargg_common.h"

class Spc_Dsp {
	typedef BOOST::int8_t int8_t;
	typedef BOOST::uint8_t uint8_t;
public:
	
	// Keeps pointer to 64K ram
	Spc_Dsp( uint8_t* ram );
	
	// Mute voice n if bit n (1 << n) of mask is clear.
	enum { voice_count = 8 };
	void mute_voices( int mask );
	
	// Clear state and silence everything.
	void reset();
	
	// Set gain, where 1.0 is normal. When greater than 1.0, output is clamped to
	// the 16-bit sample range.
	void set_gain( double );
	
	// If true, prevent channels and global volumes from being phase-negated
	void disable_surround( bool disable );
	
	// Read/write register 'n', where n ranges from 0 to register_count - 1.
	enum { register_count = 128 };
	int  read ( int n );
	void write( int n, int );
	
	// Run DSP for 'count' samples. Write resulting samples to 'buf' if not NULL.
	void run( long count, short* buf = NULL );
	
	
// End of public interface
private:
	
	struct raw_voice_t {
		int8_t  left_vol;
		int8_t  right_vol;
		uint8_t rate [2];
		uint8_t waveform;
		uint8_t adsr [2];   // envelope rates for attack, decay, and sustain
		uint8_t gain;       // envelope gain (if not using ADSR)
		int8_t  envx;       // current envelope level
		int8_t  outx;       // current sample
		int8_t  unused [6];
	};
	
	struct globals_t {
		int8_t  unused1 [12];
		int8_t  left_volume;        // 0C   Main Volume Left (-.7)
		int8_t  echo_feedback;      // 0D   Echo Feedback (-.7)
		int8_t  unused2 [14];
		int8_t  right_volume;       // 1C   Main Volume Right (-.7)
		int8_t  unused3 [15];
		int8_t  left_echo_volume;   // 2C   Echo Volume Left (-.7)
		uint8_t pitch_mods;         // 2D   Pitch Modulation on/off for each voice
		int8_t  unused4 [14];
		int8_t  right_echo_volume;  // 3C   Echo Volume Right (-.7)
		uint8_t noise_enables;      // 3D   Noise output on/off for each voice
		int8_t  unused5 [14];
		uint8_t key_ons;            // 4C   Key On for each voice
		uint8_t echo_ons;           // 4D   Echo on/off for each voice
		int8_t  unused6 [14];
		uint8_t key_offs;           // 5C   key off for each voice (instantiates release mode)
		uint8_t wave_page;          // 5D   source directory (wave table offsets)
		int8_t  unused7 [14];
		uint8_t flags;              // 6C   flags and noise freq
		uint8_t echo_page;          // 6D
		int8_t  unused8 [14];
		uint8_t wave_ended;         // 7C
		uint8_t echo_delay;         // 7D   ms >> 4
		char    unused9 [2];
	};
	
	union {
		raw_voice_t voice [voice_count];
		uint8_t reg [register_count];
		globals_t g;
	};
	
	uint8_t* const ram;
	
	// Cache of echo FIR values for faster access
	short fir_coeff [voice_count];
	
	// fir_buf [i + 8] == fir_buf [i], to avoid wrap checking in FIR code
	short fir_buf [16] [2];
	int fir_offset; // (0 to 7)
	
	enum { emu_gain_bits = 8 };
	int emu_gain;
	
	int keyed_on; // 8-bits for 8 voices
	int keys;
	
	int echo_ptr;
	int noise_amp;
	int noise;
	int noise_count;
	
	int surround_threshold;
	
	static BOOST::int16_t const gauss [];
	
	enum state_t {
		state_attack,
		state_decay,
		state_sustain,
		state_release
	};
	
	struct voice_t {
		short volume [2];
		short fraction;// 12-bit fractional position
		short interp3; // most recent four decoded samples
		short interp2;
		short interp1;
		short interp0;
		short block_remain; // number of nybbles remaining in current block
		unsigned short addr;
		short block_header; // header byte from current block
		short envcnt;
		short envx;
		short on_cnt;
		short enabled; // 7 if enabled, 31 if disabled
		short envstate;
		short unused; // pad to power of 2
	};
	
	voice_t voice_state [voice_count];
	
	int clock_envelope( int );
};

inline void Spc_Dsp::disable_surround( bool disable ) { surround_threshold = disable ? 0 : -0x7FFF; }

inline void Spc_Dsp::set_gain( double v ) { emu_gain = (int) (v * (1 << emu_gain_bits)); }

inline int Spc_Dsp::read( int i )
{
	assert( (unsigned) i < register_count );
	return reg [i];
}

#endif