blob: da91eb03916d5c3b2674d3a806de765f979a62f7 [file] [log] [blame]
Adam Gashlinb73960d2007-02-14 03:34:55 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
Michael Sevakis8552eff2007-07-16 21:03:25 +00008 * $Id$
Adam Gashlinb73960d2007-02-14 03:34:55 +00009 *
Michael Sevakisef1f7d32008-01-24 13:35:13 +000010 * Copyright (C) 2007-2008 Michael Sevakis (jhMikeS)
Adam Gashlinb73960d2007-02-14 03:34:55 +000011 * Copyright (C) 2006-2007 Adam Gashlin (hcs)
12 * Copyright (C) 2004-2007 Shay Green (blargg)
13 * Copyright (C) 2002 Brad Martin
14 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000015 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
Adam Gashlinb73960d2007-02-14 03:34:55 +000019 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25/* lovingly ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
26/* DSP Based on Brad Martin's OpenSPC DSP emulator */
27/* tag reading from sexyspc by John Brawn (John_Brawn@yahoo.com) and others */
Adam Gashlinb73960d2007-02-14 03:34:55 +000028#include "codeclib.h"
Michael Sevakis8552eff2007-07-16 21:03:25 +000029#include "spc/spc_codec.h"
Adam Gashlinb73960d2007-02-14 03:34:55 +000030#include "spc/spc_profiler.h"
31
Michael Sevakis8552eff2007-07-16 21:03:25 +000032CODEC_HEADER
Adam Gashlinb73960d2007-02-14 03:34:55 +000033
34/**************** ID666 parsing ****************/
35
36struct {
37 unsigned char isBinary;
38 char song[32];
39 char game[32];
40 char dumper[16];
41 char comments[32];
42 int day,month,year;
43 unsigned long length;
44 unsigned long fade;
45 char artist[32];
46 unsigned char muted;
47 unsigned char emulator;
48} ID666;
49
50static int LoadID666(unsigned char *buf) {
51 unsigned char *ib=buf;
52 int isbinary = 1;
53 int i;
54
55 memcpy(ID666.song,ib,32);
56 ID666.song[31]=0;
57 ib+=32;
58
59 memcpy(ID666.game,ib,32);
60 ID666.game[31]=0;
61 ib+=32;
62
63 memcpy(ID666.dumper,ib,16);
64 ID666.dumper[15]=0;
65 ib+=16;
66
67 memcpy(ID666.comments,ib,32);
68 ID666.comments[31]=0;
69 ib+=32;
70
71 /* Ok, now comes the fun part. */
72
73 /* Date check */
74 if(ib[2] == '/' && ib[5] == '/' )
75 isbinary = 0;
76
77 /* Reserved bytes check */
78 if(ib[0xD2 - 0x2E - 112] >= '0' &&
79 ib[0xD2 - 0x2E - 112] <= '9' &&
80 ib[0xD3 - 0x2E - 112] == 0x00)
81 isbinary = 0;
82
83 /* is length & fade only digits? */
84 for (i=0;i<8 && (
85 (ib[0xA9 - 0x2E - 112+i]>='0'&&ib[0xA9 - 0x2E - 112+i]<='9') ||
86 ib[0xA9 - 0x2E - 112+i]=='\0');
87 i++);
88 if (i==8) isbinary=0;
89
90 ID666.isBinary = isbinary;
91
92 if(isbinary) {
93 DEBUGF("binary tag detected\n");
94 ID666.year=*ib;
95 ib++;
96 ID666.year|=*ib<<8;
97 ib++;
98 ID666.month=*ib;
99 ib++;
100 ID666.day=*ib;
101 ib++;
102
103 ib+=7;
104
105 ID666.length=*ib;
106 ib++;
107
108 ID666.length|=*ib<<8;
109 ib++;
110
111 ID666.length|=*ib<<16;
112 ID666.length*=1000;
113 ib++;
114
115 ID666.fade=*ib;
116 ib++;
117 ID666.fade|=*ib<<8;
118 ib++;
119 ID666.fade|=*ib<<16;
120 ib++;
121 ID666.fade|=*ib<<24;
122 ib++;
123
124 memcpy(ID666.artist,ib,32);
125 ID666.artist[31]=0;
126 ib+=32;
127
128 ID666.muted=*ib;
129 ib++;
130
131 ID666.emulator=*ib;
132 ib++;
133 } else {
Adam Gashlinb73960d2007-02-14 03:34:55 +0000134 unsigned long tmp;
135 char buf[64];
136
137 DEBUGF("text tag detected\n");
Adam Bootb9a71b32007-03-03 06:08:28 +0000138
139 memcpy(buf, ib, 2);
140 buf[2] = 0;
141 tmp = 0;
142 for (i=0;i<2 && buf[i]>='0' && buf[i]<='9';i++) tmp=tmp*10+buf[i]-'0';
143 ID666.month = tmp;
144 ib+=3;
145
146 memcpy(buf, ib, 2);
147 buf[2] = 0;
148 tmp = 0;
149 for (i=0;i<2 && buf[i]>='0' && buf[i]<='9';i++) tmp=tmp*10+buf[i]-'0';
150 ID666.day = tmp;
151 ib+=3;
152
153 memcpy(buf, ib, 4);
154 buf[4] = 0;
155 tmp = 0;
156 for (i=0;i<4 && buf[i]>='0' && buf[i]<='9';i++) tmp=tmp*10+buf[i]-'0';
157 ID666.year = tmp;
158 ib+=5;
Adam Gashlinb73960d2007-02-14 03:34:55 +0000159
160 memcpy(buf, ib, 3);
161 buf[3] = 0;
162 tmp = 0;
163 for (i=0;i<3 && buf[i]>='0' && buf[i]<='9';i++) tmp=tmp*10+buf[i]-'0';
164 ID666.length = tmp * 1000;
165 ib+=3;
166
167 memcpy(buf, ib, 5);
168 buf[5] = 0;
169 tmp = 0;
170 for (i=0;i<5 && buf[i]>='0' && buf[i]<='9';i++) tmp=tmp*10+buf[i]-'0';
171 ID666.fade = tmp;
172 ib+=5;
173
174 memcpy(ID666.artist,ib,32);
175 ID666.artist[31]=0;
176 ib+=32;
177
178 /*I have no idea if this is right or not.*/
179 ID666.muted=*ib;
180 ib++;
181
182 memcpy(buf, ib, 1);
183 buf[1] = 0;
184 tmp = 0;
185 ib++;
186 }
187 return 1;
188}
189
190/**************** Codec ****************/
Michael Sevakis8552eff2007-07-16 21:03:25 +0000191enum {SAMPLE_RATE = 32000};
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000192static struct Spc_Emu spc_emu IDATA_ATTR CACHEALIGN_ATTR;
193
194#if SPC_DUAL_CORE
195/** Implementations for pipelined dual-core operation **/
196static int spc_emu_thread_stack[DEFAULT_STACK_SIZE/sizeof(int)]
197 CACHEALIGN_ATTR;
198
199static const unsigned char * const spc_emu_thread_name = "spc emu";
200static struct thread_entry *emu_thread_p;
201
202enum
203{
204 SPC_EMU_AUDIO = 0,
205 SPC_EMU_LOAD,
206 SPC_EMU_QUIT,
207};
208
209struct spc_load
210{
211 uint8_t *buf;
212 size_t size;
213};
214
215/* sample queue */
216#define WAV_NUM_CHUNKS 2
217#define WAV_CHUNK_MASK (WAV_NUM_CHUNKS-1)
218struct sample_queue_chunk
219{
220 long id;
221 union
222 {
223 intptr_t data;
224 int32_t audio[WAV_CHUNK_SIZE*2];
225 };
226};
227
228static struct
229{
230 int head, tail;
231 struct semaphore emu_sem_head;
232 struct semaphore emu_sem_tail;
233 struct event emu_evt_reply;
234 intptr_t retval;
235 struct sample_queue_chunk wav_chunk[WAV_NUM_CHUNKS];
Michael Sevakis05099142008-04-06 04:34:57 +0000236} sample_queue SHAREDBSS_ATTR;
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000237
238static inline void samples_release_wrbuf(void)
239{
240 sample_queue.tail++;
241 ci->semaphore_release(&sample_queue.emu_sem_head);
242}
243
244static inline struct sample_queue_chunk * samples_get_wrbuf(void)
245{
246 ci->semaphore_wait(&sample_queue.emu_sem_tail);
247 return &sample_queue.wav_chunk[sample_queue.tail & WAV_CHUNK_MASK];
248}
249
250static inline void samples_release_rdbuf(void)
251{
252 if (sample_queue.head != sample_queue.tail) {
253 sample_queue.head++;
254 }
255
256 ci->semaphore_release(&sample_queue.emu_sem_tail);
257}
258
259static inline int32_t * samples_get_rdbuf(void)
260{
261 ci->semaphore_wait(&sample_queue.emu_sem_head);
262
263 if (ci->stop_codec || ci->new_track)
264 {
265 /* Told to stop. Buffer must be released. */
266 samples_release_rdbuf();
267 return NULL;
268 }
269
270 return sample_queue.wav_chunk[sample_queue.head & WAV_CHUNK_MASK].audio;
271}
272
273static intptr_t emu_thread_send_msg(long id, intptr_t data)
274{
275 struct sample_queue_chunk *chunk;
276 /* Grab an audio output buffer */
277 ci->semaphore_wait(&sample_queue.emu_sem_head);
278 chunk = &sample_queue.wav_chunk[sample_queue.head & WAV_CHUNK_MASK];
279 /* Place a message in it instead of audio */
280 chunk->id = id;
281 chunk->data = data;
282 /* Release it to the emu thread */
283 samples_release_rdbuf();
Michael Sevakisef1f7d32008-01-24 13:35:13 +0000284
285 if (id != SPC_EMU_QUIT) {
286 /* Wait for a response */
287 ci->event_wait(&sample_queue.emu_evt_reply, STATE_SIGNALED);
288 }
289
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000290 return sample_queue.retval;
291}
292
293/* thread function */
294static bool emu_thread_process_msg(struct sample_queue_chunk *chunk)
295{
296 long id = chunk->id;
297 bool ret = id != SPC_EMU_QUIT;
298
299 chunk->id = SPC_EMU_AUDIO; /* Reset chunk type to audio */
300 sample_queue.retval = 0;
301
302 if (id == SPC_EMU_LOAD)
303 {
304 struct spc_load *ld = (struct spc_load *)chunk->data;
305 invalidate_icache();
306 SPC_Init(&spc_emu);
307 sample_queue.retval = SPC_load_spc(&spc_emu, ld->buf, ld->size);
Michael Sevakisef1f7d32008-01-24 13:35:13 +0000308
309 /* Empty the audio queue */
310 /* This is a dirty hack a timeout based wait would make unnescessary but
311 still safe because the other thread is known to be waiting for a reply
312 and is not using the objects. */
313 ci->semaphore_init(&sample_queue.emu_sem_tail, 2, 2);
314 ci->semaphore_init(&sample_queue.emu_sem_head, 2, 0);
315 sample_queue.head = sample_queue.tail = 0;
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000316 }
317
Michael Sevakisef1f7d32008-01-24 13:35:13 +0000318 if (id != SPC_EMU_QUIT) {
319 ci->event_set_state(&sample_queue.emu_evt_reply, STATE_SIGNALED);
320 }
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000321
322 return ret;
323}
324
325static void spc_emu_thread(void)
326{
327 CPU_Init(&spc_emu);
328
329 while (1) {
330 /* get a buffer for output */
331 struct sample_queue_chunk *chunk = samples_get_wrbuf();
332
333 if (chunk->id != SPC_EMU_AUDIO) {
334 /* This chunk doesn't contain audio but a command */
335 if (!emu_thread_process_msg(chunk))
336 break;
337 /* Have to re-get this pointer to keep semaphore counts correct */
338 continue;
339 }
340
341 ENTER_TIMER(render);
342 /* fill samples buffer */
343 if ( SPC_play(&spc_emu, WAV_CHUNK_SIZE*2, chunk->audio) )
344 assert( false );
345 EXIT_TIMER(render);
346
347 /* done so release it to output */
348 samples_release_wrbuf();
349 ci->yield();
350 }
351}
352
353static bool spc_emu_start(void)
354{
355 emu_thread_p = ci->create_thread(spc_emu_thread, spc_emu_thread_stack,
356 sizeof(spc_emu_thread_stack), CREATE_THREAD_FROZEN,
357 spc_emu_thread_name IF_PRIO(, PRIORITY_PLAYBACK), COP);
358
359 if (emu_thread_p == NULL)
360 return false;
361
362 /* Initialize audio queue as full to prevent emu thread from trying to run the
363 emulator before loading something */
364 ci->event_init(&sample_queue.emu_evt_reply,
365 EVENT_AUTOMATIC | STATE_NONSIGNALED);
366 ci->semaphore_init(&sample_queue.emu_sem_tail, 2, 0);
367 ci->semaphore_init(&sample_queue.emu_sem_head, 2, 2);
368 sample_queue.head = 0;
369 sample_queue.tail = 2;
370
371 /* Start it running */
372 ci->thread_thaw(emu_thread_p);
373 return true;
374}
375
376/* load a new program on the emu thread */
377static inline int load_spc_buffer(uint8_t *buf, size_t size)
378{
379 struct spc_load ld = { buf, size };
380 flush_icache();
381 return emu_thread_send_msg(SPC_EMU_LOAD, (intptr_t)&ld);
382}
383
384static inline void spc_emu_quit(void)
385{
Michael Sevakisef1f7d32008-01-24 13:35:13 +0000386 if (emu_thread_p != NULL) {
387 emu_thread_send_msg(SPC_EMU_QUIT, 0);
388 /* Wait for emu thread to be killed */
389 ci->thread_wait(emu_thread_p);
390 invalidate_icache();
391 }
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000392}
393
394static inline bool spc_play_get_samples(int32_t **samples)
395{
396 /* obtain filled samples buffer */
397 *samples = samples_get_rdbuf();
398 return *samples != NULL;
399}
400
401static inline void spc_play_send_samples(int32_t *samples)
402{
403 ci->pcmbuf_insert(samples, samples+WAV_CHUNK_SIZE, WAV_CHUNK_SIZE);
404 /* done with chunk so release it to emu thread */
405 samples_release_rdbuf();
406}
407
408#else /* !SPC_DUAL_CORE */
409/** Implementations for single-core operation **/
410int32_t wav_chunk[WAV_CHUNK_SIZE*2] IBSS_ATTR;
411
412/* load a new program into emu */
413static inline int load_spc_buffer(uint8_t *buf, size_t size)
414{
415 SPC_Init(&spc_emu);
416 return SPC_load_spc(&spc_emu, buf, size);
417}
418
419static inline bool spc_emu_start(void)
420{
421#ifdef CPU_COLDFIRE
422 /* signed integer mode with saturation */
423 coldfire_set_macsr(EMAC_SATURATE);
424#endif
425 CPU_Init(&spc_emu);
426 return true;
427}
428
429static inline void spc_play_send_samples(int32_t *samples)
430{
431 ci->pcmbuf_insert(samples, samples+WAV_CHUNK_SIZE, WAV_CHUNK_SIZE);
432}
433
434#define spc_emu_quit()
435#define samples_release_rdbuf()
436
437static inline bool spc_play_get_samples(int32_t **samples)
438{
439 ENTER_TIMER(render);
440 /* fill samples buffer */
441 if ( SPC_play(&spc_emu,WAV_CHUNK_SIZE*2,wav_chunk) )
442 assert( false );
443 EXIT_TIMER(render);
444 *samples = wav_chunk;
445 return true;
446}
447#endif /* SPC_DUAL_CORE */
Adam Gashlinb73960d2007-02-14 03:34:55 +0000448
449/* The main decoder loop */
450static int play_track( void )
451{
452 int sampleswritten=0;
453
Michael Sevakis8552eff2007-07-16 21:03:25 +0000454 unsigned long fadestartsample = ID666.length*(long long) SAMPLE_RATE/1000;
455 unsigned long fadeendsample = (ID666.length+ID666.fade)*(long long) SAMPLE_RATE/1000;
Adam Gashlinb73960d2007-02-14 03:34:55 +0000456 int fadedec = 0;
457 int fadevol = 0x7fffffffl;
458
459 if (fadeendsample>fadestartsample)
460 fadedec=0x7fffffffl/(fadeendsample-fadestartsample)+1;
461
462 ENTER_TIMER(total);
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000463
Adam Gashlinb73960d2007-02-14 03:34:55 +0000464 while ( 1 )
465 {
466 ci->yield();
467 if (ci->stop_codec || ci->new_track) {
468 break;
469 }
470
471 if (ci->seek_time) {
Michael Sevakis8552eff2007-07-16 21:03:25 +0000472 int curtime = sampleswritten*1000LL/SAMPLE_RATE;
Jens Arnoldf68362a2007-03-17 09:54:28 +0000473 DEBUGF("seek to %ld\ncurrently at %d\n",ci->seek_time,curtime);
Adam Gashlinb73960d2007-02-14 03:34:55 +0000474 if (ci->seek_time < curtime) {
475 DEBUGF("seek backwards = reset\n");
476 ci->seek_complete();
477 return 1;
478 }
479 ci->seek_complete();
480 }
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000481
482 int32_t *samples;
483 if (!spc_play_get_samples(&samples))
484 break;
485
486 sampleswritten += WAV_CHUNK_SIZE;
Adam Gashlinb73960d2007-02-14 03:34:55 +0000487
488 /* is track timed? */
489 if (ci->global_settings->repeat_mode!=REPEAT_ONE && ci->id3->length) {
Michael Sevakis8552eff2007-07-16 21:03:25 +0000490 unsigned long curtime = sampleswritten*1000LL/SAMPLE_RATE;
Adam Gashlinb73960d2007-02-14 03:34:55 +0000491 unsigned long lasttimesample = (sampleswritten-WAV_CHUNK_SIZE);
492
493 /* fade? */
494 if (curtime>ID666.length)
495 {
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000496 #ifdef CPU_COLDFIRE
Michael Sevakis3096d0a2007-02-21 00:18:06 +0000497 /* Have to switch modes to do this */
498 long macsr = coldfire_get_macsr();
499 coldfire_set_macsr(EMAC_SATURATE | EMAC_FRACTIONAL | EMAC_ROUND);
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000500 #endif
Adam Gashlinb73960d2007-02-14 03:34:55 +0000501 int i;
502 for (i=0;i<WAV_CHUNK_SIZE;i++) {
503 if (lasttimesample+i>fadestartsample) {
504 if (fadevol>0) {
Michael Sevakis3096d0a2007-02-21 00:18:06 +0000505 samples[i] = FRACMUL(samples[i], fadevol);
506 samples[i+WAV_CHUNK_SIZE] = FRACMUL(samples[i+WAV_CHUNK_SIZE], fadevol);
Adam Gashlinb73960d2007-02-14 03:34:55 +0000507 } else samples[i]=samples[i+WAV_CHUNK_SIZE]=0;
508 fadevol-=fadedec;
509 }
510 }
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000511 #ifdef CPU_COLDFIRE
Michael Sevakis3096d0a2007-02-21 00:18:06 +0000512 coldfire_set_macsr(macsr);
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000513 #endif
Adam Gashlinb73960d2007-02-14 03:34:55 +0000514 }
515 /* end? */
516 if (lasttimesample>=fadeendsample)
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000517 {
518 samples_release_rdbuf();
Adam Gashlinb73960d2007-02-14 03:34:55 +0000519 break;
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000520 }
Adam Gashlinb73960d2007-02-14 03:34:55 +0000521 }
522
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000523 spc_play_send_samples(samples);
Adam Gashlinb73960d2007-02-14 03:34:55 +0000524
525 if (ci->global_settings->repeat_mode!=REPEAT_ONE)
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000526 ci->set_elapsed(sampleswritten*1000LL/SAMPLE_RATE);
Adam Gashlinb73960d2007-02-14 03:34:55 +0000527 else
528 ci->set_elapsed(0);
529 }
530
531 EXIT_TIMER(total);
Adam Gashlinb73960d2007-02-14 03:34:55 +0000532 return 0;
533}
534
535/* this is the codec entry point */
536enum codec_status codec_main(void)
537{
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000538 enum codec_status stat = CODEC_ERROR;
539
540 if (!spc_emu_start())
541 goto codec_quit;
Michael Sevakisd31162a2007-02-20 10:27:39 +0000542
Adam Gashlinb73960d2007-02-14 03:34:55 +0000543 do
544 {
545 DEBUGF("SPC: next_track\n");
546 if (codec_init()) {
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000547 goto codec_quit;
Adam Gashlinb73960d2007-02-14 03:34:55 +0000548 }
549 DEBUGF("SPC: after init\n");
550
551 ci->configure(DSP_SET_SAMPLE_DEPTH, 24);
Michael Sevakis8552eff2007-07-16 21:03:25 +0000552 ci->configure(DSP_SET_FREQUENCY, SAMPLE_RATE);
Adam Gashlinb73960d2007-02-14 03:34:55 +0000553 ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
554
555 /* wait for track info to load */
556 while (!*ci->taginfo_ready && !ci->stop_codec)
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000557 ci->yield();
Adam Gashlinb73960d2007-02-14 03:34:55 +0000558
Michael Sevakis9b9e2272007-02-26 17:15:04 +0000559 codec_set_replaygain(ci->id3);
560
Adam Gashlinb73960d2007-02-14 03:34:55 +0000561 /* Read the entire file */
562 DEBUGF("SPC: request initial buffer\n");
563 ci->configure(CODEC_SET_FILEBUF_WATERMARK, ci->filesize);
564
565 ci->seek_buffer(0);
566 size_t buffersize;
567 uint8_t* buffer = ci->request_buffer(&buffersize, ci->filesize);
568 if (!buffer) {
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000569 goto codec_quit;
Adam Gashlinb73960d2007-02-14 03:34:55 +0000570 }
571
Jens Arnoldbd5c0ad2007-03-17 10:50:58 +0000572 DEBUGF("SPC: read size = 0x%lx\n",(unsigned long)buffersize);
Adam Gashlinb73960d2007-02-14 03:34:55 +0000573 do
574 {
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000575 if (load_spc_buffer(buffer, buffersize)) {
Adam Gashlinb73960d2007-02-14 03:34:55 +0000576 DEBUGF("SPC load failure\n");
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000577 goto codec_quit;
Adam Gashlinb73960d2007-02-14 03:34:55 +0000578 }
579
580 LoadID666(buffer+0x2e);
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000581
Adam Gashlinb73960d2007-02-14 03:34:55 +0000582 if (ci->global_settings->repeat_mode!=REPEAT_ONE && ID666.length==0) {
583 ID666.length=3*60*1000; /* 3 minutes */
584 ID666.fade=5*1000; /* 5 seconds */
585 }
Adam Gashlinb73960d2007-02-14 03:34:55 +0000586
587 reset_profile_timers();
588 }
Adam Gashlinb73960d2007-02-14 03:34:55 +0000589 while ( play_track() );
590
591 print_timers(ci->id3->path);
592 }
593 while ( ci->request_next_track() );
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000594
595 stat = CODEC_OK;
596
597codec_quit:
598 spc_emu_quit();
Adam Gashlinb73960d2007-02-14 03:34:55 +0000599
Michael Sevakis1a41c8a2007-11-18 19:03:45 +0000600 return stat;
Adam Gashlinb73960d2007-02-14 03:34:55 +0000601}