comparison Plugins/Input/console/Fir_Resampler.cpp @ 90:252843aac42f trunk

[svn] Import the initial sources for console music support.
author nenolod
date Tue, 01 Nov 2005 19:57:26 -0800
parents
children 84aabc053b6e
comparison
equal deleted inserted replaced
89:feeda0dda3ce 90:252843aac42f
1
2 // Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/
3
4 #include "Fir_Resampler.h"
5
6 #include <stdio.h>
7 #include <string.h>
8 #include <stdlib.h>
9 #include <math.h>
10
11 /* Copyright (C) 2003-2005 Shay Green. This module is free software; you
12 can redistribute it and/or modify it under the terms of the GNU Lesser
13 General Public License as published by the Free Software Foundation; either
14 version 2.1 of the License, or (at your option) any later version. This
15 module is distributed in the hope that it will be useful, but WITHOUT ANY
16 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
18 more details. You should have received a copy of the GNU Lesser General
19 Public License along with this module; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
21
22 #include BLARGG_SOURCE_BEGIN
23
24 static const double pi = 3.1415926535897932384626433832795029L;
25
26 const bool show_impulse = 0;
27
28 class Dsf {
29 double rolloff;
30 double factor;
31 public:
32 Dsf( double r ) : rolloff( r ) {
33 factor = 1.0;
34 //if ( rolloff < 1.0 )
35 // factor = 1.0 / (*this)( 0 );
36 }
37
38 double operator () ( double angle ) const
39 {
40 double const n_harm = 256;
41 angle /= n_harm;
42 double pow_a_n = pow( rolloff, n_harm );
43 double rescale = 1.0 / n_harm;
44
45 double num = 1.0 - rolloff * cos( angle ) -
46 pow_a_n * cos( n_harm * angle ) +
47 pow_a_n * rolloff * cos( (n_harm - 1) * angle );
48 double den = 1 + rolloff * (rolloff - 2 * cos( angle ));
49
50 return (num / den - 1) / n_harm * factor;
51 }
52 };
53
54 template<class T,class Sinc>
55 void gen_sinc( int width, double offset, double spacing, int count, double scale, T* p,
56 const Sinc& sinc )
57 {
58 double range = pi * (width / 2);
59 double step = pi * spacing;
60 double a = -step * (count / 2 - 1);
61 a -= offset * step;
62
63 while ( count-- )
64 {
65 double w = a / range;
66 double y = 0.0;
67 if ( fabs( w ) < 1.0 )
68 {
69 y = cos( pi * w ) * 0.5 + 0.5;
70 y *= sinc( a );
71 }
72
73 *p++ = y * scale;
74 a += step;
75 }
76 }
77
78 static double plain_sinc( double a ) {
79 return fabs( a ) < 0.00001 ? 1.0 : sin( a ) / a;
80 }
81
82 Fir_Resampler::Fir_Resampler()
83 {
84 res = 1;
85 skip_bits = 0;
86 step = 2;
87 buf = NULL;
88 write_pos = NULL;
89 buf_size = 0;
90 }
91
92 Fir_Resampler::~Fir_Resampler() {
93 free( buf );
94 }
95
96 void Fir_Resampler::clear()
97 {
98 imp = 0;
99 if ( buf ) {
100 write_pos = buf + latency;
101 memset( buf, 0, (write_pos - buf) * sizeof *buf );
102 }
103 }
104
105 blargg_err_t Fir_Resampler::buffer_size( int new_size )
106 {
107 new_size += latency;
108 void* new_buf = realloc( buf, new_size * sizeof *buf );
109 if ( !new_buf )
110 return "Out of memory";
111 buf = (sample_t*) new_buf;
112 buf_size = new_size;
113 clear();
114 return blargg_success;
115 }
116
117 double Fir_Resampler::time_ratio( double ratio, double rolloff, double volume )
118 {
119 this->ratio = ratio;
120
121 double fstep = 0.0;
122 {
123 double least_error = 2;
124 double pos = 0;
125 res = -1;
126 for ( int r = 1; r <= max_res; r++ ) {
127 pos += ratio;
128 double nearest = floor( pos + 0.5 );
129 double error = fabs( pos - nearest );
130 if ( error < least_error ) {
131 res = r;
132 fstep = nearest / res;
133 least_error = error;
134 }
135 }
136 }
137
138 skip_bits = 0;
139
140 step = 2 * (int) floor( fstep );
141
142 ratio = fstep;
143 fstep = fmod( fstep, 1.0 );
144
145 double filter = (ratio < 1.0) ? 1.0 : 1.0 / ratio;
146 double pos = 0.0;
147 Dsf dsf( rolloff );
148 for ( int i = 0; i < res; i++ )
149 {
150 if ( show_impulse )
151 printf( "pos = %f\n", pos );
152
153 gen_sinc( int (width * filter + 1) & ~1, pos, filter, (int) width,
154 double (0x7fff * volume * filter), impulses [i], dsf );
155
156 if ( show_impulse ) {
157 for ( int j = 0; j < width; j++ )
158 printf( "%d ", (int) impulses [i] [j] );
159 printf( "\n" );
160 }
161
162 pos += fstep;
163 if ( pos >= 0.9999999 ) {
164 pos -= 1.0;
165 skip_bits |= 1 << i;
166 }
167 }
168
169 if ( show_impulse ) {
170 printf( "skip = %X\n", skip_bits );
171 printf( "step = %d\n", step );
172 }
173
174 clear();
175
176 return ratio;
177 }
178
179 #include BLARGG_ENABLE_OPTIMIZER
180
181 int Fir_Resampler::read( sample_t* out_begin, int count )
182 {
183 sample_t* out = out_begin;
184 const sample_t* in = buf;
185 sample_t* end_pos = write_pos;
186 unsigned long skip = skip_bits >> imp;
187 sample_t const* imp = impulses [this->imp];
188 int remain = res - this->imp;
189 int const step = this->step;
190
191 count = (count >> 1) + 1;
192
193 // to do: optimize loop to use a single counter rather than 'in' and 'count'
194
195 if ( end_pos - in >= width * 2 )
196 {
197 end_pos -= width * 2;
198 do
199 {
200 count--;
201
202 // accumulate in extended precision
203 long l = 0;
204 long r = 0;
205
206 const sample_t* i = in;
207 if ( !count )
208 break;
209
210 for ( int n = width / 2; n--; )
211 {
212 int pt0 = imp [0];
213 int pt1 = imp [1];
214 imp += 2;
215 l += (pt0 * i [0]) + (pt1 * i [2]);
216 r += (pt0 * i [1]) + (pt1 * i [3]);
217 i += 4;
218 }
219
220 remain--;
221
222 l >>= 15;
223 r >>= 15;
224
225 in += step + ((skip * 2) & 2);
226 skip >>= 1;
227
228 if ( !remain ) {
229 imp = impulses [0];
230 skip = skip_bits;
231 remain = res;
232 }
233
234 out [0] = l;
235 out [1] = r;
236 out += 2;
237 }
238 while ( in <= end_pos );
239 }
240
241 this->imp = res - remain;
242
243 int left = write_pos - in;
244 write_pos = buf + left;
245 assert( unsigned (write_pos - buf) <= buf_size );
246 memmove( buf, in, left * sizeof *in );
247
248 return out - out_begin;
249 }
250
251 int Fir_Resampler::skip_input( int count )
252 {
253 int remain = write_pos - buf;
254 int avail = remain - width * 2;
255 if ( count > avail )
256 count = avail;
257
258 remain -= count;
259 write_pos = buf + remain;
260 assert( unsigned (write_pos - buf) <= buf_size );
261 memmove( buf, buf + count, remain * sizeof *buf );
262
263 return count;
264 }
265
266 /*
267 int Fir_Resampler::skip( int count )
268 {
269 count = int (count * ratio) & ~1;
270 count = skip_input( count );
271 return int (count / ratio) & ~1;
272 }
273 */