comparison Plugins/Input/console/Gym_Emu.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 "Gym_Emu.h"
5
6 #include "ym2612.h"
7
8 #include <string.h>
9
10 /* Copyright (C) 2003-2005 Shay Green. This module is free software; you
11 can redistribute it and/or modify it under the terms of the GNU Lesser
12 General Public License as published by the Free Software Foundation; either
13 version 2.1 of the License, or (at your option) any later version. This
14 module is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
17 more details. You should have received a copy of the GNU Lesser General
18 Public License along with this module; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
20
21 #include BLARGG_SOURCE_BEGIN
22
23 const long base_clock = 53700300;
24 const long clock_rate = base_clock / 15;
25
26 Gym_Emu::Gym_Emu()
27 {
28 data = NULL;
29 pos = NULL;
30 mem = NULL;
31 pairs_per_frame = 0;
32 }
33
34 Gym_Emu::~Gym_Emu()
35 {
36 unload();
37 }
38
39 void Gym_Emu::unload()
40 {
41 delete [] mem;
42 mem = NULL;
43 data = NULL;
44 pos = NULL;
45 track_ended_ = false;
46 }
47
48 blargg_err_t Gym_Emu::init( long sample_rate, double gain, double oversample_ )
49 {
50 require( oversample_ <= 4.0 );
51
52 blip_eq_t eq( -32, 8000, sample_rate );
53 apu.treble_eq( eq );
54 apu.volume( 0.27 * gain );
55 dac_synth.treble_eq( eq );
56 dac_synth.volume( 0.25 * gain );
57 oversample = resampler.time_ratio( oversample_, 0.990, gain );
58
59 pairs_per_frame = sample_rate / 60;
60 oversamples_per_frame = int (pairs_per_frame * oversample) * 2 + 2;
61 clocks_per_sample = (double) clock_rate / sample_rate;
62
63 BLARGG_RETURN_ERR( resampler.buffer_size( oversamples_per_frame + 256 ) );
64
65 BLARGG_RETURN_ERR( blip_buf.sample_rate( sample_rate, 1000 / 30 ) );
66
67 BLARGG_RETURN_ERR( fm.set_rate( sample_rate * oversample, base_clock / 7 ) );
68
69 blip_buf.clock_rate( clock_rate );
70
71 return blargg_success;
72 }
73
74 void Gym_Emu::mute_voices( int mask )
75 {
76 fm.mute_voices( mask );
77 dac_disabled = mask & 0x40;
78 apu.output( (mask & 0x80) ? NULL : &blip_buf );
79 }
80
81 const char** Gym_Emu::voice_names() const
82 {
83 static const char* names [] = {
84 "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "SN76489"
85 };
86 return names;
87 }
88
89 static blargg_err_t check_header( const Gym_Emu::header_t& h, int* data_offset = NULL )
90 {
91 if ( memcmp( h.tag, "GYMX", 4 ) == 0 )
92 {
93 if ( memcmp( h.packed, "\0\0\0\0", 4 ) != 0 )
94 return "Packed GYM file not supported";
95
96 if ( data_offset )
97 *data_offset = sizeof h;
98 }
99 else if ( h.tag [0] != 0 && h.tag [0] != 1 ) {
100 // not a headerless GYM
101 // to do: more thorough check, or just require a damn header
102 return "Not a GYM file";
103 }
104
105 return blargg_success;
106 }
107
108 blargg_err_t Gym_Emu::load_( const void* file, long data_offset, long file_size )
109 {
110 require( pairs_per_frame );
111
112 data = (const byte*) file + data_offset;
113 data_end = (const byte*) file + file_size;
114
115 loop_begin = NULL;
116 loop_offset = 0;
117 if ( data_offset )
118 {
119 const header_t& h = *(header_t*) file;
120 loop_offset =
121 h.loop [3] * 0x1000000L +
122 h.loop [2] * 0x10000L +
123 h.loop [1] * 0x100L +
124 h.loop [0];
125 }
126
127 track_count_ = 1;
128 voice_count_ = 8;
129 mute_voices( 0 );
130
131 return blargg_success;
132 }
133
134 blargg_err_t Gym_Emu::load( const void* file, long file_size )
135 {
136 unload();
137
138 if ( file_size < sizeof (header_t) )
139 return "Not a GYM file";
140
141 int data_offset = 0;
142 BLARGG_RETURN_ERR( check_header( *(header_t*) file, &data_offset ) );
143
144 return load_( file, data_offset, file_size );
145 }
146
147 blargg_err_t Gym_Emu::load( const header_t& h, Emu_Reader& in )
148 {
149 unload();
150
151 int data_offset = 0;
152 BLARGG_RETURN_ERR( check_header( h, &data_offset ) );
153
154 long file_size = sizeof h + in.remain();
155 mem = new byte [file_size];
156 if ( !mem )
157 return "Out of memory";
158 memcpy( mem, &h, sizeof h );
159 BLARGG_RETURN_ERR( in.read( mem + sizeof h, file_size - sizeof h ) );
160
161 return load_( mem, data_offset, file_size );
162 }
163
164 int Gym_Emu::track_length() const
165 {
166 if ( loop_offset || loop_begin )
167 return 0;
168
169 long time = 0; // 1/60 sec frames
170 const byte* p = data;
171 while ( p < data_end )
172 {
173 switch ( *p++ )
174 {
175 case 0:
176 time++;
177 break;
178
179 case 1:
180 case 2:
181 ++p;
182 case 3:
183 ++p;
184 break;
185
186 default:
187 dprintf( "Bad command: %02X\n", (int) p [-1] );
188 break;
189 }
190 }
191
192 return (time + 30 + 59) / 60;
193 }
194
195 blargg_err_t Gym_Emu::start_track( int )
196 {
197 require( data );
198
199 pos = &data [0];
200 extra_pos = 0;
201 loop_remain = loop_offset;
202
203 prev_dac_count = 0;
204 dac_enabled = false;
205 last_dac = -1;
206
207 fm.reset();
208 apu.reset();
209 blip_buf.clear( false );
210 resampler.clear();
211
212 track_ended_ = false;
213
214 return blargg_success;
215 }
216
217 void Gym_Emu::play_frame( sample_t* out )
218 {
219 parse_frame();
220
221 // run SMS APU and buffer
222 blip_time_t clock_count = (pairs_per_frame + 1 - blip_buf.samples_avail()) *
223 clocks_per_sample;
224 apu.end_frame( clock_count );
225 blip_buf.end_frame( clock_count );
226 assert( unsigned (blip_buf.samples_avail() - pairs_per_frame) <= 4 );
227
228 // run fm
229 const int sample_count = oversamples_per_frame - resampler.written();
230 sample_t* buf = resampler.buffer();
231 memset( buf, 0, sample_count * sizeof *buf );
232 fm.run( buf, sample_count );
233 resampler.write( sample_count );
234 int count = resampler.read( sample_buf, pairs_per_frame * 2 );
235 assert( count <= sample_buf_size );
236 assert( unsigned (count - pairs_per_frame * 2) < 32 );
237
238 // mix outputs
239 mix_samples( out );
240 blip_buf.remove_samples( pairs_per_frame );
241 }
242
243 blargg_err_t Gym_Emu::play( long count, sample_t* out )
244 {
245 require( pos );
246
247 const int samples_per_frame = pairs_per_frame * 2;
248
249 // empty extra buffer
250 if ( extra_pos ) {
251 int n = samples_per_frame - extra_pos;
252 if ( n > count )
253 n = count;
254 memcpy( out, sample_buf + extra_pos, n * sizeof *out );
255 out += n;
256 count -= n;
257 extra_pos = (extra_pos + n) % samples_per_frame;
258 }
259
260 // entire frames
261 while ( count >= samples_per_frame ) {
262 play_frame( out );
263 out += samples_per_frame;
264 count -= samples_per_frame;
265 }
266
267 // extra
268 if ( count ) {
269 play_frame( sample_buf );
270 extra_pos = count;
271 memcpy( out, sample_buf, count * sizeof *out );
272 out += count;
273 }
274
275 return blargg_success;
276 }
277
278 blargg_err_t Gym_Emu::skip( long count )
279 {
280 // to do: figure out why total muting generated access violation on MorphOS
281 const int buf_size = 1024;
282 sample_t buf [buf_size];
283
284 while ( count )
285 {
286 int n = buf_size;
287 if ( n > count )
288 n = count;
289 count -= n;
290 BLARGG_RETURN_ERR( play( n, buf ) );
291 }
292
293 return blargg_success;
294 }
295
296 void Gym_Emu::run_dac( int dac_count )
297 {
298 if ( !dac_disabled )
299 {
300 // Guess beginning and end of sample and adjust rate and buffer position accordingly.
301
302 // count dac samples in next frame
303 int next_dac_count = 0;
304 const byte* p = this->pos;
305 int cmd;
306 while ( (cmd = *p++) != 0 ) {
307 int data = *p++;
308 if ( cmd <= 2 )
309 ++p;
310 if ( cmd == 1 && data == 0x2A )
311 next_dac_count++;
312 }
313
314 // adjust
315 int rate_count = dac_count;
316 int start = 0;
317 if ( !prev_dac_count && next_dac_count && dac_count < next_dac_count ) {
318 rate_count = next_dac_count;
319 start = next_dac_count - dac_count;
320 }
321 else if ( prev_dac_count && !next_dac_count && dac_count < prev_dac_count ) {
322 rate_count = prev_dac_count;
323 }
324
325 // Evenly space samples within buffer section being used
326 Blip_Buffer::resampled_time_t period =
327 blip_buf.resampled_duration( clock_rate / 60 ) / rate_count;
328
329 Blip_Buffer::resampled_time_t time = blip_buf.resampled_time( 0 ) +
330 period * start + (period >> 1);
331
332 int last_dac = this->last_dac;
333 if ( last_dac < 0 )
334 last_dac = dac_buf [0];
335
336 for ( int i = 0; i < dac_count; i++ )
337 {
338 int diff = dac_buf [i] - last_dac;
339 last_dac += diff;
340 dac_synth.offset_resampled( time, diff, &blip_buf );
341 time += period;
342 }
343 this->last_dac = last_dac;
344 }
345
346 int const step = 6 * oversample;
347 int remain = pairs_per_frame * oversample;
348 while ( remain ) {
349 int n = step;
350 if ( n > remain )
351 n = remain;
352 remain -= n;
353 fm.run_timer( n );
354 }
355 }
356
357 void Gym_Emu::parse_frame()
358 {
359 if ( track_ended_ )
360 return;
361
362 int dac_count = 0;
363
364 const byte* pos = this->pos;
365 if ( loop_remain && !--loop_remain )
366 loop_begin = pos; // find loop on first time through sequence
367 int cmd;
368 while ( (cmd = *pos++) != 0 )
369 {
370 int data = *pos++;
371 if ( cmd == 1 ) {
372 int data2 = *pos++;
373 if ( data == 0x2A ) {
374 if ( dac_count < sizeof dac_buf ) {
375 dac_buf [dac_count] = data2;
376 dac_count += dac_enabled;
377 }
378 }
379 else {
380 if ( data == 0x2B )
381 dac_enabled = (data2 & 0x80) != 0;
382
383 fm.write( 0, data );
384 fm.write( 1, data2 );
385 }
386 }
387 else if ( cmd == 2 ) {
388 fm.write( 2, data );
389 fm.write( 3, *pos++ );
390 }
391 else if ( cmd == 3 ) {
392 apu.write_data( 0, data );
393 }
394 else {
395 dprintf( "Bad command: %02X\n", (int) cmd );
396 --pos; // put data back
397 }
398 }
399 // loop
400 if ( pos >= data_end ) {
401 if ( loop_begin )
402 pos = loop_begin;
403 else
404 track_ended_ = true;
405 }
406 this->pos = pos;
407
408 // dac
409 if ( dac_count )
410 run_dac( dac_count );
411 prev_dac_count = dac_count;
412 }
413
414 #include BLARGG_ENABLE_OPTIMIZER
415
416 void Gym_Emu::mix_samples( sample_t* out )
417 {
418 // Mix one frame of Blip_Buffer (SMS APU and PCM) and resampled YM audio
419 Blip_Reader sn;
420 int bass = sn.begin( blip_buf );
421 const sample_t* ym = sample_buf;
422
423 for ( int n = pairs_per_frame; n--; )
424 {
425 int s = sn.read();
426 long l = ym [0] * 2 + s;
427 sn.next( bass );
428 if ( (BOOST::int16_t) l != l )
429 l = 0x7FFF - (l >> 24);
430 long r = ym [1] * 2 + s;
431 ym += 2;
432 out [0] = l;
433 out [1] = r;
434 out += 2;
435 if ( (BOOST::int16_t) r != r )
436 out [-1] = 0x7FFF - (r >> 24);
437 }
438
439 sn.end( blip_buf );
440 }
441