Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 1 | /*************************************************************************** |
| 2 | * __________ __ ___. |
| 3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| 4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| 5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| 6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| 7 | * \/ \/ \/ \/ \/ |
Michael Sevakis | 8552eff | 2007-07-16 21:03:25 +0000 | [diff] [blame] | 8 | * $Id$ |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 9 | * |
Michael Sevakis | ef1f7d3 | 2008-01-24 13:35:13 +0000 | [diff] [blame] | 10 | * Copyright (C) 2007-2008 Michael Sevakis (jhMikeS) |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 11 | * Copyright (C) 2006-2007 Adam Gashlin (hcs) |
| 12 | * Copyright (C) 2004-2007 Shay Green (blargg) |
| 13 | * Copyright (C) 2002 Brad Martin |
| 14 | * |
Daniel Stenberg | 2acc0ac | 2008-06-28 18:10:04 +0000 | [diff] [blame^] | 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. |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 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 */ |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 28 | #include "codeclib.h" |
Michael Sevakis | 8552eff | 2007-07-16 21:03:25 +0000 | [diff] [blame] | 29 | #include "spc/spc_codec.h" |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 30 | #include "spc/spc_profiler.h" |
| 31 | |
Michael Sevakis | 8552eff | 2007-07-16 21:03:25 +0000 | [diff] [blame] | 32 | CODEC_HEADER |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 33 | |
| 34 | /**************** ID666 parsing ****************/ |
| 35 | |
| 36 | struct { |
| 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 | |
| 50 | static 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 Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 134 | unsigned long tmp; |
| 135 | char buf[64]; |
| 136 | |
| 137 | DEBUGF("text tag detected\n"); |
Adam Boot | b9a71b3 | 2007-03-03 06:08:28 +0000 | [diff] [blame] | 138 | |
| 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 Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 159 | |
| 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 Sevakis | 8552eff | 2007-07-16 21:03:25 +0000 | [diff] [blame] | 191 | enum {SAMPLE_RATE = 32000}; |
Michael Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 192 | static struct Spc_Emu spc_emu IDATA_ATTR CACHEALIGN_ATTR; |
| 193 | |
| 194 | #if SPC_DUAL_CORE |
| 195 | /** Implementations for pipelined dual-core operation **/ |
| 196 | static int spc_emu_thread_stack[DEFAULT_STACK_SIZE/sizeof(int)] |
| 197 | CACHEALIGN_ATTR; |
| 198 | |
| 199 | static const unsigned char * const spc_emu_thread_name = "spc emu"; |
| 200 | static struct thread_entry *emu_thread_p; |
| 201 | |
| 202 | enum |
| 203 | { |
| 204 | SPC_EMU_AUDIO = 0, |
| 205 | SPC_EMU_LOAD, |
| 206 | SPC_EMU_QUIT, |
| 207 | }; |
| 208 | |
| 209 | struct 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) |
| 218 | struct 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 | |
| 228 | static 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 Sevakis | 0509914 | 2008-04-06 04:34:57 +0000 | [diff] [blame] | 236 | } sample_queue SHAREDBSS_ATTR; |
Michael Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 237 | |
| 238 | static inline void samples_release_wrbuf(void) |
| 239 | { |
| 240 | sample_queue.tail++; |
| 241 | ci->semaphore_release(&sample_queue.emu_sem_head); |
| 242 | } |
| 243 | |
| 244 | static 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 | |
| 250 | static 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 | |
| 259 | static 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 | |
| 273 | static 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 Sevakis | ef1f7d3 | 2008-01-24 13:35:13 +0000 | [diff] [blame] | 284 | |
| 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 Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 290 | return sample_queue.retval; |
| 291 | } |
| 292 | |
| 293 | /* thread function */ |
| 294 | static 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 Sevakis | ef1f7d3 | 2008-01-24 13:35:13 +0000 | [diff] [blame] | 308 | |
| 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 Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 316 | } |
| 317 | |
Michael Sevakis | ef1f7d3 | 2008-01-24 13:35:13 +0000 | [diff] [blame] | 318 | if (id != SPC_EMU_QUIT) { |
| 319 | ci->event_set_state(&sample_queue.emu_evt_reply, STATE_SIGNALED); |
| 320 | } |
Michael Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 321 | |
| 322 | return ret; |
| 323 | } |
| 324 | |
| 325 | static 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 | |
| 353 | static 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 */ |
| 377 | static 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 | |
| 384 | static inline void spc_emu_quit(void) |
| 385 | { |
Michael Sevakis | ef1f7d3 | 2008-01-24 13:35:13 +0000 | [diff] [blame] | 386 | 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 Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 392 | } |
| 393 | |
| 394 | static 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 | |
| 401 | static 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 **/ |
| 410 | int32_t wav_chunk[WAV_CHUNK_SIZE*2] IBSS_ATTR; |
| 411 | |
| 412 | /* load a new program into emu */ |
| 413 | static 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 | |
| 419 | static 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 | |
| 429 | static 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 | |
| 437 | static 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 Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 448 | |
| 449 | /* The main decoder loop */ |
| 450 | static int play_track( void ) |
| 451 | { |
| 452 | int sampleswritten=0; |
| 453 | |
Michael Sevakis | 8552eff | 2007-07-16 21:03:25 +0000 | [diff] [blame] | 454 | unsigned long fadestartsample = ID666.length*(long long) SAMPLE_RATE/1000; |
| 455 | unsigned long fadeendsample = (ID666.length+ID666.fade)*(long long) SAMPLE_RATE/1000; |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 456 | int fadedec = 0; |
| 457 | int fadevol = 0x7fffffffl; |
| 458 | |
| 459 | if (fadeendsample>fadestartsample) |
| 460 | fadedec=0x7fffffffl/(fadeendsample-fadestartsample)+1; |
| 461 | |
| 462 | ENTER_TIMER(total); |
Michael Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 463 | |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 464 | while ( 1 ) |
| 465 | { |
| 466 | ci->yield(); |
| 467 | if (ci->stop_codec || ci->new_track) { |
| 468 | break; |
| 469 | } |
| 470 | |
| 471 | if (ci->seek_time) { |
Michael Sevakis | 8552eff | 2007-07-16 21:03:25 +0000 | [diff] [blame] | 472 | int curtime = sampleswritten*1000LL/SAMPLE_RATE; |
Jens Arnold | f68362a | 2007-03-17 09:54:28 +0000 | [diff] [blame] | 473 | DEBUGF("seek to %ld\ncurrently at %d\n",ci->seek_time,curtime); |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 474 | 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 Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 481 | |
| 482 | int32_t *samples; |
| 483 | if (!spc_play_get_samples(&samples)) |
| 484 | break; |
| 485 | |
| 486 | sampleswritten += WAV_CHUNK_SIZE; |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 487 | |
| 488 | /* is track timed? */ |
| 489 | if (ci->global_settings->repeat_mode!=REPEAT_ONE && ci->id3->length) { |
Michael Sevakis | 8552eff | 2007-07-16 21:03:25 +0000 | [diff] [blame] | 490 | unsigned long curtime = sampleswritten*1000LL/SAMPLE_RATE; |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 491 | unsigned long lasttimesample = (sampleswritten-WAV_CHUNK_SIZE); |
| 492 | |
| 493 | /* fade? */ |
| 494 | if (curtime>ID666.length) |
| 495 | { |
Michael Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 496 | #ifdef CPU_COLDFIRE |
Michael Sevakis | 3096d0a | 2007-02-21 00:18:06 +0000 | [diff] [blame] | 497 | /* Have to switch modes to do this */ |
| 498 | long macsr = coldfire_get_macsr(); |
| 499 | coldfire_set_macsr(EMAC_SATURATE | EMAC_FRACTIONAL | EMAC_ROUND); |
Michael Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 500 | #endif |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 501 | int i; |
| 502 | for (i=0;i<WAV_CHUNK_SIZE;i++) { |
| 503 | if (lasttimesample+i>fadestartsample) { |
| 504 | if (fadevol>0) { |
Michael Sevakis | 3096d0a | 2007-02-21 00:18:06 +0000 | [diff] [blame] | 505 | samples[i] = FRACMUL(samples[i], fadevol); |
| 506 | samples[i+WAV_CHUNK_SIZE] = FRACMUL(samples[i+WAV_CHUNK_SIZE], fadevol); |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 507 | } else samples[i]=samples[i+WAV_CHUNK_SIZE]=0; |
| 508 | fadevol-=fadedec; |
| 509 | } |
| 510 | } |
Michael Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 511 | #ifdef CPU_COLDFIRE |
Michael Sevakis | 3096d0a | 2007-02-21 00:18:06 +0000 | [diff] [blame] | 512 | coldfire_set_macsr(macsr); |
Michael Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 513 | #endif |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 514 | } |
| 515 | /* end? */ |
| 516 | if (lasttimesample>=fadeendsample) |
Michael Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 517 | { |
| 518 | samples_release_rdbuf(); |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 519 | break; |
Michael Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 520 | } |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 521 | } |
| 522 | |
Michael Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 523 | spc_play_send_samples(samples); |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 524 | |
| 525 | if (ci->global_settings->repeat_mode!=REPEAT_ONE) |
Michael Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 526 | ci->set_elapsed(sampleswritten*1000LL/SAMPLE_RATE); |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 527 | else |
| 528 | ci->set_elapsed(0); |
| 529 | } |
| 530 | |
| 531 | EXIT_TIMER(total); |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 532 | return 0; |
| 533 | } |
| 534 | |
| 535 | /* this is the codec entry point */ |
| 536 | enum codec_status codec_main(void) |
| 537 | { |
Michael Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 538 | enum codec_status stat = CODEC_ERROR; |
| 539 | |
| 540 | if (!spc_emu_start()) |
| 541 | goto codec_quit; |
Michael Sevakis | d31162a | 2007-02-20 10:27:39 +0000 | [diff] [blame] | 542 | |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 543 | do |
| 544 | { |
| 545 | DEBUGF("SPC: next_track\n"); |
| 546 | if (codec_init()) { |
Michael Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 547 | goto codec_quit; |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 548 | } |
| 549 | DEBUGF("SPC: after init\n"); |
| 550 | |
| 551 | ci->configure(DSP_SET_SAMPLE_DEPTH, 24); |
Michael Sevakis | 8552eff | 2007-07-16 21:03:25 +0000 | [diff] [blame] | 552 | ci->configure(DSP_SET_FREQUENCY, SAMPLE_RATE); |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 553 | 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 Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 557 | ci->yield(); |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 558 | |
Michael Sevakis | 9b9e227 | 2007-02-26 17:15:04 +0000 | [diff] [blame] | 559 | codec_set_replaygain(ci->id3); |
| 560 | |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 561 | /* 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 Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 569 | goto codec_quit; |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 570 | } |
| 571 | |
Jens Arnold | bd5c0ad | 2007-03-17 10:50:58 +0000 | [diff] [blame] | 572 | DEBUGF("SPC: read size = 0x%lx\n",(unsigned long)buffersize); |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 573 | do |
| 574 | { |
Michael Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 575 | if (load_spc_buffer(buffer, buffersize)) { |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 576 | DEBUGF("SPC load failure\n"); |
Michael Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 577 | goto codec_quit; |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 578 | } |
| 579 | |
| 580 | LoadID666(buffer+0x2e); |
Michael Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 581 | |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 582 | 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 Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 586 | |
| 587 | reset_profile_timers(); |
| 588 | } |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 589 | while ( play_track() ); |
| 590 | |
| 591 | print_timers(ci->id3->path); |
| 592 | } |
| 593 | while ( ci->request_next_track() ); |
Michael Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 594 | |
| 595 | stat = CODEC_OK; |
| 596 | |
| 597 | codec_quit: |
| 598 | spc_emu_quit(); |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 599 | |
Michael Sevakis | 1a41c8a | 2007-11-18 19:03:45 +0000 | [diff] [blame] | 600 | return stat; |
Adam Gashlin | b73960d | 2007-02-14 03:34:55 +0000 | [diff] [blame] | 601 | } |