summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libgme/track_filter.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libgme/track_filter.c')
-rw-r--r--lib/rbcodec/codecs/libgme/track_filter.c294
1 files changed, 294 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libgme/track_filter.c b/lib/rbcodec/codecs/libgme/track_filter.c
new file mode 100644
index 0000000000..d0d75f2ded
--- /dev/null
+++ b/lib/rbcodec/codecs/libgme/track_filter.c
@@ -0,0 +1,294 @@
1// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
2
3#include "track_filter.h"
4
5/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
6can redistribute it and/or modify it under the terms of the GNU Lesser
7General Public License as published by the Free Software Foundation; either
8version 2.1 of the License, or (at your option) any later version. This
9module is distributed in the hope that it will be useful, but WITHOUT ANY
10WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12details. You should have received a copy of the GNU Lesser General Public
13License along with this module; if not, write to the Free Software Foundation,
14Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
15
16#include "blargg_source.h"
17
18int const fade_block_size = 512;
19int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
20int const silence_threshold = 8;
21
22void track_create( struct Track_Filter* this )
23{
24 this->emu_ = NULL;
25 this->setup_.max_initial = 0;
26 this->setup_.lookahead = 0;
27 this->setup_.max_silence = indefinite_count;
28 this->silence_ignored_ = false;
29 track_stop( this );
30}
31
32blargg_err_t track_init( struct Track_Filter* this, void* emu )
33{
34 this->emu_ = emu;
35 return 0;
36}
37
38static void clear_time_vars( struct Track_Filter* this )
39{
40 this->emu_time = this->buf_remain;
41 this->out_time = 0;
42 this->silence_time = 0;
43 this->silence_count = 0;
44}
45
46void track_stop( struct Track_Filter* this )
47{
48 this->emu_track_ended_ = true;
49 this->track_ended_ = true;
50 this->fade_start = indefinite_count;
51 this->fade_step = 1;
52 this->buf_remain = 0;
53 this->emu_error = NULL;
54 clear_time_vars( this );
55}
56
57blargg_err_t track_start( struct Track_Filter* this )
58{
59 this->emu_error = NULL;
60 track_stop( this );
61
62 this->emu_track_ended_ = false;
63 this->track_ended_ = false;
64
65 if ( !this->silence_ignored_ )
66 {
67 // play until non-silence or end of track
68 while ( this->emu_time < this->setup_.max_initial )
69 {
70 fill_buf( this );
71 if ( this->buf_remain | this->emu_track_ended_ )
72 break;
73 }
74 }
75
76 clear_time_vars( this );
77 return this->emu_error;
78}
79
80static void end_track_if_error( struct Track_Filter* this, blargg_err_t err )
81{
82 if ( err )
83 {
84 this->emu_error = err;
85 this->emu_track_ended_ = true;
86 }
87}
88
89blargg_err_t track_skip( struct Track_Filter* this, int count )
90{
91 this->emu_error = NULL;
92 this->out_time += count;
93
94 // remove from silence and buf first
95 {
96 int n = min( count, this->silence_count );
97 this->silence_count -= n;
98 count -= n;
99
100 n = min( count, this->buf_remain );
101 this->buf_remain -= n;
102 count -= n;
103 }
104
105 if ( count && !this->emu_track_ended_ )
106 {
107 this->emu_time += count;
108 this->silence_time = this->emu_time; // would otherwise be invalid
109 end_track_if_error( this, skip_( this->emu_, count ) );
110 }
111
112 if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
113 this->track_ended_ |= this->emu_track_ended_;
114
115 return this->emu_error;
116}
117
118blargg_err_t skippy_( struct Track_Filter* this, int count )
119{
120 while ( count && !this->emu_track_ended_ )
121 {
122 int n = buf_size;
123 if ( n > count )
124 n = count;
125 count -= n;
126 RETURN_ERR( play_( this->emu_, n, this->buf ) );
127 }
128 return 0;
129}
130
131// Fading
132
133void track_set_fade( struct Track_Filter* this, int start, int length )
134{
135 this->fade_start = start;
136 this->fade_step = length / (fade_block_size * fade_shift);
137 if ( this->fade_step < 1 )
138 this->fade_step = 1;
139}
140
141static bool is_fading( struct Track_Filter* this )
142{
143 return this->out_time >= this->fade_start && this->fade_start != indefinite_count;
144}
145
146// unit / pow( 2.0, (double) x / step )
147static int int_log( int x, int step, int unit )
148{
149 int shift = x / step;
150 int fraction = (x - shift * step) * unit / step;
151 return ((unit - fraction) + (fraction >> 1)) >> shift;
152}
153
154static void handle_fade( struct Track_Filter* this, sample_t out [], int out_count )
155{
156 int i;
157 for ( i = 0; i < out_count; i += fade_block_size )
158 {
159 int const shift = 14;
160 int const unit = 1 << shift;
161 int gain = int_log( (this->out_time + i - this->fade_start) / fade_block_size,
162 this->fade_step, unit );
163 if ( gain < (unit >> fade_shift) )
164 this->track_ended_ = this->emu_track_ended_ = true;
165
166 sample_t* io = &out [i];
167 for ( int count = min( fade_block_size, out_count - i ); count; --count )
168 {
169 *io = (sample_t) ((*io * gain) >> shift);
170 ++io;
171 }
172 }
173}
174
175// Silence detection
176
177static void emu_play( struct Track_Filter* this, sample_t out [], int count )
178{
179 this->emu_time += count;
180 if ( !this->emu_track_ended_ )
181 end_track_if_error( this, play_( this->emu_, count, out ) );
182 else
183 memset( out, 0, count * sizeof *out );
184}
185
186// number of consecutive silent samples at end
187static int count_silence( sample_t begin [], int size )
188{
189 sample_t first = *begin;
190 *begin = silence_threshold * 2; // sentinel
191 sample_t* p = begin + size;
192 while ( (unsigned) (*--p + silence_threshold) <= (unsigned) silence_threshold * 2 ) { }
193 *begin = first;
194 return size - (p - begin);
195}
196
197// fill internal buffer and check it for silence
198void fill_buf( struct Track_Filter* this )
199{
200 assert( !this->buf_remain );
201 if ( !this->emu_track_ended_ )
202 {
203 emu_play( this, this->buf, buf_size );
204 int silence = count_silence( this->buf, buf_size );
205 if ( silence < buf_size )
206 {
207 this->silence_time = this->emu_time - silence;
208 this->buf_remain = buf_size;
209 return;
210 }
211 }
212 this->silence_count += buf_size;
213}
214
215blargg_err_t track_play( struct Track_Filter* this, int out_count, sample_t out [] )
216{
217 this->emu_error = NULL;
218 if ( this->track_ended_ )
219 {
220 memset( out, 0, out_count * sizeof *out );
221 }
222 else
223 {
224 assert( this->emu_time >= this->out_time );
225
226 // prints nifty graph of how far ahead we are when searching for silence
227 //dprintf( "%*s \n", int ((emu_time - out_time) * 7 / 44100), "*" );
228
229 // use any remaining silence samples
230 int pos = 0;
231 if ( this->silence_count )
232 {
233 if ( !this->silence_ignored_ )
234 {
235 // during a run of silence, run emulator at >=2x speed so it gets ahead
236 int ahead_time = this->setup_.lookahead * (this->out_time + out_count - this->silence_time) +
237 this->silence_time;
238 while ( this->emu_time < ahead_time && !(this->buf_remain | this->emu_track_ended_) )
239 fill_buf( this );
240
241 // end track if sufficient silence has been found
242 if ( this->emu_time - this->silence_time > this->setup_.max_silence )
243 {
244 this->track_ended_ = this->emu_track_ended_ = true;
245 this->silence_count = out_count;
246 this->buf_remain = 0;
247 }
248 }
249
250 // fill from remaining silence
251 pos = min( this->silence_count, out_count );
252 memset( out, 0, pos * sizeof *out );
253 this->silence_count -= pos;
254 }
255
256 // use any remaining samples from buffer
257 if ( this->buf_remain )
258 {
259 int n = min( this->buf_remain, (int) (out_count - pos) );
260 memcpy( out + pos, this->buf + (buf_size - this->buf_remain), n * sizeof *out );
261 this->buf_remain -= n;
262 pos += n;
263 }
264
265 // generate remaining samples normally
266 int remain = out_count - pos;
267 if ( remain )
268 {
269 emu_play( this, out + pos, remain );
270 this->track_ended_ |= this->emu_track_ended_;
271
272 if ( this->silence_ignored_ && !is_fading( this ) )
273 {
274 // if left unupdated, ahead_time could become too large
275 this->silence_time = this->emu_time;
276 }
277 else
278 {
279 // check end for a new run of silence
280 int silence = count_silence( out + pos, remain );
281 if ( silence < remain )
282 this->silence_time = this->emu_time - silence;
283
284 if ( this->emu_time - this->silence_time >= buf_size )
285 fill_buf( this ); // cause silence detection on next play()
286 }
287 }
288
289 if ( is_fading( this ) )
290 handle_fade( this, out, out_count );
291 }
292 this->out_time += out_count;
293 return this->emu_error;
294}