summaryrefslogtreecommitdiff
path: root/plugins/gme/game-music-emu-svn/gme/Nes_Fme7_Apu.h
blob: 97094897795139dc42f0f323586c2e7eab214539 (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
// Sunsoft FME-7 sound emulator

// Game_Music_Emu 0.5.5
#ifndef NES_FME7_APU_H
#define NES_FME7_APU_H

#include "blargg_common.h"
#include "Blip_Buffer.h"

struct fme7_apu_state_t
{
	enum { reg_count = 14 };
	BOOST::uint8_t regs [reg_count];
	BOOST::uint8_t phases [3]; // 0 or 1
	BOOST::uint8_t latch;
	BOOST::uint16_t delays [3]; // a, b, c
};

class Nes_Fme7_Apu : private fme7_apu_state_t {
public:
	// See Nes_Apu.h for reference
	void reset();
	void volume( double );
	void treble_eq( blip_eq_t const& );
	void output( Blip_Buffer* );
	enum { osc_count = 3 };
	void osc_output( int index, Blip_Buffer* );
	void end_frame( blip_time_t );
	void save_state( fme7_apu_state_t* ) const;
	void load_state( fme7_apu_state_t const& );
	
	// Mask and addresses of registers
	enum { addr_mask = 0xE000 };
	enum { data_addr = 0xE000 };
	enum { latch_addr = 0xC000 };
	
	// (addr & addr_mask) == latch_addr
	void write_latch( int );
	
	// (addr & addr_mask) == data_addr
	void write_data( blip_time_t, int data );
	
public:
	Nes_Fme7_Apu();
	BLARGG_DISABLE_NOTHROW
private:
	// noncopyable
	Nes_Fme7_Apu( const Nes_Fme7_Apu& );
	Nes_Fme7_Apu& operator = ( const Nes_Fme7_Apu& );
	
	static unsigned char const amp_table [16];
	
	struct {
		Blip_Buffer* output;
		int last_amp;
	} oscs [osc_count];
	blip_time_t last_time;
	
	enum { amp_range = 192 }; // can be any value; this gives best error/quality tradeoff
	Blip_Synth<blip_good_quality,1> synth;
	
	void run_until( blip_time_t );
};

inline void Nes_Fme7_Apu::volume( double v )
{
	synth.volume( 0.38 / amp_range * v ); // to do: fine-tune
}

inline void Nes_Fme7_Apu::treble_eq( blip_eq_t const& eq )
{
	synth.treble_eq( eq );
}

inline void Nes_Fme7_Apu::osc_output( int i, Blip_Buffer* buf )
{
	assert( (unsigned) i < osc_count );
	oscs [i].output = buf;
}

inline void Nes_Fme7_Apu::output( Blip_Buffer* buf )
{
	for ( int i = 0; i < osc_count; i++ )
		osc_output( i, buf );
}

inline Nes_Fme7_Apu::Nes_Fme7_Apu()
{
	output( NULL );
	volume( 1.0 );
	reset();
}

inline void Nes_Fme7_Apu::write_latch( int data ) { latch = data; }

inline void Nes_Fme7_Apu::write_data( blip_time_t time, int data )
{
	if ( (unsigned) latch >= reg_count )
	{
		#ifdef debug_printf
			debug_printf( "FME7 write to %02X (past end of sound registers)\n", (int) latch );
		#endif
		return;
	}
	
	run_until( time );
	regs [latch] = data;
}

inline void Nes_Fme7_Apu::end_frame( blip_time_t time )
{
	if ( time > last_time )
		run_until( time );
	
	assert( last_time >= time );
	last_time -= time;
}

inline void Nes_Fme7_Apu::save_state( fme7_apu_state_t* out ) const
{
	*out = *this;
}

inline void Nes_Fme7_Apu::load_state( fme7_apu_state_t const& in )
{
	reset();
	fme7_apu_state_t* state = this;
	*state = in;
}

#endif