summaryrefslogtreecommitdiff
path: root/apps/codecs/spc.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/codecs/spc.c')
-rw-r--r--apps/codecs/spc.c309
1 files changed, 276 insertions, 33 deletions
diff --git a/apps/codecs/spc.c b/apps/codecs/spc.c
index f2890cd4a4..ae3026354d 100644
--- a/apps/codecs/spc.c
+++ b/apps/codecs/spc.c
@@ -185,12 +185,253 @@ static int LoadID666(unsigned char *buf) {
185} 185}
186 186
187/**************** Codec ****************/ 187/**************** Codec ****************/
188enum {SAMPLE_RATE = 32000};
189static struct Spc_Emu spc_emu IDATA_ATTR CACHEALIGN_ATTR;
188 190
189static int32_t samples[WAV_CHUNK_SIZE*2] IBSS_ATTR; 191#if SPC_DUAL_CORE
192/** Implementations for pipelined dual-core operation **/
193static int spc_emu_thread_stack[DEFAULT_STACK_SIZE/sizeof(int)]
194 CACHEALIGN_ATTR;
190 195
191static struct Spc_Emu spc_emu IDATA_ATTR; 196static const unsigned char * const spc_emu_thread_name = "spc emu";
197static struct thread_entry *emu_thread_p;
192 198
193enum {SAMPLE_RATE = 32000}; 199enum
200{
201 SPC_EMU_AUDIO = 0,
202 SPC_EMU_LOAD,
203 SPC_EMU_QUIT,
204};
205
206struct spc_load
207{
208 uint8_t *buf;
209 size_t size;
210};
211
212/* sample queue */
213#define WAV_NUM_CHUNKS 2
214#define WAV_CHUNK_MASK (WAV_NUM_CHUNKS-1)
215struct sample_queue_chunk
216{
217 long id;
218 union
219 {
220 intptr_t data;
221 int32_t audio[WAV_CHUNK_SIZE*2];
222 };
223};
224
225static struct
226{
227 int head, tail;
228 struct semaphore emu_sem_head;
229 struct semaphore emu_sem_tail;
230 struct event emu_evt_reply;
231 intptr_t retval;
232 struct sample_queue_chunk wav_chunk[WAV_NUM_CHUNKS];
233} sample_queue NOCACHEBSS_ATTR;
234
235static inline void samples_release_wrbuf(void)
236{
237 sample_queue.tail++;
238 ci->semaphore_release(&sample_queue.emu_sem_head);
239}
240
241static inline struct sample_queue_chunk * samples_get_wrbuf(void)
242{
243 ci->semaphore_wait(&sample_queue.emu_sem_tail);
244 return &sample_queue.wav_chunk[sample_queue.tail & WAV_CHUNK_MASK];
245}
246
247static inline void samples_release_rdbuf(void)
248{
249 if (sample_queue.head != sample_queue.tail) {
250 sample_queue.head++;
251 }
252
253 ci->semaphore_release(&sample_queue.emu_sem_tail);
254}
255
256static inline int32_t * samples_get_rdbuf(void)
257{
258 ci->semaphore_wait(&sample_queue.emu_sem_head);
259
260 if (ci->stop_codec || ci->new_track)
261 {
262 /* Told to stop. Buffer must be released. */
263 samples_release_rdbuf();
264 return NULL;
265 }
266
267 return sample_queue.wav_chunk[sample_queue.head & WAV_CHUNK_MASK].audio;
268}
269
270static intptr_t emu_thread_send_msg(long id, intptr_t data)
271{
272 struct sample_queue_chunk *chunk;
273 /* Grab an audio output buffer */
274 ci->semaphore_wait(&sample_queue.emu_sem_head);
275 chunk = &sample_queue.wav_chunk[sample_queue.head & WAV_CHUNK_MASK];
276 /* Place a message in it instead of audio */
277 chunk->id = id;
278 chunk->data = data;
279 /* Release it to the emu thread */
280 samples_release_rdbuf();
281 /* Wait for a response */
282 ci->event_wait(&sample_queue.emu_evt_reply, STATE_SIGNALED);
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 invalidate_icache();
299 SPC_Init(&spc_emu);
300 sample_queue.retval = SPC_load_spc(&spc_emu, ld->buf, ld->size);
301 }
302
303 /* Empty the audio queue */
304 /* This is a dirty hack a timeout based wait would make unnescessary but
305 still safe because the other thread is known to be waiting for a reply
306 and is not using the objects. */
307 ci->semaphore_init(&sample_queue.emu_sem_tail, 2, 2);
308 ci->semaphore_init(&sample_queue.emu_sem_head, 2, 0);
309 sample_queue.head = sample_queue.tail = 0;
310 ci->event_set_state(&sample_queue.emu_evt_reply, STATE_SIGNALED);
311
312 return ret;
313}
314
315static void spc_emu_thread(void)
316{
317 CPU_Init(&spc_emu);
318
319 while (1) {
320 /* get a buffer for output */
321 struct sample_queue_chunk *chunk = samples_get_wrbuf();
322
323 if (chunk->id != SPC_EMU_AUDIO) {
324 /* This chunk doesn't contain audio but a command */
325 if (!emu_thread_process_msg(chunk))
326 break;
327 /* Have to re-get this pointer to keep semaphore counts correct */
328 continue;
329 }
330
331 ENTER_TIMER(render);
332 /* fill samples buffer */
333 if ( SPC_play(&spc_emu, WAV_CHUNK_SIZE*2, chunk->audio) )
334 assert( false );
335 EXIT_TIMER(render);
336
337 /* done so release it to output */
338 samples_release_wrbuf();
339 ci->yield();
340 }
341}
342
343static bool spc_emu_start(void)
344{
345 emu_thread_p = ci->create_thread(spc_emu_thread, spc_emu_thread_stack,
346 sizeof(spc_emu_thread_stack), CREATE_THREAD_FROZEN,
347 spc_emu_thread_name IF_PRIO(, PRIORITY_PLAYBACK), COP);
348
349 if (emu_thread_p == NULL)
350 return false;
351
352 /* Initialize audio queue as full to prevent emu thread from trying to run the
353 emulator before loading something */
354 ci->event_init(&sample_queue.emu_evt_reply,
355 EVENT_AUTOMATIC | STATE_NONSIGNALED);
356 ci->semaphore_init(&sample_queue.emu_sem_tail, 2, 0);
357 ci->semaphore_init(&sample_queue.emu_sem_head, 2, 2);
358 sample_queue.head = 0;
359 sample_queue.tail = 2;
360
361 /* Start it running */
362 ci->thread_thaw(emu_thread_p);
363 return true;
364}
365
366/* load a new program on the emu thread */
367static inline int load_spc_buffer(uint8_t *buf, size_t size)
368{
369 struct spc_load ld = { buf, size };
370 flush_icache();
371 return emu_thread_send_msg(SPC_EMU_LOAD, (intptr_t)&ld);
372}
373
374static inline void spc_emu_quit(void)
375{
376 emu_thread_send_msg(SPC_EMU_QUIT, 0);
377 /* Wait for emu thread to be killed */
378 ci->thread_wait(emu_thread_p);
379}
380
381static inline bool spc_play_get_samples(int32_t **samples)
382{
383 /* obtain filled samples buffer */
384 *samples = samples_get_rdbuf();
385 return *samples != NULL;
386}
387
388static inline void spc_play_send_samples(int32_t *samples)
389{
390 ci->pcmbuf_insert(samples, samples+WAV_CHUNK_SIZE, WAV_CHUNK_SIZE);
391 /* done with chunk so release it to emu thread */
392 samples_release_rdbuf();
393}
394
395#else /* !SPC_DUAL_CORE */
396/** Implementations for single-core operation **/
397int32_t wav_chunk[WAV_CHUNK_SIZE*2] IBSS_ATTR;
398
399/* load a new program into emu */
400static inline int load_spc_buffer(uint8_t *buf, size_t size)
401{
402 SPC_Init(&spc_emu);
403 return SPC_load_spc(&spc_emu, buf, size);
404}
405
406static inline bool spc_emu_start(void)
407{
408#ifdef CPU_COLDFIRE
409 /* signed integer mode with saturation */
410 coldfire_set_macsr(EMAC_SATURATE);
411#endif
412 CPU_Init(&spc_emu);
413 return true;
414}
415
416static inline void spc_play_send_samples(int32_t *samples)
417{
418 ci->pcmbuf_insert(samples, samples+WAV_CHUNK_SIZE, WAV_CHUNK_SIZE);
419}
420
421#define spc_emu_quit()
422#define samples_release_rdbuf()
423
424static inline bool spc_play_get_samples(int32_t **samples)
425{
426 ENTER_TIMER(render);
427 /* fill samples buffer */
428 if ( SPC_play(&spc_emu,WAV_CHUNK_SIZE*2,wav_chunk) )
429 assert( false );
430 EXIT_TIMER(render);
431 *samples = wav_chunk;
432 return true;
433}
434#endif /* SPC_DUAL_CORE */
194 435
195/* The main decoder loop */ 436/* The main decoder loop */
196static int play_track( void ) 437static int play_track( void )
@@ -206,7 +447,7 @@ static int play_track( void )
206 fadedec=0x7fffffffl/(fadeendsample-fadestartsample)+1; 447 fadedec=0x7fffffffl/(fadeendsample-fadestartsample)+1;
207 448
208 ENTER_TIMER(total); 449 ENTER_TIMER(total);
209 450
210 while ( 1 ) 451 while ( 1 )
211 { 452 {
212 ci->yield(); 453 ci->yield();
@@ -224,14 +465,12 @@ static int play_track( void )
224 } 465 }
225 ci->seek_complete(); 466 ci->seek_complete();
226 } 467 }
227 468
228 ENTER_TIMER(render); 469 int32_t *samples;
229 /* fill samples buffer */ 470 if (!spc_play_get_samples(&samples))
230 if ( SPC_play(&spc_emu,WAV_CHUNK_SIZE*2,samples) ) 471 break;
231 assert( false ); 472
232 EXIT_TIMER(render); 473 sampleswritten += WAV_CHUNK_SIZE;
233
234 sampleswritten+=WAV_CHUNK_SIZE;
235 474
236 /* is track timed? */ 475 /* is track timed? */
237 if (ci->global_settings->repeat_mode!=REPEAT_ONE && ci->id3->length) { 476 if (ci->global_settings->repeat_mode!=REPEAT_ONE && ci->id3->length) {
@@ -241,11 +480,11 @@ static int play_track( void )
241 /* fade? */ 480 /* fade? */
242 if (curtime>ID666.length) 481 if (curtime>ID666.length)
243 { 482 {
244#ifdef CPU_COLDFIRE 483 #ifdef CPU_COLDFIRE
245 /* Have to switch modes to do this */ 484 /* Have to switch modes to do this */
246 long macsr = coldfire_get_macsr(); 485 long macsr = coldfire_get_macsr();
247 coldfire_set_macsr(EMAC_SATURATE | EMAC_FRACTIONAL | EMAC_ROUND); 486 coldfire_set_macsr(EMAC_SATURATE | EMAC_FRACTIONAL | EMAC_ROUND);
248#endif 487 #endif
249 int i; 488 int i;
250 for (i=0;i<WAV_CHUNK_SIZE;i++) { 489 for (i=0;i<WAV_CHUNK_SIZE;i++) {
251 if (lasttimesample+i>fadestartsample) { 490 if (lasttimesample+i>fadestartsample) {
@@ -256,42 +495,43 @@ static int play_track( void )
256 fadevol-=fadedec; 495 fadevol-=fadedec;
257 } 496 }
258 } 497 }
259#ifdef CPU_COLDFIRE 498 #ifdef CPU_COLDFIRE
260 coldfire_set_macsr(macsr); 499 coldfire_set_macsr(macsr);
261#endif 500 #endif
262 } 501 }
263 /* end? */ 502 /* end? */
264 if (lasttimesample>=fadeendsample) 503 if (lasttimesample>=fadeendsample)
504 {
505 samples_release_rdbuf();
265 break; 506 break;
507 }
266 } 508 }
267 509
268 ci->pcmbuf_insert(samples, samples+WAV_CHUNK_SIZE, WAV_CHUNK_SIZE); 510 spc_play_send_samples(samples);
269 511
270 if (ci->global_settings->repeat_mode!=REPEAT_ONE) 512 if (ci->global_settings->repeat_mode!=REPEAT_ONE)
271 ci->set_elapsed(sampleswritten*1000LL/SAMPLE_RATE); 513 ci->set_elapsed(sampleswritten*1000LL/SAMPLE_RATE);
272 else 514 else
273 ci->set_elapsed(0); 515 ci->set_elapsed(0);
274 } 516 }
275 517
276 EXIT_TIMER(total); 518 EXIT_TIMER(total);
277
278 return 0; 519 return 0;
279} 520}
280 521
281/* this is the codec entry point */ 522/* this is the codec entry point */
282enum codec_status codec_main(void) 523enum codec_status codec_main(void)
283{ 524{
284#ifdef CPU_COLDFIRE 525 enum codec_status stat = CODEC_ERROR;
285 /* signed integer mode with saturation */ 526
286 coldfire_set_macsr(EMAC_SATURATE); 527 if (!spc_emu_start())
287#endif 528 goto codec_quit;
288 CPU_Init(&spc_emu);
289 529
290 do 530 do
291 { 531 {
292 DEBUGF("SPC: next_track\n"); 532 DEBUGF("SPC: next_track\n");
293 if (codec_init()) { 533 if (codec_init()) {
294 return CODEC_ERROR; 534 goto codec_quit;
295 } 535 }
296 DEBUGF("SPC: after init\n"); 536 DEBUGF("SPC: after init\n");
297 537
@@ -301,7 +541,7 @@ enum codec_status codec_main(void)
301 541
302 /* wait for track info to load */ 542 /* wait for track info to load */
303 while (!*ci->taginfo_ready && !ci->stop_codec) 543 while (!*ci->taginfo_ready && !ci->stop_codec)
304 ci->sleep(1); 544 ci->yield();
305 545
306 codec_set_replaygain(ci->id3); 546 codec_set_replaygain(ci->id3);
307 547
@@ -313,20 +553,19 @@ enum codec_status codec_main(void)
313 size_t buffersize; 553 size_t buffersize;
314 uint8_t* buffer = ci->request_buffer(&buffersize, ci->filesize); 554 uint8_t* buffer = ci->request_buffer(&buffersize, ci->filesize);
315 if (!buffer) { 555 if (!buffer) {
316 return CODEC_ERROR; 556 goto codec_quit;
317 } 557 }
318 558
319 DEBUGF("SPC: read size = 0x%lx\n",(unsigned long)buffersize); 559 DEBUGF("SPC: read size = 0x%lx\n",(unsigned long)buffersize);
320 do 560 do
321 { 561 {
322 SPC_Init(&spc_emu); 562 if (load_spc_buffer(buffer, buffersize)) {
323 if (SPC_load_spc(&spc_emu,buffer,buffersize)) {
324 DEBUGF("SPC load failure\n"); 563 DEBUGF("SPC load failure\n");
325 return CODEC_ERROR; 564 goto codec_quit;
326 } 565 }
327 566
328 LoadID666(buffer+0x2e); 567 LoadID666(buffer+0x2e);
329 568
330 if (ci->global_settings->repeat_mode!=REPEAT_ONE && ID666.length==0) { 569 if (ci->global_settings->repeat_mode!=REPEAT_ONE && ID666.length==0) {
331 ID666.length=3*60*1000; /* 3 minutes */ 570 ID666.length=3*60*1000; /* 3 minutes */
332 ID666.fade=5*1000; /* 5 seconds */ 571 ID666.fade=5*1000; /* 5 seconds */
@@ -340,12 +579,16 @@ enum codec_status codec_main(void)
340 579
341 reset_profile_timers(); 580 reset_profile_timers();
342 } 581 }
343
344 while ( play_track() ); 582 while ( play_track() );
345 583
346 print_timers(ci->id3->path); 584 print_timers(ci->id3->path);
347 } 585 }
348 while ( ci->request_next_track() ); 586 while ( ci->request_next_track() );
587
588 stat = CODEC_OK;
589
590codec_quit:
591 spc_emu_quit();
349 592
350 return CODEC_OK; 593 return stat;
351} 594}