summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/spc.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/spc.c')
-rw-r--r--lib/rbcodec/codecs/spc.c586
1 files changed, 586 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/spc.c b/lib/rbcodec/codecs/spc.c
new file mode 100644
index 0000000000..809562e2a0
--- /dev/null
+++ b/lib/rbcodec/codecs/spc.c
@@ -0,0 +1,586 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007-2008 Michael Sevakis (jhMikeS)
11 * Copyright (C) 2006-2007 Adam Gashlin (hcs)
12 * Copyright (C) 2004-2007 Shay Green (blargg)
13 * Copyright (C) 2002 Brad Martin
14 *
15 * 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.
19 *
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 */
28#include "codeclib.h"
29#include "../fracmul.h"
30#include "libspc/spc_codec.h"
31#include "libspc/spc_profiler.h"
32
33CODEC_HEADER
34
35/**************** ID666 parsing ****************/
36
37static struct {
38 unsigned char isBinary;
39 char song[32];
40 char game[32];
41 char dumper[16];
42 char comments[32];
43 int day,month,year;
44 unsigned long length;
45 unsigned long fade;
46 char artist[32];
47 unsigned char muted;
48 unsigned char emulator;
49} ID666;
50
51static int LoadID666(unsigned char *buf) {
52 unsigned char *ib=buf;
53 int isbinary = 1;
54 int i;
55
56 memcpy(ID666.song,ib,32);
57 ID666.song[31]=0;
58 ib+=32;
59
60 memcpy(ID666.game,ib,32);
61 ID666.game[31]=0;
62 ib+=32;
63
64 memcpy(ID666.dumper,ib,16);
65 ID666.dumper[15]=0;
66 ib+=16;
67
68 memcpy(ID666.comments,ib,32);
69 ID666.comments[31]=0;
70 ib+=32;
71
72 /* Ok, now comes the fun part. */
73
74 /* Date check */
75 if(ib[2] == '/' && ib[5] == '/' )
76 isbinary = 0;
77
78 /* Reserved bytes check */
79 if(ib[0xD2 - 0x2E - 112] >= '0' &&
80 ib[0xD2 - 0x2E - 112] <= '9' &&
81 ib[0xD3 - 0x2E - 112] == 0x00)
82 isbinary = 0;
83
84 /* is length & fade only digits? */
85 for (i=0;i<8 && (
86 (ib[0xA9 - 0x2E - 112+i]>='0'&&ib[0xA9 - 0x2E - 112+i]<='9') ||
87 ib[0xA9 - 0x2E - 112+i]=='\0');
88 i++);
89 if (i==8) isbinary=0;
90
91 ID666.isBinary = isbinary;
92
93 if(isbinary) {
94 DEBUGF("binary tag detected\n");
95 ID666.year=*ib;
96 ib++;
97 ID666.year|=*ib<<8;
98 ib++;
99 ID666.month=*ib;
100 ib++;
101 ID666.day=*ib;
102 ib++;
103
104 ib+=7;
105
106 ID666.length=*ib;
107 ib++;
108
109 ID666.length|=*ib<<8;
110 ib++;
111
112 ID666.length|=*ib<<16;
113 ID666.length*=1000;
114 ib++;
115
116 ID666.fade=*ib;
117 ib++;
118 ID666.fade|=*ib<<8;
119 ib++;
120 ID666.fade|=*ib<<16;
121 ib++;
122 ID666.fade|=*ib<<24;
123 ib++;
124
125 memcpy(ID666.artist,ib,32);
126 ID666.artist[31]=0;
127 ib+=32;
128
129 ID666.muted=*ib;
130 ib++;
131
132 ID666.emulator=*ib;
133 ib++;
134 } else {
135 unsigned long tmp;
136 char buf[64];
137
138 DEBUGF("text tag detected\n");
139
140 memcpy(buf, ib, 2);
141 buf[2] = 0;
142 tmp = 0;
143 for (i=0;i<2 && buf[i]>='0' && buf[i]<='9';i++) tmp=tmp*10+buf[i]-'0';
144 ID666.month = tmp;
145 ib+=3;
146
147 memcpy(buf, ib, 2);
148 buf[2] = 0;
149 tmp = 0;
150 for (i=0;i<2 && buf[i]>='0' && buf[i]<='9';i++) tmp=tmp*10+buf[i]-'0';
151 ID666.day = tmp;
152 ib+=3;
153
154 memcpy(buf, ib, 4);
155 buf[4] = 0;
156 tmp = 0;
157 for (i=0;i<4 && buf[i]>='0' && buf[i]<='9';i++) tmp=tmp*10+buf[i]-'0';
158 ID666.year = tmp;
159 ib+=5;
160
161 memcpy(buf, ib, 3);
162 buf[3] = 0;
163 tmp = 0;
164 for (i=0;i<3 && buf[i]>='0' && buf[i]<='9';i++) tmp=tmp*10+buf[i]-'0';
165 ID666.length = tmp * 1000;
166 ib+=3;
167
168 memcpy(buf, ib, 5);
169 buf[5] = 0;
170 tmp = 0;
171 for (i=0;i<5 && buf[i]>='0' && buf[i]<='9';i++) tmp=tmp*10+buf[i]-'0';
172 ID666.fade = tmp;
173 ib+=5;
174
175 memcpy(ID666.artist,ib,32);
176 ID666.artist[31]=0;
177 ib+=32;
178
179 /*I have no idea if this is right or not.*/
180 ID666.muted=*ib;
181 ib++;
182
183 memcpy(buf, ib, 1);
184 buf[1] = 0;
185 tmp = 0;
186 ib++;
187 }
188 return 1;
189}
190
191/**************** Codec ****************/
192enum {SAMPLE_RATE = 32000};
193static struct Spc_Emu spc_emu IBSS_ATTR_SPC CACHEALIGN_ATTR;
194
195#if SPC_DUAL_CORE
196/** Implementations for pipelined dual-core operation **/
197static int spc_emu_thread_stack[DEFAULT_STACK_SIZE/sizeof(int)]
198 CACHEALIGN_ATTR;
199
200static const unsigned char * const spc_emu_thread_name = "spc emu";
201static unsigned int emu_thread_id = 0;
202
203enum
204{
205 SPC_EMU_AUDIO = 0,
206 SPC_EMU_LOAD,
207 SPC_EMU_QUIT,
208};
209
210struct spc_load
211{
212 uint8_t *buf;
213 size_t size;
214};
215
216/* sample queue */
217#define WAV_NUM_CHUNKS 2
218#define WAV_CHUNK_MASK (WAV_NUM_CHUNKS-1)
219struct sample_queue_chunk
220{
221 long id;
222 union
223 {
224 intptr_t data;
225 int32_t audio[WAV_CHUNK_SIZE*2];
226 };
227};
228
229static struct
230{
231 int head, tail;
232 struct semaphore emu_sem_head;
233 struct semaphore emu_sem_tail;
234 struct semaphore emu_evt_reply;
235 intptr_t retval;
236 struct sample_queue_chunk wav_chunk[WAV_NUM_CHUNKS];
237} sample_queue SHAREDBSS_ATTR;
238
239static inline void samples_release_wrbuf(void)
240{
241 sample_queue.tail++;
242 ci->semaphore_release(&sample_queue.emu_sem_head);
243}
244
245static inline struct sample_queue_chunk * samples_get_wrbuf(void)
246{
247 ci->semaphore_wait(&sample_queue.emu_sem_tail, TIMEOUT_BLOCK);
248 return &sample_queue.wav_chunk[sample_queue.tail & WAV_CHUNK_MASK];
249}
250
251static inline void samples_release_rdbuf(void)
252{
253 if (sample_queue.head != sample_queue.tail) {
254 sample_queue.head++;
255 }
256
257 ci->semaphore_release(&sample_queue.emu_sem_tail);
258}
259
260static inline int32_t * samples_get_rdbuf(void)
261{
262 ci->semaphore_wait(&sample_queue.emu_sem_head, TIMEOUT_BLOCK);
263 return sample_queue.wav_chunk[sample_queue.head & WAV_CHUNK_MASK].audio;
264}
265
266static intptr_t emu_thread_send_msg(long id, intptr_t data)
267{
268 struct sample_queue_chunk *chunk;
269 /* Grab an audio output buffer */
270 ci->semaphore_wait(&sample_queue.emu_sem_head, TIMEOUT_BLOCK);
271 chunk = &sample_queue.wav_chunk[sample_queue.head & WAV_CHUNK_MASK];
272 /* Place a message in it instead of audio */
273 chunk->id = id;
274 chunk->data = data;
275 /* Release it to the emu thread */
276 samples_release_rdbuf();
277
278 if (id != SPC_EMU_QUIT) {
279 /* Wait for a response */
280 ci->semaphore_wait(&sample_queue.emu_evt_reply, TIMEOUT_BLOCK);
281 }
282
283 return sample_queue.retval;
284}
285
286/* thread function */
287static bool emu_thread_process_msg(struct sample_queue_chunk *chunk)
288{
289 long id = chunk->id;
290 bool ret = id != SPC_EMU_QUIT;
291
292 chunk->id = SPC_EMU_AUDIO; /* Reset chunk type to audio */
293 sample_queue.retval = 0;
294
295 if (id == SPC_EMU_LOAD)
296 {
297 struct spc_load *ld = (struct spc_load *)chunk->data;
298 ci->commit_discard_dcache();
299 SPC_Init(&spc_emu);
300 sample_queue.retval = SPC_load_spc(&spc_emu, ld->buf, ld->size);
301
302 /* Empty the audio queue */
303 ci->semaphore_release(&sample_queue.emu_sem_tail);
304 ci->semaphore_release(&sample_queue.emu_sem_tail);
305 ci->semaphore_wait(&sample_queue.emu_sem_head, TIMEOUT_NOBLOCK);
306 ci->semaphore_wait(&sample_queue.emu_sem_head, TIMEOUT_NOBLOCK);
307 sample_queue.head = sample_queue.tail = 0;
308 }
309
310 if (id != SPC_EMU_QUIT) {
311 ci->semaphore_release(&sample_queue.emu_evt_reply);
312 }
313
314 return ret;
315}
316
317static void spc_emu_thread(void)
318{
319 CPU_Init(&spc_emu);
320
321 while (1) {
322 /* get a buffer for output */
323 struct sample_queue_chunk *chunk = samples_get_wrbuf();
324
325 if (chunk->id != SPC_EMU_AUDIO) {
326 /* This chunk doesn't contain audio but a command */
327 if (!emu_thread_process_msg(chunk))
328 break;
329 /* Have to re-get this pointer to keep semaphore counts correct */
330 continue;
331 }
332
333 ENTER_TIMER(render);
334 /* fill samples buffer */
335 if ( SPC_play(&spc_emu, WAV_CHUNK_SIZE*2, chunk->audio) )
336 assert( false );
337 EXIT_TIMER(render);
338
339 /* done so release it to output */
340 samples_release_wrbuf();
341 ci->yield();
342 }
343}
344
345static bool spc_emu_start(void)
346{
347 emu_thread_id = ci->create_thread(spc_emu_thread, spc_emu_thread_stack,
348 sizeof(spc_emu_thread_stack), CREATE_THREAD_FROZEN,
349 spc_emu_thread_name IF_PRIO(, PRIORITY_PLAYBACK), COP);
350
351 if (emu_thread_id == 0)
352 return false;
353
354 /* Initialize audio queue as full to prevent emu thread from trying to run the
355 emulator before loading something */
356 ci->semaphore_init(&sample_queue.emu_evt_reply, 1, 0);
357 ci->semaphore_init(&sample_queue.emu_sem_tail, 2, 0);
358 ci->semaphore_init(&sample_queue.emu_sem_head, 2, 2);
359 sample_queue.head = 0;
360 sample_queue.tail = 2;
361
362 /* Start it running */
363 ci->thread_thaw(emu_thread_id);
364 return true;
365}
366
367/* load a new program on the emu thread */
368static inline int load_spc_buffer(uint8_t *buf, size_t size)
369{
370 struct spc_load ld = { buf, size };
371 ci->commit_dcache();
372 return emu_thread_send_msg(SPC_EMU_LOAD, (intptr_t)&ld);
373}
374
375static inline void spc_emu_quit(void)
376{
377 if (emu_thread_id != 0) {
378 emu_thread_send_msg(SPC_EMU_QUIT, 0);
379 /* Wait for emu thread to be killed */
380 ci->thread_wait(emu_thread_id);
381 ci->commit_discard_dcache();
382 }
383}
384
385static inline int32_t * spc_play_get_samples(void)
386{
387 /* obtain filled samples buffer */
388 return samples_get_rdbuf();
389}
390
391static inline void spc_play_send_samples(int32_t *samples)
392{
393 ci->pcmbuf_insert(samples, samples+WAV_CHUNK_SIZE, WAV_CHUNK_SIZE);
394 /* done with chunk so release it to emu thread */
395 samples_release_rdbuf();
396}
397
398#else /* !SPC_DUAL_CORE */
399/** Implementations for single-core operation **/
400static int32_t wav_chunk[WAV_CHUNK_SIZE*2] IBSS_ATTR;
401
402/* load a new program into emu */
403static inline int load_spc_buffer(uint8_t *buf, size_t size)
404{
405 SPC_Init(&spc_emu);
406 return SPC_load_spc(&spc_emu, buf, size);
407}
408
409static inline bool spc_emu_start(void)
410{
411#ifdef CPU_COLDFIRE
412 /* signed integer mode with saturation */
413 coldfire_set_macsr(EMAC_SATURATE);
414#endif
415 CPU_Init(&spc_emu);
416 return true;
417}
418
419static inline void spc_play_send_samples(int32_t *samples)
420{
421 ci->pcmbuf_insert(samples, samples+WAV_CHUNK_SIZE, WAV_CHUNK_SIZE);
422}
423
424#define spc_emu_quit()
425#define samples_release_rdbuf()
426
427static inline int32_t * spc_play_get_samples(void)
428{
429 ENTER_TIMER(render);
430 /* fill samples buffer */
431 if ( SPC_play(&spc_emu,WAV_CHUNK_SIZE*2,wav_chunk) )
432 assert( false );
433 EXIT_TIMER(render);
434 return wav_chunk;
435}
436#endif /* SPC_DUAL_CORE */
437
438/* The main decoder loop */
439static int play_track( void )
440{
441 int sampleswritten=0;
442
443 unsigned long fadestartsample = ID666.length*(long long) SAMPLE_RATE/1000;
444 unsigned long fadeendsample = (ID666.length+ID666.fade)*(long long) SAMPLE_RATE/1000;
445 int fadedec = 0;
446 int fadevol = 0x7fffffffl;
447 intptr_t param;
448
449 if (fadeendsample>fadestartsample)
450 fadedec=0x7fffffffl/(fadeendsample-fadestartsample)+1;
451
452 ENTER_TIMER(total);
453
454 while ( 1 )
455 {
456 enum codec_command_action action = ci->get_command(&param);
457
458 if (action == CODEC_ACTION_HALT)
459 break;
460
461 if (action == CODEC_ACTION_SEEK_TIME) {
462 int curtime = sampleswritten*1000LL/SAMPLE_RATE;
463 DEBUGF("seek to %ld\ncurrently at %d\n", (long)param, curtime);
464 if (param < curtime) {
465 DEBUGF("seek backwards = reset\n");
466 ci->set_elapsed(0);
467 ci->seek_complete();
468 return 1;
469 }
470
471 ci->set_elapsed(curtime);
472 ci->seek_complete();
473 }
474
475 int32_t *samples = spc_play_get_samples();
476
477 sampleswritten += WAV_CHUNK_SIZE;
478
479 /* is track timed? */
480 if (!ci->loop_track() && ci->id3->length) {
481 unsigned long curtime = sampleswritten*1000LL/SAMPLE_RATE;
482 unsigned long lasttimesample = (sampleswritten-WAV_CHUNK_SIZE);
483
484 /* fade? */
485 if (curtime>ID666.length)
486 {
487 #ifdef CPU_COLDFIRE
488 /* Have to switch modes to do this */
489 long macsr = coldfire_get_macsr();
490 coldfire_set_macsr(EMAC_SATURATE | EMAC_FRACTIONAL | EMAC_ROUND);
491 #endif
492 int i;
493 for (i=0;i<WAV_CHUNK_SIZE;i++) {
494 if (lasttimesample+i>fadestartsample) {
495 if (fadevol>0) {
496 samples[i] = FRACMUL(samples[i], fadevol);
497 samples[i+WAV_CHUNK_SIZE] = FRACMUL(samples[i+WAV_CHUNK_SIZE], fadevol);
498 } else samples[i]=samples[i+WAV_CHUNK_SIZE]=0;
499 fadevol-=fadedec;
500 }
501 }
502 #ifdef CPU_COLDFIRE
503 coldfire_set_macsr(macsr);
504 #endif
505 }
506 /* end? */
507 if (lasttimesample>=fadeendsample)
508 {
509 samples_release_rdbuf();
510 break;
511 }
512 }
513
514 spc_play_send_samples(samples);
515
516 if (ci->loop_track())
517 ci->set_elapsed(0);
518 else
519 ci->set_elapsed(sampleswritten*1000LL/SAMPLE_RATE);
520 }
521
522 EXIT_TIMER(total);
523 return 0;
524}
525
526/* this is the codec entry point */
527enum codec_status codec_main(enum codec_entry_call_reason reason)
528{
529 if (reason == CODEC_LOAD) {
530 if (!spc_emu_start())
531 return CODEC_ERROR;
532
533 ci->configure(DSP_SET_SAMPLE_DEPTH, 24);
534 ci->configure(DSP_SET_FREQUENCY, SAMPLE_RATE);
535 ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
536 }
537 else if (reason == CODEC_UNLOAD) {
538 spc_emu_quit();
539 }
540
541 return CODEC_OK;
542}
543
544/* this is called for each file to process */
545enum codec_status codec_run(void)
546{
547 DEBUGF("SPC: next_track\n");
548 if (codec_init())
549 return CODEC_ERROR;
550 DEBUGF("SPC: after init\n");
551
552 codec_set_replaygain(ci->id3);
553
554 /* Read the entire file */
555 DEBUGF("SPC: request initial buffer\n");
556 ci->seek_buffer(0);
557 size_t buffersize;
558 uint8_t* buffer = ci->request_buffer(&buffersize, ci->filesize);
559 if (!buffer)
560 return CODEC_ERROR;
561
562 DEBUGF("SPC: read size = 0x%lx\n",(unsigned long)buffersize);
563 ci->set_elapsed(0);
564
565 do
566 {
567 if (load_spc_buffer(buffer, buffersize)) {
568 DEBUGF("SPC load failure\n");
569 return CODEC_ERROR;
570 }
571
572 LoadID666(buffer+0x2e);
573
574 if (!ci->loop_track() && ID666.length==0) {
575 ID666.length=3*60*1000; /* 3 minutes */
576 ID666.fade=5*1000; /* 5 seconds */
577 }
578
579 reset_profile_timers();
580 }
581 while ( play_track() );
582
583 print_timers(ci->id3->path);
584
585 return CODEC_OK;
586}