Mercurial > audlegacy
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 |