Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 1 | /*************************************************************************** |
| 2 | * __________ __ ___. |
| 3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| 4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| 5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| 6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| 7 | * \/ \/ \/ \/ \/ |
| 8 | * $Id$ |
| 9 | * |
| 10 | * Copyright (C) 2007 by Michael Sevakis |
| 11 | * |
Daniel Stenberg | 2acc0ac | 2008-06-28 18:10:04 +0000 | [diff] [blame^] | 12 | * This program is free software; you can redistribute it and/or |
| 13 | * modify it under the terms of the GNU General Public License |
| 14 | * as published by the Free Software Foundation; either version 2 |
| 15 | * of the License, or (at your option) any later version. |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 16 | * |
| 17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| 18 | * KIND, either express or implied. |
| 19 | * |
| 20 | ****************************************************************************/ |
| 21 | #include <stdlib.h> |
| 22 | #include "system.h" |
| 23 | #include "kernel.h" |
| 24 | #include "logf.h" |
| 25 | #include "audio.h" |
| 26 | #include "sound.h" |
| 27 | |
| 28 | /** |
| 29 | * Aspects implemented in the target-specific portion: |
| 30 | * |
| 31 | * ==Playback== |
| 32 | * Public - |
| 33 | * pcm_postinit |
| 34 | * pcm_get_bytes_waiting |
| 35 | * pcm_play_lock |
| 36 | * pcm_play_unlock |
| 37 | * Semi-private - |
| 38 | * pcm_play_dma_init |
| 39 | * pcm_play_dma_init |
| 40 | * pcm_play_dma_start |
| 41 | * pcm_play_dma_stop |
| 42 | * pcm_play_dma_pause |
| 43 | * pcm_play_dma_get_peak_buffer |
| 44 | * Data Read/Written within TSP - |
| 45 | * pcm_curr_sampr (RW) |
| 46 | * pcm_callback_for_more (R) |
| 47 | * pcm_playing (R) |
| 48 | * pcm_paused (R) |
| 49 | * |
| 50 | * ==Recording== |
| 51 | * Public - |
| 52 | * pcm_rec_lock |
| 53 | * pcm_rec_unlock |
| 54 | * Semi-private - |
| 55 | * pcm_rec_dma_init |
| 56 | * pcm_rec_dma_close |
| 57 | * pcm_rec_dma_start |
| 58 | * pcm_rec_dma_stop |
| 59 | * pcm_rec_dma_get_peak_buffer |
| 60 | * Data Read/Written within TSP - |
| 61 | * pcm_rec_peak_addr (RW) |
| 62 | * pcm_callback_more_ready (R) |
| 63 | * pcm_recording (R) |
| 64 | * |
| 65 | * States are set _after_ the target's pcm driver is called so that it may |
Michael Sevakis | 0553385 | 2007-10-07 23:19:09 +0000 | [diff] [blame] | 66 | * know from whence the state is changed. One exception is init. |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 67 | * |
| 68 | */ |
| 69 | |
| 70 | /* the registered callback function to ask for more mp3 data */ |
| 71 | volatile pcm_more_callback_type pcm_callback_for_more |
Michael Sevakis | 0509914 | 2008-04-06 04:34:57 +0000 | [diff] [blame] | 72 | SHAREDBSS_ATTR = NULL; |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 73 | /* PCM playback state */ |
Michael Sevakis | 0509914 | 2008-04-06 04:34:57 +0000 | [diff] [blame] | 74 | volatile bool pcm_playing SHAREDBSS_ATTR = false; |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 75 | /* PCM paused state. paused implies playing */ |
Michael Sevakis | 0509914 | 2008-04-06 04:34:57 +0000 | [diff] [blame] | 76 | volatile bool pcm_paused SHAREDBSS_ATTR = false; |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 77 | /* samplerate of currently playing audio - undefined if stopped */ |
Michael Sevakis | 0509914 | 2008-04-06 04:34:57 +0000 | [diff] [blame] | 78 | unsigned long pcm_curr_sampr SHAREDBSS_ATTR = 0; |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 79 | |
| 80 | /** |
| 81 | * Do peak calculation using distance squared from axis and save a lot |
| 82 | * of jumps and negation. Don't bother with the calculations of left or |
| 83 | * right only as it's never really used and won't save much time. |
| 84 | * |
| 85 | * Used for recording and playback. |
| 86 | */ |
| 87 | static void pcm_peak_peeker(const void *addr, int count, int peaks[2]) |
| 88 | { |
| 89 | int32_t peak_l = 0, peak_r = 0; |
| 90 | int32_t peaksq_l = 0, peaksq_r = 0; |
| 91 | |
| 92 | do |
| 93 | { |
| 94 | int32_t value = *(int32_t *)addr; |
| 95 | int32_t ch, chsq; |
| 96 | #ifdef ROCKBOX_BIG_ENDIAN |
| 97 | ch = value >> 16; |
| 98 | #else |
| 99 | ch = (int16_t)value; |
| 100 | #endif |
| 101 | chsq = ch*ch; |
| 102 | if (chsq > peaksq_l) |
| 103 | peak_l = ch, peaksq_l = chsq; |
| 104 | |
| 105 | #ifdef ROCKBOX_BIG_ENDIAN |
| 106 | ch = (int16_t)value; |
| 107 | #else |
| 108 | ch = value >> 16; |
| 109 | #endif |
| 110 | chsq = ch*ch; |
| 111 | if (chsq > peaksq_r) |
| 112 | peak_r = ch, peaksq_r = chsq; |
| 113 | |
| 114 | addr += 16; |
| 115 | count -= 4; |
| 116 | } |
| 117 | while (count > 0); |
| 118 | |
| 119 | peaks[0] = abs(peak_l); |
| 120 | peaks[1] = abs(peak_r); |
| 121 | } |
| 122 | |
| 123 | void pcm_calculate_peaks(int *left, int *right) |
| 124 | { |
| 125 | static int peaks[2] = { 0, 0 }; |
| 126 | static unsigned long last_peak_tick = 0; |
| 127 | static unsigned long frame_period = 0; |
| 128 | |
| 129 | long tick = current_tick; |
| 130 | |
| 131 | /* Throttled peak ahead based on calling period */ |
| 132 | long period = tick - last_peak_tick; |
| 133 | |
| 134 | /* Keep reasonable limits on period */ |
| 135 | if (period < 1) |
| 136 | period = 1; |
| 137 | else if (period > HZ/5) |
| 138 | period = HZ/5; |
| 139 | |
| 140 | frame_period = (3*frame_period + period) >> 2; |
| 141 | |
| 142 | last_peak_tick = tick; |
| 143 | |
| 144 | if (pcm_playing && !pcm_paused) |
| 145 | { |
| 146 | const void *addr; |
| 147 | int count, framecount; |
| 148 | |
| 149 | addr = pcm_play_dma_get_peak_buffer(&count); |
| 150 | |
| 151 | framecount = frame_period*pcm_curr_sampr / HZ; |
| 152 | count = MIN(framecount, count); |
| 153 | |
| 154 | if (count > 0) |
| 155 | pcm_peak_peeker(addr, count, peaks); |
| 156 | } |
| 157 | else |
| 158 | { |
| 159 | peaks[0] = peaks[1] = 0; |
| 160 | } |
| 161 | |
| 162 | if (left) |
| 163 | *left = peaks[0]; |
| 164 | |
| 165 | if (right) |
| 166 | *right = peaks[1]; |
| 167 | } |
| 168 | |
| 169 | /**************************************************************************** |
| 170 | * Functions that do not require targeted implementation but only a targeted |
| 171 | * interface |
| 172 | */ |
| 173 | |
| 174 | /* This should only be called at startup before any audio playback or |
| 175 | recording is attempted */ |
| 176 | void pcm_init(void) |
| 177 | { |
| 178 | logf("pcm_init"); |
| 179 | |
| 180 | pcm_play_dma_stopped_callback(); |
| 181 | |
| 182 | logf(" pcm_play_dma_init"); |
| 183 | pcm_play_dma_init(); |
| 184 | } |
| 185 | |
| 186 | /* Common code to pcm_play_data and pcm_play_pause */ |
| 187 | static void pcm_play_data_start(unsigned char *start, size_t size) |
| 188 | { |
| 189 | if (!(start && size)) |
| 190 | { |
| 191 | pcm_more_callback_type get_more = pcm_callback_for_more; |
| 192 | size = 0; |
| 193 | if (get_more) |
| 194 | { |
| 195 | logf(" get_more"); |
| 196 | get_more(&start, &size); |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | if (start && size) |
| 201 | { |
| 202 | logf(" pcm_play_dma_start"); |
| 203 | pcm_play_dma_start(start, size); |
| 204 | pcm_playing = true; |
| 205 | pcm_paused = false; |
| 206 | return; |
| 207 | } |
| 208 | |
| 209 | /* Force a stop */ |
| 210 | logf(" pcm_play_dma_stop"); |
| 211 | pcm_play_dma_stop(); |
| 212 | pcm_play_dma_stopped_callback(); |
| 213 | } |
| 214 | |
| 215 | void pcm_play_data(pcm_more_callback_type get_more, |
| 216 | unsigned char *start, size_t size) |
| 217 | { |
| 218 | logf("pcm_play_data"); |
| 219 | |
| 220 | pcm_play_lock(); |
| 221 | |
| 222 | pcm_callback_for_more = get_more; |
| 223 | |
| 224 | logf(" pcm_play_dma_start"); |
| 225 | pcm_play_data_start(start, size); |
| 226 | |
| 227 | pcm_play_unlock(); |
| 228 | } |
| 229 | |
| 230 | void pcm_play_pause(bool play) |
| 231 | { |
| 232 | logf("pcm_play_pause: %s", play ? "play" : "pause"); |
| 233 | |
| 234 | pcm_play_lock(); |
| 235 | |
| 236 | if (play == pcm_paused && pcm_playing) |
| 237 | { |
| 238 | if (!play) |
| 239 | { |
| 240 | logf(" pcm_play_dma_pause"); |
| 241 | pcm_play_dma_pause(true); |
| 242 | pcm_paused = true; |
| 243 | } |
| 244 | else if (pcm_get_bytes_waiting() > 0) |
| 245 | { |
| 246 | logf(" pcm_play_dma_pause"); |
| 247 | pcm_play_dma_pause(false); |
| 248 | pcm_paused = false; |
| 249 | } |
| 250 | else |
| 251 | { |
| 252 | logf(" pcm_play_dma_start: no data"); |
| 253 | pcm_play_data_start(NULL, 0); |
| 254 | } |
| 255 | } |
| 256 | else |
| 257 | { |
| 258 | logf(" no change"); |
| 259 | } |
| 260 | |
| 261 | pcm_play_unlock(); |
| 262 | } |
| 263 | |
| 264 | void pcm_play_stop(void) |
| 265 | { |
| 266 | logf("pcm_play_stop"); |
| 267 | |
| 268 | pcm_play_lock(); |
| 269 | |
| 270 | if (pcm_playing) |
| 271 | { |
| 272 | logf(" pcm_play_dma_stop"); |
| 273 | pcm_play_dma_stop(); |
| 274 | pcm_play_dma_stopped_callback(); |
| 275 | } |
| 276 | else |
| 277 | { |
| 278 | logf(" not playing"); |
| 279 | } |
| 280 | |
| 281 | pcm_play_unlock(); |
| 282 | } |
| 283 | |
| 284 | void pcm_play_dma_stopped_callback(void) |
| 285 | { |
| 286 | pcm_callback_for_more = NULL; |
| 287 | pcm_paused = false; |
| 288 | pcm_playing = false; |
| 289 | } |
| 290 | |
| 291 | /**/ |
| 292 | |
| 293 | bool pcm_is_playing(void) |
| 294 | { |
| 295 | return pcm_playing; |
| 296 | } |
| 297 | |
| 298 | bool pcm_is_paused(void) |
| 299 | { |
| 300 | return pcm_paused; |
| 301 | } |
| 302 | |
| 303 | void pcm_mute(bool mute) |
| 304 | { |
| 305 | #ifndef SIMULATOR |
| 306 | audiohw_mute(mute); |
| 307 | #endif |
| 308 | |
| 309 | if (mute) |
| 310 | sleep(HZ/16); |
| 311 | } |
| 312 | |
| 313 | #ifdef HAVE_RECORDING |
| 314 | /** Low level pcm recording apis **/ |
| 315 | |
| 316 | /* Next start for recording peaks */ |
Michael Sevakis | 0509914 | 2008-04-06 04:34:57 +0000 | [diff] [blame] | 317 | const volatile void *pcm_rec_peak_addr SHAREDBSS_ATTR = NULL; |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 318 | /* the registered callback function for when more data is available */ |
| 319 | volatile pcm_more_callback_type2 |
Michael Sevakis | 0509914 | 2008-04-06 04:34:57 +0000 | [diff] [blame] | 320 | pcm_callback_more_ready SHAREDBSS_ATTR = NULL; |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 321 | /* DMA transfer in is currently active */ |
Michael Sevakis | 0509914 | 2008-04-06 04:34:57 +0000 | [diff] [blame] | 322 | volatile bool pcm_recording SHAREDBSS_ATTR = false; |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 323 | |
| 324 | /** |
| 325 | * Return recording peaks - From the end of the last peak up to |
| 326 | * current write position. |
| 327 | */ |
| 328 | void pcm_calculate_rec_peaks(int *left, int *right) |
| 329 | { |
| 330 | static int peaks[2]; |
| 331 | |
| 332 | if (pcm_recording) |
| 333 | { |
| 334 | const void *addr; |
| 335 | int count; |
| 336 | |
| 337 | addr = pcm_rec_dma_get_peak_buffer(&count); |
| 338 | |
| 339 | if (count > 0) |
| 340 | { |
| 341 | pcm_peak_peeker(addr, count, peaks); |
| 342 | |
| 343 | if (addr == pcm_rec_peak_addr) |
| 344 | pcm_rec_peak_addr = (int32_t *)addr + count; |
| 345 | } |
| 346 | } |
| 347 | else |
| 348 | { |
| 349 | peaks[0] = peaks[1] = 0; |
| 350 | } |
| 351 | |
| 352 | if (left) |
| 353 | *left = peaks[0]; |
| 354 | |
| 355 | if (right) |
| 356 | *right = peaks[1]; |
| 357 | } /* pcm_calculate_rec_peaks */ |
| 358 | |
| 359 | /**************************************************************************** |
| 360 | * Functions that do not require targeted implementation but only a targeted |
| 361 | * interface |
| 362 | */ |
| 363 | void pcm_init_recording(void) |
| 364 | { |
| 365 | logf("pcm_init_recording"); |
| 366 | |
Michael Sevakis | 0553385 | 2007-10-07 23:19:09 +0000 | [diff] [blame] | 367 | /* Recording init is locked unlike general pcm init since this is not |
| 368 | * just a one-time event at startup and it should and must be safe by |
| 369 | * now. */ |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 370 | pcm_rec_lock(); |
| 371 | |
| 372 | logf(" pcm_rec_dma_init"); |
| 373 | pcm_rec_dma_stopped_callback(); |
| 374 | pcm_rec_dma_init(); |
| 375 | |
| 376 | pcm_rec_unlock(); |
| 377 | } |
| 378 | |
| 379 | void pcm_close_recording(void) |
| 380 | { |
| 381 | logf("pcm_close_recording"); |
| 382 | |
| 383 | pcm_rec_lock(); |
| 384 | |
| 385 | if (pcm_recording) |
| 386 | { |
| 387 | logf(" pcm_rec_dma_stop"); |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 388 | pcm_rec_dma_stop(); |
Michael Sevakis | 0553385 | 2007-10-07 23:19:09 +0000 | [diff] [blame] | 389 | pcm_rec_dma_stopped_callback(); |
Michael Sevakis | 6077e5b | 2007-10-06 22:27:27 +0000 | [diff] [blame] | 390 | } |
| 391 | |
| 392 | logf(" pcm_rec_dma_close"); |
| 393 | pcm_rec_dma_close(); |
| 394 | |
| 395 | pcm_rec_unlock(); |
| 396 | } |
| 397 | |
| 398 | void pcm_record_data(pcm_more_callback_type2 more_ready, |
| 399 | void *start, size_t size) |
| 400 | { |
| 401 | logf("pcm_record_data"); |
| 402 | |
| 403 | if (!(start && size)) |
| 404 | { |
| 405 | logf(" no buffer"); |
| 406 | return; |
| 407 | } |
| 408 | |
| 409 | pcm_rec_lock(); |
| 410 | |
| 411 | pcm_callback_more_ready = more_ready; |
| 412 | |
| 413 | logf(" pcm_rec_dma_start"); |
| 414 | pcm_rec_dma_start(start, size); |
| 415 | pcm_recording = true; |
| 416 | |
| 417 | pcm_rec_unlock(); |
| 418 | } /* pcm_record_data */ |
| 419 | |
| 420 | void pcm_stop_recording(void) |
| 421 | { |
| 422 | logf("pcm_stop_recording"); |
| 423 | |
| 424 | pcm_rec_lock(); |
| 425 | |
| 426 | if (pcm_recording) |
| 427 | { |
| 428 | logf(" pcm_rec_dma_stop"); |
| 429 | pcm_rec_dma_stop(); |
| 430 | pcm_rec_dma_stopped_callback(); |
| 431 | } |
| 432 | |
| 433 | pcm_rec_unlock(); |
| 434 | } /* pcm_stop_recording */ |
| 435 | |
| 436 | void pcm_rec_dma_stopped_callback(void) |
| 437 | { |
| 438 | pcm_recording = false; |
| 439 | pcm_callback_more_ready = NULL; |
| 440 | } |
| 441 | |
| 442 | #endif /* HAVE_RECORDING */ |