diff options
Diffstat (limited to 'apps/codecs/libgme/ay_emu.c')
-rw-r--r-- | apps/codecs/libgme/ay_emu.c | 783 |
1 files changed, 783 insertions, 0 deletions
diff --git a/apps/codecs/libgme/ay_emu.c b/apps/codecs/libgme/ay_emu.c new file mode 100644 index 0000000000..dc775cbf79 --- /dev/null +++ b/apps/codecs/libgme/ay_emu.c | |||
@@ -0,0 +1,783 @@ | |||
1 | // Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/ | ||
2 | |||
3 | #include "ay_emu.h" | ||
4 | |||
5 | #include "blargg_endian.h" | ||
6 | |||
7 | /* Copyright (C) 2006-2009 Shay Green. This module is free software; you | ||
8 | can redistribute it and/or modify it under the terms of the GNU Lesser | ||
9 | General Public License as published by the Free Software Foundation; either | ||
10 | version 2.1 of the License, or (at your option) any later version. This | ||
11 | module is distributed in the hope that it will be useful, but WITHOUT ANY | ||
12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
13 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more | ||
14 | details. You should have received a copy of the GNU Lesser General Public | ||
15 | License along with this module; if not, write to the Free Software Foundation, | ||
16 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ | ||
17 | |||
18 | #include "blargg_source.h" | ||
19 | |||
20 | int const stereo = 2; // number of channels for stereo | ||
21 | int const silence_max = 6; // seconds | ||
22 | int const silence_threshold = 0x10; | ||
23 | long const fade_block_size = 512; | ||
24 | int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift) | ||
25 | |||
26 | const char* const gme_wrong_file_type = "Wrong file type for this emulator"; | ||
27 | |||
28 | // TODO: probably don't need detailed errors as to why file is corrupt | ||
29 | |||
30 | int const spectrum_clock = 3546900; // 128K Spectrum | ||
31 | int const spectrum_period = 70908; | ||
32 | |||
33 | //int const spectrum_clock = 3500000; // 48K Spectrum | ||
34 | //int const spectrum_period = 69888; | ||
35 | |||
36 | int const cpc_clock = 2000000; | ||
37 | |||
38 | void clear_track_vars( struct Ay_Emu *this ) | ||
39 | { | ||
40 | this->current_track = -1; | ||
41 | this->out_time = 0; | ||
42 | this->emu_time = 0; | ||
43 | this->emu_track_ended_ = true; | ||
44 | this->track_ended = true; | ||
45 | this->fade_start = INT_MAX / 2 + 1; | ||
46 | this->fade_step = 1; | ||
47 | this->silence_time = 0; | ||
48 | this->silence_count = 0; | ||
49 | this->buf_remain = 0; | ||
50 | /* warning(); // clear warning */ | ||
51 | } | ||
52 | |||
53 | void Ay_init( struct Ay_Emu *this ) | ||
54 | { | ||
55 | this->sample_rate = 0; | ||
56 | this->mute_mask_ = 0; | ||
57 | this->tempo = 1.0; | ||
58 | this->gain = 1.0; | ||
59 | this->track_count = 0; | ||
60 | |||
61 | // defaults | ||
62 | this->max_initial_silence = 2; | ||
63 | this->ignore_silence = false; | ||
64 | |||
65 | this->voice_count = 0; | ||
66 | clear_track_vars( this ); | ||
67 | this->beeper_output = NULL; | ||
68 | disable_beeper( this ); | ||
69 | |||
70 | Ay_apu_init( &this->apu ); | ||
71 | Z80_init( &this->cpu ); | ||
72 | |||
73 | this->silence_lookahead = 6 ; | ||
74 | } | ||
75 | |||
76 | // Track info | ||
77 | |||
78 | // Given pointer to 2-byte offset of data, returns pointer to data, or NULL if | ||
79 | // offset is 0 or there is less than min_size bytes of data available. | ||
80 | static byte const* get_data( struct file_t const* file, byte const ptr [], int min_size ) | ||
81 | { | ||
82 | int offset = (int16_t) get_be16( ptr ); | ||
83 | int pos = ptr - (byte const*) file->header; | ||
84 | int size = file->end - (byte const*) file->header; | ||
85 | assert( (unsigned) pos <= (unsigned) size - 2 ); | ||
86 | int limit = size - min_size; | ||
87 | if ( limit < 0 || !offset || (unsigned) (pos + offset) > (unsigned) limit ) | ||
88 | return NULL; | ||
89 | return ptr + offset; | ||
90 | } | ||
91 | |||
92 | static blargg_err_t parse_header( byte const in [], int size, struct file_t* out ) | ||
93 | { | ||
94 | if ( size < header_size ) | ||
95 | return gme_wrong_file_type; | ||
96 | |||
97 | out->header = (struct header_t const*) in; | ||
98 | out->end = in + size; | ||
99 | struct header_t const* h = (struct header_t const*) in; | ||
100 | if ( memcmp( h->tag, "ZXAYEMUL", 8 ) ) | ||
101 | return gme_wrong_file_type; | ||
102 | |||
103 | out->tracks = get_data( out, h->track_info, (h->max_track + 1) * 4 ); | ||
104 | if ( !out->tracks ) | ||
105 | return "missing track data"; | ||
106 | |||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | long Track_get_length( struct Ay_Emu* this, int n ) | ||
111 | { | ||
112 | long length = 0; | ||
113 | |||
114 | byte const* track_info = get_data( &this->file, this->file.tracks + n * 4 + 2, 6 ); | ||
115 | if ( track_info ) | ||
116 | length = get_be16( track_info + 4 ) * (1000 / 50); // frames to msec | ||
117 | |||
118 | if ( (this->m3u.size > 0) && (n < this->m3u.size) ) { | ||
119 | struct entry_t* entry = &this->m3u.entries [n]; | ||
120 | length = entry->length; | ||
121 | } | ||
122 | |||
123 | if ( length <= 0 ) | ||
124 | length = 120 * 1000; /* 2 minutes */ | ||
125 | |||
126 | return length; | ||
127 | } | ||
128 | |||
129 | // Setup | ||
130 | |||
131 | void change_clock_rate( struct Ay_Emu *this, long rate ) | ||
132 | { | ||
133 | this->clock_rate_ = rate; | ||
134 | Buffer_clock_rate( &this->stereo_buf, rate ); | ||
135 | } | ||
136 | |||
137 | blargg_err_t Ay_load_mem( struct Ay_Emu *this, byte const in [], int size ) | ||
138 | { | ||
139 | assert( offsetof (struct header_t,track_info [2]) == header_size ); | ||
140 | |||
141 | RETURN_ERR( parse_header( in, size, &this->file ) ); | ||
142 | |||
143 | /* if ( file.header->vers > 2 ) | ||
144 | warning( "Unknown file version" ); */ | ||
145 | |||
146 | this->voice_count = ay_osc_count + 1; // +1 for beeper | ||
147 | Ay_apu_volume( &this->apu, this->gain ); | ||
148 | |||
149 | // Setup buffer | ||
150 | change_clock_rate( this, spectrum_clock ); | ||
151 | this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf ); | ||
152 | |||
153 | Sound_set_tempo( this, this->tempo ); | ||
154 | |||
155 | // Remute voices | ||
156 | Sound_mute_voices( this, this->mute_mask_ ); | ||
157 | |||
158 | this->track_count = this->file.header->max_track + 1; | ||
159 | this->m3u.size = 0; | ||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | void set_beeper_output( struct Ay_Emu *this, struct Blip_Buffer* b ) | ||
164 | { | ||
165 | this->beeper_output = b; | ||
166 | if ( b && !this->cpc_mode ) | ||
167 | this->beeper_mask = 0x10; | ||
168 | else | ||
169 | disable_beeper( this ); | ||
170 | } | ||
171 | |||
172 | void set_voice( struct Ay_Emu *this, int i, struct Blip_Buffer* center ) | ||
173 | { | ||
174 | if ( i >= ay_osc_count ) | ||
175 | set_beeper_output( this, center ); | ||
176 | else | ||
177 | Ay_apu_set_output( &this->apu, i, center ); | ||
178 | } | ||
179 | |||
180 | blargg_err_t run_clocks( struct Ay_Emu *this, blip_time_t* duration, int msec ) | ||
181 | { | ||
182 | #if defined(ROCKBOX) | ||
183 | (void) msec; | ||
184 | #endif | ||
185 | |||
186 | cpu_time_t *end = duration; | ||
187 | struct Z80_Cpu* cpu = &this->cpu; | ||
188 | Z80_set_time( cpu, 0 ); | ||
189 | |||
190 | // Since detection of CPC mode will halve clock rate during the frame | ||
191 | // and thus generate up to twice as much sound, we must generate half | ||
192 | // as much until mode is known. | ||
193 | if ( !(this->spectrum_mode | this->cpc_mode) ) | ||
194 | *end /= 2; | ||
195 | |||
196 | while ( Z80_time( cpu ) < *end ) | ||
197 | { | ||
198 | run_cpu( this, min( *end, this->next_play ) ); | ||
199 | |||
200 | if ( Z80_time( cpu ) >= this->next_play ) | ||
201 | { | ||
202 | // next frame | ||
203 | this->next_play += this->play_period; | ||
204 | |||
205 | if ( cpu->r.iff1 ) | ||
206 | { | ||
207 | // interrupt enabled | ||
208 | |||
209 | if ( this->mem.ram [cpu->r.pc] == 0x76 ) | ||
210 | cpu->r.pc++; // advance past HALT instruction | ||
211 | |||
212 | cpu->r.iff1 = 0; | ||
213 | cpu->r.iff2 = 0; | ||
214 | |||
215 | this->mem.ram [--cpu->r.sp] = (byte) (cpu->r.pc >> 8); | ||
216 | this->mem.ram [--cpu->r.sp] = (byte) (cpu->r.pc); | ||
217 | |||
218 | // fixed interrupt | ||
219 | cpu->r.pc = 0x38; | ||
220 | Z80_adjust_time( cpu, 12 ); | ||
221 | |||
222 | if ( cpu->r.im == 2 ) | ||
223 | { | ||
224 | // vectored interrupt | ||
225 | addr_t addr = cpu->r.i * 0x100 + 0xFF; | ||
226 | cpu->r.pc = this->mem.ram [(addr + 1) & 0xFFFF] * 0x100 + this->mem.ram [addr]; | ||
227 | Z80_adjust_time( cpu, 6 ); | ||
228 | } | ||
229 | } | ||
230 | } | ||
231 | } | ||
232 | |||
233 | // End time frame | ||
234 | *end = Z80_time( cpu ); | ||
235 | this->next_play -= *end; | ||
236 | check( this->next_play >= 0 ); | ||
237 | Z80_adjust_time( cpu, -*end ); | ||
238 | Ay_apu_end_frame( &this->apu, *end ); | ||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | // Emulation | ||
243 | |||
244 | void cpu_out_( struct Ay_Emu *this, cpu_time_t time, addr_t addr, int data ) | ||
245 | { | ||
246 | // Spectrum | ||
247 | if ( !this->cpc_mode ) | ||
248 | { | ||
249 | switch ( addr & 0xFEFF ) | ||
250 | { | ||
251 | case 0xFEFD: | ||
252 | this->spectrum_mode = true; | ||
253 | Ay_apu_write_addr( &this->apu, data ); | ||
254 | return; | ||
255 | |||
256 | case 0xBEFD: | ||
257 | this->spectrum_mode = true; | ||
258 | Ay_apu_write_data( &this->apu, time, data ); | ||
259 | return; | ||
260 | } | ||
261 | } | ||
262 | |||
263 | // CPC | ||
264 | if ( !this->spectrum_mode ) | ||
265 | { | ||
266 | switch ( addr >> 8 ) | ||
267 | { | ||
268 | case 0xF6: | ||
269 | switch ( data & 0xC0 ) | ||
270 | { | ||
271 | case 0xC0: | ||
272 | Ay_apu_write_addr( &this->apu, this->cpc_latch ); | ||
273 | goto enable_cpc; | ||
274 | |||
275 | case 0x80: | ||
276 | Ay_apu_write_data( &this->apu, time, this->cpc_latch ); | ||
277 | goto enable_cpc; | ||
278 | } | ||
279 | break; | ||
280 | |||
281 | case 0xF4: | ||
282 | this->cpc_latch = data; | ||
283 | goto enable_cpc; | ||
284 | } | ||
285 | } | ||
286 | |||
287 | /* dprintf( "Unmapped OUT: $%04X <- $%02X\n", addr, data ); */ | ||
288 | return; | ||
289 | |||
290 | enable_cpc: | ||
291 | if ( !this->cpc_mode ) | ||
292 | { | ||
293 | this->cpc_mode = true; | ||
294 | disable_beeper( this ); | ||
295 | |||
296 | change_clock_rate( this, cpc_clock ); | ||
297 | Sound_set_tempo( this, this->tempo ); | ||
298 | } | ||
299 | } | ||
300 | |||
301 | blargg_err_t Ay_set_sample_rate( struct Ay_Emu *this, long rate ) | ||
302 | { | ||
303 | require( !this->sample_rate ); // sample rate can't be changed once set | ||
304 | Buffer_init( &this->stereo_buf ); | ||
305 | RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buf, rate, 1000 / 20 ) ); | ||
306 | |||
307 | // Set buffer bass | ||
308 | Buffer_bass_freq( &this->stereo_buf, 160 ); | ||
309 | |||
310 | this->sample_rate = rate; | ||
311 | return 0; | ||
312 | } | ||
313 | |||
314 | void Sound_mute_voice( struct Ay_Emu *this, int index, bool mute ) | ||
315 | { | ||
316 | require( (unsigned) index < (unsigned) this->voice_count ); | ||
317 | int bit = 1 << index; | ||
318 | int mask = this->mute_mask_ | bit; | ||
319 | if ( !mute ) | ||
320 | mask ^= bit; | ||
321 | Sound_mute_voices( this, mask ); | ||
322 | } | ||
323 | |||
324 | void Sound_mute_voices( struct Ay_Emu *this, int mask ) | ||
325 | { | ||
326 | require( this->sample_rate ); // sample rate must be set first | ||
327 | this->mute_mask_ = mask; | ||
328 | |||
329 | int i; | ||
330 | for ( i = this->voice_count; i--; ) | ||
331 | { | ||
332 | if ( mask & (1 << i) ) | ||
333 | { | ||
334 | set_voice( this, i, 0 ); | ||
335 | } | ||
336 | else | ||
337 | { | ||
338 | struct channel_t ch = Buffer_channel( &this->stereo_buf ); | ||
339 | assert( (ch.center && ch.left && ch.right) || | ||
340 | (!ch.center && !ch.left && !ch.right) ); // all or nothing | ||
341 | set_voice( this, i, ch.center ); | ||
342 | } | ||
343 | } | ||
344 | } | ||
345 | |||
346 | void Sound_set_tempo( struct Ay_Emu *this, double t ) | ||
347 | { | ||
348 | require( this->sample_rate ); // sample rate must be set first | ||
349 | double const min = 0.02; | ||
350 | double const max = 4.00; | ||
351 | if ( t < min ) t = min; | ||
352 | if ( t > max ) t = max; | ||
353 | this->tempo = t; | ||
354 | |||
355 | int p = spectrum_period; | ||
356 | if ( this->clock_rate_ != spectrum_clock ) | ||
357 | p = this->clock_rate_ / 50; | ||
358 | |||
359 | this->play_period = (blip_time_t) (p / t); | ||
360 | } | ||
361 | |||
362 | void fill_buf( struct Ay_Emu *this ) ICODE_ATTR;; | ||
363 | blargg_err_t Ay_start_track( struct Ay_Emu *this, int track ) | ||
364 | { | ||
365 | clear_track_vars( this ); | ||
366 | |||
367 | // Remap track if playlist available | ||
368 | if ( this->m3u.size > 0 ) { | ||
369 | struct entry_t* e = &this->m3u.entries[track]; | ||
370 | track = e->track; | ||
371 | } | ||
372 | |||
373 | this->current_track = track; | ||
374 | Buffer_clear( &this->stereo_buf ); | ||
375 | |||
376 | byte* const mem = this->mem.ram; | ||
377 | |||
378 | memset( mem + 0x0000, 0xC9, 0x100 ); // fill RST vectors with RET | ||
379 | memset( mem + 0x0100, 0xFF, 0x4000 - 0x100 ); | ||
380 | memset( mem + ram_addr, 0x00, mem_size - ram_addr ); | ||
381 | |||
382 | // locate data blocks | ||
383 | byte const* const data = get_data( &this->file, this->file.tracks + track * 4 + 2, 14 ); | ||
384 | if ( !data ) | ||
385 | return "file data missing"; | ||
386 | |||
387 | byte const* const more_data = get_data( &this->file, data + 10, 6 ); | ||
388 | if ( !more_data ) | ||
389 | return "file data missing"; | ||
390 | |||
391 | byte const* blocks = get_data( &this->file, data + 12, 8 ); | ||
392 | if ( !blocks ) | ||
393 | return "file data missing"; | ||
394 | |||
395 | // initial addresses | ||
396 | unsigned addr = get_be16( blocks ); | ||
397 | if ( !addr ) | ||
398 | return "file data missing"; | ||
399 | |||
400 | unsigned init = get_be16( more_data + 2 ); | ||
401 | if ( !init ) | ||
402 | init = addr; | ||
403 | |||
404 | // copy blocks into memory | ||
405 | do | ||
406 | { | ||
407 | blocks += 2; | ||
408 | unsigned len = get_be16( blocks ); blocks += 2; | ||
409 | if ( addr + len > mem_size ) | ||
410 | { | ||
411 | /* warning( "Bad data block size" ); */ | ||
412 | len = mem_size - addr; | ||
413 | } | ||
414 | check( len ); | ||
415 | byte const* in = get_data( &this->file, blocks, 0 ); blocks += 2; | ||
416 | if ( len > (unsigned) (this->file.end - in) ) | ||
417 | { | ||
418 | /* warning( "File data missing" ); */ | ||
419 | len = this->file.end - in; | ||
420 | } | ||
421 | |||
422 | memcpy( mem + addr, in, len ); | ||
423 | |||
424 | if ( this->file.end - blocks < 8 ) | ||
425 | { | ||
426 | /* warning( "File data missing" ); */ | ||
427 | break; | ||
428 | } | ||
429 | } | ||
430 | while ( (addr = get_be16( blocks )) != 0 ); | ||
431 | |||
432 | // copy and configure driver | ||
433 | static byte const passive [] = { | ||
434 | 0xF3, // DI | ||
435 | 0xCD, 0, 0, // CALL init | ||
436 | 0xED, 0x5E, // LOOP: IM 2 | ||
437 | 0xFB, // EI | ||
438 | 0x76, // HALT | ||
439 | 0x18, 0xFA // JR LOOP | ||
440 | }; | ||
441 | static byte const active [] = { | ||
442 | 0xF3, // DI | ||
443 | 0xCD, 0, 0, // CALL init | ||
444 | 0xED, 0x56, // LOOP: IM 1 | ||
445 | 0xFB, // EI | ||
446 | 0x76, // HALT | ||
447 | 0xCD, 0, 0, // CALL play | ||
448 | 0x18, 0xF7 // JR LOOP | ||
449 | }; | ||
450 | memcpy( mem, passive, sizeof passive ); | ||
451 | int const play_addr = get_be16( more_data + 4 ); | ||
452 | if ( play_addr ) | ||
453 | { | ||
454 | memcpy( mem, active, sizeof active ); | ||
455 | mem [ 9] = play_addr; | ||
456 | mem [10] = play_addr >> 8; | ||
457 | } | ||
458 | mem [2] = init; | ||
459 | mem [3] = init >> 8; | ||
460 | |||
461 | mem [0x38] = 0xFB; // Put EI at interrupt vector (followed by RET) | ||
462 | |||
463 | // start at spectrum speed | ||
464 | change_clock_rate( this, spectrum_clock ); | ||
465 | Sound_set_tempo( this, this->tempo ); | ||
466 | |||
467 | struct registers_t r; | ||
468 | memset( &r, 0, sizeof(struct registers_t) ); | ||
469 | |||
470 | r.sp = get_be16( more_data ); | ||
471 | r.b.a = r.b.b = r.b.d = r.b.h = data [8]; | ||
472 | r.b.flags = r.b.c = r.b.e = r.b.l = data [9]; | ||
473 | r.alt.w = r.w; | ||
474 | r.ix = r.iy = r.w.hl; | ||
475 | |||
476 | memset( this->mem.padding1, 0xFF, sizeof this->mem.padding1 ); | ||
477 | |||
478 | int const mirrored = 0x80; // this much is mirrored after end of memory | ||
479 | memset( this->mem.ram + mem_size + mirrored, 0xFF, sizeof this->mem.ram - mem_size - mirrored ); | ||
480 | memcpy( this->mem.ram + mem_size, this->mem.ram, mirrored ); // some code wraps around (ugh) | ||
481 | |||
482 | Z80_reset( &this->cpu, this->mem.padding1, this->mem.padding1 ); | ||
483 | Z80_map_mem( &this->cpu, 0, mem_size, this->mem.ram, this->mem.ram ); | ||
484 | this->cpu.r = r; | ||
485 | |||
486 | this->beeper_delta = (int) (ay_amp_range * 0.8); | ||
487 | this->last_beeper = 0; | ||
488 | this->next_play = this->play_period; | ||
489 | this->spectrum_mode = false; | ||
490 | this->cpc_mode = false; | ||
491 | this->cpc_latch = 0; | ||
492 | set_beeper_output( this, this->beeper_output ); | ||
493 | Ay_apu_reset( &this->apu ); | ||
494 | |||
495 | // a few tunes rely on channels having tone enabled at the beginning | ||
496 | Ay_apu_write_addr( &this->apu, 7 ); | ||
497 | Ay_apu_write_data( &this->apu, 0, 0x38 ); | ||
498 | |||
499 | this->emu_track_ended_ = false; | ||
500 | this->track_ended = false; | ||
501 | |||
502 | if ( !this->ignore_silence ) | ||
503 | { | ||
504 | // play until non-silence or end of track | ||
505 | long end; | ||
506 | for ( end = this->max_initial_silence * stereo * this->sample_rate; this->emu_time < end; ) | ||
507 | { | ||
508 | fill_buf( this ); | ||
509 | if ( this->buf_remain | (int) this->emu_track_ended_ ) | ||
510 | break; | ||
511 | } | ||
512 | |||
513 | this->emu_time = this->buf_remain; | ||
514 | this->out_time = 0; | ||
515 | this->silence_time = 0; | ||
516 | this->silence_count = 0; | ||
517 | } | ||
518 | /* return track_ended() ? warning() : 0; */ | ||
519 | return 0; | ||
520 | } | ||
521 | |||
522 | // Tell/Seek | ||
523 | |||
524 | blargg_long msec_to_samples( blargg_long msec, long sample_rate ) | ||
525 | { | ||
526 | blargg_long sec = msec / 1000; | ||
527 | msec -= sec * 1000; | ||
528 | return (sec * sample_rate + msec * sample_rate / 1000) * stereo; | ||
529 | } | ||
530 | |||
531 | long Track_tell( struct Ay_Emu *this ) | ||
532 | { | ||
533 | blargg_long rate = this->sample_rate * stereo; | ||
534 | blargg_long sec = this->out_time / rate; | ||
535 | return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate; | ||
536 | } | ||
537 | |||
538 | blargg_err_t Track_seek( struct Ay_Emu *this, long msec ) | ||
539 | { | ||
540 | blargg_long time = msec_to_samples( msec, this->sample_rate ); | ||
541 | if ( time < this->out_time ) | ||
542 | RETURN_ERR( Ay_start_track( this, this->current_track ) ); | ||
543 | return Track_skip( this, time - this->out_time ); | ||
544 | } | ||
545 | |||
546 | blargg_err_t play_( struct Ay_Emu *this, long count, sample_t* out ) ICODE_ATTR; | ||
547 | blargg_err_t skip_( struct Ay_Emu *this, long count ) | ||
548 | { | ||
549 | // for long skip, mute sound | ||
550 | const long threshold = 30000; | ||
551 | if ( count > threshold ) | ||
552 | { | ||
553 | int saved_mute = this->mute_mask_; | ||
554 | Sound_mute_voices( this, ~0 ); | ||
555 | |||
556 | while ( count > threshold / 2 && !this->emu_track_ended_ ) | ||
557 | { | ||
558 | RETURN_ERR( play_( this, buf_size, this->buf ) ); | ||
559 | count -= buf_size; | ||
560 | } | ||
561 | |||
562 | Sound_mute_voices( this, saved_mute ); | ||
563 | } | ||
564 | |||
565 | while ( count && !this->emu_track_ended_ ) | ||
566 | { | ||
567 | long n = buf_size; | ||
568 | if ( n > count ) | ||
569 | n = count; | ||
570 | count -= n; | ||
571 | RETURN_ERR( play_( this, n, this->buf ) ); | ||
572 | } | ||
573 | return 0; | ||
574 | } | ||
575 | |||
576 | blargg_err_t Track_skip( struct Ay_Emu *this, long count ) | ||
577 | { | ||
578 | require( this->current_track >= 0 ); // start_track() must have been called already | ||
579 | this->out_time += count; | ||
580 | |||
581 | // remove from silence and buf first | ||
582 | { | ||
583 | long n = min( count, this->silence_count ); | ||
584 | this->silence_count -= n; | ||
585 | count -= n; | ||
586 | |||
587 | n = min( count, this->buf_remain ); | ||
588 | this->buf_remain -= n; | ||
589 | count -= n; | ||
590 | } | ||
591 | |||
592 | if ( count && !this->emu_track_ended_ ) | ||
593 | { | ||
594 | this->emu_time += count; | ||
595 | |||
596 | // End track if error | ||
597 | if ( skip_( this, count ) ) | ||
598 | this->emu_track_ended_ = true; | ||
599 | } | ||
600 | |||
601 | if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended | ||
602 | this->track_ended |= this->emu_track_ended_; | ||
603 | |||
604 | return 0; | ||
605 | } | ||
606 | |||
607 | // Fading | ||
608 | |||
609 | void Track_set_fade( struct Ay_Emu *this, long start_msec, long length_msec ) | ||
610 | { | ||
611 | this->fade_step = this->sample_rate * length_msec / (fade_block_size * fade_shift * 1000 / stereo); | ||
612 | this->fade_start = msec_to_samples( start_msec, this->sample_rate ); | ||
613 | } | ||
614 | |||
615 | // unit / pow( 2.0, (double) x / step ) | ||
616 | static int int_log( blargg_long x, int step, int unit ) | ||
617 | { | ||
618 | int shift = x / step; | ||
619 | int fraction = (x - shift * step) * unit / step; | ||
620 | return ((unit - fraction) + (fraction >> 1)) >> shift; | ||
621 | } | ||
622 | |||
623 | void handle_fade( struct Ay_Emu *this, long out_count, sample_t* out ) | ||
624 | { | ||
625 | int i; | ||
626 | for ( i = 0; i < out_count; i += fade_block_size ) | ||
627 | { | ||
628 | int const shift = 14; | ||
629 | int const unit = 1 << shift; | ||
630 | int gain = int_log( (this->out_time + i - this->fade_start) / fade_block_size, | ||
631 | this->fade_step, unit ); | ||
632 | if ( gain < (unit >> fade_shift) ) | ||
633 | this->track_ended = this->emu_track_ended_ = true; | ||
634 | |||
635 | sample_t* io = &out [i]; | ||
636 | int count; | ||
637 | for ( count = min( fade_block_size, out_count - i ); count; --count ) | ||
638 | { | ||
639 | *io = (sample_t) ((*io * gain) >> shift); | ||
640 | ++io; | ||
641 | } | ||
642 | } | ||
643 | } | ||
644 | |||
645 | // Silence detection | ||
646 | |||
647 | void emu_play( struct Ay_Emu *this, long count, sample_t* out ) | ||
648 | { | ||
649 | check( current_track_ >= 0 ); | ||
650 | this->emu_time += count; | ||
651 | if ( this->current_track >= 0 && !this->emu_track_ended_ ) { | ||
652 | if ( play_( this, count, out ) ) | ||
653 | this->emu_track_ended_ = true; | ||
654 | } | ||
655 | else | ||
656 | memset( out, 0, count * sizeof *out ); | ||
657 | } | ||
658 | |||
659 | // number of consecutive silent samples at end | ||
660 | static long count_silence( sample_t* begin, long size ) | ||
661 | { | ||
662 | sample_t first = *begin; | ||
663 | *begin = silence_threshold; // sentinel | ||
664 | sample_t* p = begin + size; | ||
665 | while ( (unsigned) (*--p + silence_threshold / 2) <= (unsigned) silence_threshold ) { } | ||
666 | *begin = first; | ||
667 | return size - (p - begin); | ||
668 | } | ||
669 | |||
670 | // fill internal buffer and check it for silence | ||
671 | void fill_buf( struct Ay_Emu *this ) | ||
672 | { | ||
673 | assert( !this->buf_remain ); | ||
674 | if ( !this->emu_track_ended_ ) | ||
675 | { | ||
676 | emu_play( this, buf_size, this->buf ); | ||
677 | long silence = count_silence( this->buf, buf_size ); | ||
678 | if ( silence < buf_size ) | ||
679 | { | ||
680 | this->silence_time = this->emu_time - silence; | ||
681 | this->buf_remain = buf_size; | ||
682 | return; | ||
683 | } | ||
684 | } | ||
685 | this->silence_count += buf_size; | ||
686 | } | ||
687 | |||
688 | blargg_err_t Ay_play( struct Ay_Emu *this, long out_count, sample_t* out ) | ||
689 | { | ||
690 | if ( this->track_ended ) | ||
691 | { | ||
692 | memset( out, 0, out_count * sizeof *out ); | ||
693 | } | ||
694 | else | ||
695 | { | ||
696 | require( this->current_track >= 0 ); | ||
697 | require( out_count % stereo == 0 ); | ||
698 | |||
699 | assert( this->emu_time >= this->out_time ); | ||
700 | |||
701 | // prints nifty graph of how far ahead we are when searching for silence | ||
702 | //debug_printf( "%*s \n", int ((emu_time - out_time) * 7 / sample_rate()), "*" ); | ||
703 | |||
704 | long pos = 0; | ||
705 | if ( this->silence_count ) | ||
706 | { | ||
707 | // during a run of silence, run emulator at >=2x speed so it gets ahead | ||
708 | long ahead_time = this->silence_lookahead * (this->out_time + out_count - this->silence_time) + this->silence_time; | ||
709 | while ( this->emu_time < ahead_time && !(this->buf_remain | this->emu_track_ended_) ) | ||
710 | fill_buf( this ); | ||
711 | |||
712 | // fill with silence | ||
713 | pos = min( this->silence_count, out_count ); | ||
714 | memset( out, 0, pos * sizeof *out ); | ||
715 | this->silence_count -= pos; | ||
716 | |||
717 | if ( this->emu_time - this->silence_time > silence_max * stereo * this->sample_rate ) | ||
718 | { | ||
719 | this->track_ended = this->emu_track_ended_ = true; | ||
720 | this->silence_count = 0; | ||
721 | this->buf_remain = 0; | ||
722 | } | ||
723 | } | ||
724 | |||
725 | if ( this->buf_remain ) | ||
726 | { | ||
727 | // empty silence buf | ||
728 | long n = min( this->buf_remain, out_count - pos ); | ||
729 | memcpy( &out [pos], this->buf + (buf_size - this->buf_remain), n * sizeof *out ); | ||
730 | this->buf_remain -= n; | ||
731 | pos += n; | ||
732 | } | ||
733 | |||
734 | // generate remaining samples normally | ||
735 | long remain = out_count - pos; | ||
736 | if ( remain ) | ||
737 | { | ||
738 | emu_play( this, remain, out + pos ); | ||
739 | this->track_ended |= this->emu_track_ended_; | ||
740 | |||
741 | if ( !this->ignore_silence || this->out_time > this->fade_start ) | ||
742 | { | ||
743 | // check end for a new run of silence | ||
744 | long silence = count_silence( out + pos, remain ); | ||
745 | if ( silence < remain ) | ||
746 | this->silence_time = this->emu_time - silence; | ||
747 | |||
748 | if ( this->emu_time - this->silence_time >= buf_size ) | ||
749 | fill_buf( this ); // cause silence detection on next play() | ||
750 | } | ||
751 | } | ||
752 | |||
753 | if ( this->out_time > this->fade_start ) | ||
754 | handle_fade( this, out_count, out ); | ||
755 | } | ||
756 | this->out_time += out_count; | ||
757 | return 0; | ||
758 | } | ||
759 | |||
760 | blargg_err_t play_( struct Ay_Emu *this, long count, sample_t* out ) | ||
761 | { | ||
762 | long remain = count; | ||
763 | while ( remain ) | ||
764 | { | ||
765 | remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain ); | ||
766 | if ( remain ) | ||
767 | { | ||
768 | if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) ) | ||
769 | { | ||
770 | this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf ); | ||
771 | |||
772 | // Remute voices | ||
773 | Sound_mute_voices( this, this->mute_mask_ ); | ||
774 | } | ||
775 | int msec = Buffer_length( &this->stereo_buf ); | ||
776 | blip_time_t clocks_emulated = (blargg_long) msec * this->clock_rate_ / 1000 - 100; | ||
777 | RETURN_ERR( run_clocks( this, &clocks_emulated, msec ) ); | ||
778 | assert( clocks_emulated ); | ||
779 | Buffer_end_frame( &this->stereo_buf, clocks_emulated ); | ||
780 | } | ||
781 | } | ||
782 | return 0; | ||
783 | } | ||