summaryrefslogtreecommitdiff
path: root/plugins/gme/Game_Music_Emu-0.5.2/gme/Fir_Resampler.h
blob: 339dfce37dc7b36c389490a91b03248fb740b4b9 (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
161
162
163
164
165
166
167
168
169
170
171
// Finite impulse response (FIR) resampler with adjustable FIR size

// Game_Music_Emu 0.5.2
#ifndef FIR_RESAMPLER_H
#define FIR_RESAMPLER_H

#include "blargg_common.h"
#include <string.h>

class Fir_Resampler_ {
public:
	
	// Use Fir_Resampler<width> (below)
	
	// Set input/output resampling ratio and optionally low-pass rolloff and gain.
	// Returns actual ratio used (rounded to internal precision).
	double time_ratio( double factor, double rolloff = 0.999, double gain = 1.0 );
	
	// Current input/output ratio
	double ratio() const { return ratio_; }
	
// Input
	
	typedef short sample_t;
	
	// Resize and clear input buffer
	blargg_err_t buffer_size( int );
	
	// Clear input buffer. At least two output samples will be available after
	// two input samples are written.
	void clear();
	
	// Number of input samples that can be written
	int max_write() const { return buf.end() - write_pos; }
	
	// Pointer to place to write input samples
	sample_t* buffer() { return write_pos; }
	
	// Notify resampler that 'count' input samples have been written
	void write( long count );
	
	// Number of input samples in buffer
	int written() const { return write_pos - &buf [write_offset]; }
	
	// Skip 'count' input samples. Returns number of samples actually skipped.
	int skip_input( long count );
	
// Output
	
	// Number of extra input samples needed until 'count' output samples are available
	int input_needed( blargg_long count ) const;
	
	// Number of output samples available
	int avail() const { return avail_( write_pos - &buf [width_ * stereo] ); }
	
public:
	~Fir_Resampler_();
protected:
	enum { stereo = 2 };
	enum { max_res = 32 };
	blargg_vector<sample_t> buf;
	sample_t* write_pos;
	int res;
	int imp_phase;
	int const width_;
	int const write_offset;
	blargg_ulong skip_bits;
	int step;
	int input_per_cycle;
	double ratio_;
	sample_t* impulses;
	
	Fir_Resampler_( int width, sample_t* );
	int avail_( blargg_long input_count ) const;
};

// Width is number of points in FIR. Must be even and 4 or more. More points give
// better quality and rolloff effectiveness, and take longer to calculate.
template<int width>
class Fir_Resampler : public Fir_Resampler_ {
	BOOST_STATIC_ASSERT( width >= 4 && width % 2 == 0 );
	short impulses [max_res] [width];
public:
	Fir_Resampler() : Fir_Resampler_( width, impulses [0] ) { }
	
	// Read at most 'count' samples. Returns number of samples actually read.
	typedef short sample_t;
	int read( sample_t* out, blargg_long count );
};

// End of public interface

inline void Fir_Resampler_::write( long count )
{
	write_pos += count;
	assert( write_pos <= buf.end() );
}

template<int width>
int Fir_Resampler<width>::read( sample_t* out_begin, blargg_long count )
{
	sample_t* out = out_begin;
	const sample_t* in = buf.begin();
	sample_t* end_pos = write_pos;
	blargg_ulong skip = skip_bits >> imp_phase;
	sample_t const* imp = impulses [imp_phase];
	int remain = res - imp_phase;
	int const step = this->step;
	
	count >>= 1;
	
	if ( end_pos - in >= width * stereo )
	{
		end_pos -= width * stereo;
		do
		{
			count--;
			
			// accumulate in extended precision
			blargg_long l = 0;
			blargg_long r = 0;
			
			const sample_t* i = in;
			if ( count < 0 )
				break;
			
			for ( int n = width / 2; n; --n )
			{
				int pt0 = imp [0];
				l += pt0 * i [0];
				r += pt0 * i [1];
				int pt1 = imp [1];
				imp += 2;
				l += pt1 * i [2];
				r += pt1 * i [3];
				i += 4;
			}
			
			remain--;
			
			l >>= 15;
			r >>= 15;
			
			in += (skip * stereo) & stereo;
			skip >>= 1;
			in += step;
			
			if ( !remain )
			{
				imp = impulses [0];
				skip = skip_bits;
				remain = res;
			}
			
			out [0] = (sample_t) l;
			out [1] = (sample_t) r;
			out += 2;
		}
		while ( in <= end_pos );
	}
	
	imp_phase = res - remain;
	
	int left = write_pos - in;
	write_pos = &buf [left];
	memmove( buf.begin(), in, left * sizeof *in );
	
	return out - out_begin;
}

#endif