comparison Plugins/Input/console/Fir_Resampler.cpp @ 493:c04dff121e1d trunk

[svn] hostile merge, phase 2: reimport based on new plugin code
author nenolod
date Tue, 24 Jan 2006 20:19:01 -0800
parents
children f12d7e208b43
comparison
equal deleted inserted replaced
492:ccb68bad47b2 493:c04dff121e1d
1
2 // Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
3
4 #include "Fir_Resampler.h"
5
6 #include <string.h>
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <math.h>
10
11 /* Copyright (C) 2004-2006 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 // to do: fix problems with rolloff < 0.99 or so, and rolloff == 1.0, and related problems
25
26 // Sinc impulse genertor
27
28 const bool show_impulse = 0;
29
30 static const double pi = 3.1415926535897932384626433832795029L;
31
32 class Dsf {
33 double rolloff;
34 double factor;
35 public:
36 Dsf( double r ) : rolloff( r )
37 {
38 factor = 1.0;
39 //if ( rolloff < 1.0 )
40 // factor = 1.0 / (*this)( 0 );
41 }
42
43 double operator () ( double angle ) const
44 {
45 double const n_harm = 256;
46 angle /= n_harm;
47 double pow_a_n = pow( rolloff, n_harm );
48 //double rescale = 1.0 / n_harm;
49
50 double num = 1.0 - rolloff * cos( angle ) -
51 pow_a_n * cos( n_harm * angle ) +
52 pow_a_n * rolloff * cos( (n_harm - 1) * angle );
53 double den = 1 + rolloff * (rolloff - 2 * cos( angle ));
54
55 return (num / den - 1) / n_harm * factor;
56 }
57 };
58
59 template<class Sinc>
60 void gen_sinc( int width, double offset, double spacing, int count, double scale, short* p,
61 const Sinc& sinc )
62 {
63 double range = pi * (width / 2);
64 double step = pi * spacing;
65 double a = -step * (count / 2 - 1);
66 a -= offset * step;
67
68 while ( count-- )
69 {
70 double w = a / range;
71 double y = 0.0;
72 if ( fabs( w ) < 1.0 )
73 {
74 double window = cos( pi * w ) * 0.5 + 0.5;
75 y = sinc( a ) * window;
76 }
77
78 *p++ = (short) (y * scale);
79 a += step;
80 }
81 }
82
83 static double plain_sinc( double a )
84 {
85 return fabs( a ) < 0.00001 ? 1.0 : sin( a ) / a;
86 }
87
88 // Fir_Resampler
89
90 Fir_Resampler_::Fir_Resampler_( int width, sample_t* impulses_ ) :
91 width_( width ),
92 write_offset( width * stereo - stereo ),
93 impulses( impulses_ )
94 {
95 write_pos = NULL;
96 res = 1;
97 imp = 0;
98 skip_bits = 0;
99 step = stereo;
100 ratio_ = 1.0;
101 }
102
103 Fir_Resampler_::~Fir_Resampler_()
104 {
105 }
106
107 void Fir_Resampler_::clear()
108 {
109 imp = 0;
110 if ( buf.size() )
111 {
112 write_pos = &buf [write_offset];
113 memset( buf.begin(), 0, write_offset * sizeof buf [0] );
114 }
115 }
116
117 blargg_err_t Fir_Resampler_::buffer_size( int new_size )
118 {
119 BLARGG_RETURN_ERR( buf.resize( new_size + write_offset ) );
120 clear();
121 return blargg_success;
122 }
123
124 double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gain )
125 {
126 ratio_ = new_factor;
127
128 double fstep = 0.0;
129 {
130 double least_error = 2;
131 double pos = 0;
132 res = -1;
133 for ( int r = 1; r <= max_res; r++ )
134 {
135 pos += ratio_;
136 double nearest = floor( pos + 0.5 );
137 double error = fabs( pos - nearest );
138 if ( error < least_error )
139 {
140 res = r;
141 fstep = nearest / res;
142 least_error = error;
143 }
144 }
145 }
146
147 skip_bits = 0;
148
149 step = stereo * (int) floor( fstep );
150
151 ratio_ = fstep;
152 fstep = fmod( fstep, 1.0 );
153
154 double filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_;
155 double pos = 0.0;
156 input_per_cycle = 0;
157 Dsf dsf( rolloff );
158 for ( int i = 0; i < res; i++ )
159 {
160 if ( show_impulse )
161 printf( "pos = %f\n", pos );
162
163 gen_sinc( int (width_ * filter + 1) & ~1, pos, filter, (int) width_,
164 double (0x7fff * gain * filter), impulses + i * width_, dsf );
165
166 if ( show_impulse )
167 {
168 for ( int j = 0; j < width_; j++ )
169 printf( "%d ", (int) impulses [i * width_ + j] );
170 printf( "\n" );
171 }
172
173 pos += fstep;
174 input_per_cycle += step;
175 if ( pos >= 0.9999999 )
176 {
177 pos -= 1.0;
178 skip_bits |= 1 << i;
179 input_per_cycle++;
180 }
181 }
182
183 if ( show_impulse )
184 {
185 printf( "skip = %8lX\n", (long) skip_bits );
186 printf( "step = %d\n", step );
187 }
188
189 clear();
190
191 return ratio_;
192 }
193
194 int Fir_Resampler_::input_needed( long output_count ) const
195 {
196 long input_count = 0;
197
198 unsigned long skip = skip_bits >> imp;
199 int remain = res - imp;
200 while ( (output_count -= 2) > 0 )
201 {
202 input_count += step + (skip & 1) * stereo;
203 skip >>= 1;
204 if ( !--remain )
205 {
206 skip = skip_bits;
207 remain = res;
208 }
209 output_count -= 2;
210 }
211
212 long input_extra = input_count - (write_pos - &buf [(width_ - 1) * stereo]);
213 if ( input_extra < 0 )
214 input_extra = 0;
215 return input_extra;
216 }
217
218 int Fir_Resampler_::avail_( long input_count ) const
219 {
220 int cycle_count = input_count / input_per_cycle;
221 int output_count = cycle_count * res * stereo;
222 input_count -= cycle_count * input_per_cycle;
223
224 unsigned long skip = skip_bits >> imp;
225 int remain = res - imp;
226 while ( input_count >= 0 )
227 {
228 input_count -= step + (skip & 1) * stereo;
229 skip >>= 1;
230 if ( !--remain )
231 {
232 skip = skip_bits;
233 remain = res;
234 }
235 output_count += 2;
236 }
237 return output_count;
238 }
239
240 int Fir_Resampler_::skip_input( long count )
241 {
242 int remain = write_pos - buf.begin();
243 int avail = remain - width_ * stereo;
244 if ( avail < 0 ) avail = 0; // inserted
245 if ( count > avail )
246 count = avail;
247
248 remain -= count;
249 write_pos = &buf [remain];
250 memmove( buf.begin(), &buf [count], remain * sizeof buf [0] );
251
252 return count;
253 }
254