A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 550 lines 14 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2007 by Michael Sevakis 11 * 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. 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 25/* Define LOGF_ENABLE to enable logf output in this file */ 26//#define LOGF_ENABLE 27#include "logf.h" 28#include "audio.h" 29#include "sound.h" 30#include "general.h" 31#include "pcm-internal.h" 32#include "pcm_mixer.h" 33 34/** 35 * Aspects implemented in the target-specific portion: 36 * 37 * ==Playback== 38 * Public - 39 * pcm_postinit 40 * pcm_play_lock 41 * pcm_play_unlock 42 * Semi-private - 43 * pcm_play_dma_complete_callback 44 * pcm_play_dma_status_callback 45 * pcm_play_dma_init 46 * pcm_play_dma_postinit 47 * pcm_play_dma_start 48 * pcm_play_dma_stop 49 * Data Read/Written within TSP - 50 * pcm_sampr (R) 51 * pcm_fsel (R) 52 * pcm_curr_sampr (R) 53 * pcm_playing (R) 54 * 55 * ==Playback/Recording== 56 * Public - 57 * pcm_dma_addr 58 * Semi-private - 59 * pcm_dma_apply_settings 60 * 61 * ==Recording== 62 * Public - 63 * pcm_rec_lock 64 * pcm_rec_unlock 65 * Semi-private - 66 * pcm_rec_dma_complete_callback 67 * pcm_rec_dma_status_callback 68 * pcm_rec_dma_init 69 * pcm_rec_dma_close 70 * pcm_rec_dma_start 71 * pcm_rec_dma_stop 72 * pcm_rec_dma_get_peak_buffer 73 * Data Read/Written within TSP - 74 * pcm_recording (R) 75 * 76 * States are set _after_ the target's pcm driver is called so that it may 77 * know from whence the state is changed. One exception is init. 78 * 79 */ 80 81/* 'true' when all stages of pcm initialization have completed */ 82static bool pcm_is_ready = false; 83 84/* The registered callback function to ask for more mp3 data */ 85volatile pcm_play_callback_type 86 pcm_callback_for_more SHAREDBSS_ATTR = NULL; 87/* The registered callback function to inform of DMA status */ 88volatile pcm_status_callback_type 89 pcm_play_status_callback SHAREDBSS_ATTR = NULL; 90/* PCM playback state */ 91volatile bool pcm_playing SHAREDBSS_ATTR = false; 92/* samplerate of currently playing audio - undefined if stopped */ 93unsigned long pcm_curr_sampr SHAREDBSS_ATTR = 0; 94/* samplerate waiting to be set */ 95unsigned long pcm_sampr SHAREDBSS_ATTR = HW_SAMPR_DEFAULT; 96/* samplerate frequency selection index */ 97int pcm_fsel SHAREDBSS_ATTR = HW_FREQ_DEFAULT; 98 99static void pcm_play_data_start_int(const void *addr, size_t size); 100void pcm_play_stop_int(void); 101 102#if !defined(HAVE_SW_VOLUME_CONTROL) || defined(PCM_SW_VOLUME_UNBUFFERED) 103/** Standard hw volume/unbuffered control functions - otherwise, see 104 ** pcm_sw_volume.c **/ 105static inline void pcm_play_dma_start_int(const void *addr, size_t size) 106{ 107#ifdef HAVE_SW_VOLUME_CONTROL 108 /* Smoothed transition might not have happened so sync now */ 109 pcm_sync_pcm_factors(); 110#endif 111 pcm_play_dma_start(addr, size); 112} 113 114static inline void pcm_play_dma_stop_int(void) 115{ 116 pcm_play_dma_stop(); 117} 118 119bool pcm_play_dma_complete_callback(enum pcm_dma_status status, 120 const void **addr, size_t *size) 121{ 122 /* Check status callback first if error */ 123 if (status < PCM_DMAST_OK) 124 status = pcm_play_dma_status_callback(status); 125 126 if (status >= PCM_DMAST_OK && pcm_get_more_int(addr, size)) 127 return true; 128 129 /* Error, callback missing or no more DMA to do */ 130 pcm_play_stop_int(); 131 return false; 132} 133#endif /* !HAVE_SW_VOLUME_CONTROL || PCM_SW_VOLUME_UNBUFFERED */ 134 135static void pcm_play_data_start_int(const void *addr, size_t size) 136{ 137 ALIGN_AUDIOBUF(addr, size); 138 139 if ((addr && size) || pcm_get_more_int(&addr, &size)) 140 { 141 pcm_apply_settings(); 142 logf(" pcm_play_dma_start_int"); 143 pcm_play_dma_start_int(addr, size); 144 pcm_playing = true; 145 } 146 else 147 { 148 /* Force a stop */ 149 logf(" pcm_play_stop_int"); 150 pcm_play_stop_int(); 151 } 152} 153 154void pcm_play_stop_int(void) 155{ 156 pcm_play_dma_stop_int(); 157 pcm_callback_for_more = NULL; 158 pcm_play_status_callback = NULL; 159 pcm_playing = false; 160} 161 162static void pcm_wait_for_init(void) 163{ 164 while (!pcm_is_ready) 165 sleep(0); 166} 167 168/** 169 * Perform peak calculation on a buffer of packed 16-bit samples. 170 * 171 * Used for recording and playback. 172 */ 173static void pcm_peak_peeker(const int16_t *p, int count, 174 struct pcm_peaks *peaks) 175{ 176 uint32_t peak_l = 0, peak_r = 0; 177 const int16_t *pend = p + 2 * count; 178 179 do 180 { 181 int32_t s; 182 183 s = p[0]; 184 185 if (s < 0) 186 s = -s; 187 188 if ((uint32_t)s > peak_l) 189 peak_l = s; 190 191 s = p[1]; 192 193 if (s < 0) 194 s = -s; 195 196 if ((uint32_t)s > peak_r) 197 peak_r = s; 198 199 p += 4 * 2; /* Every 4th sample, interleaved */ 200 } 201 while (p < pend); 202 203 peaks->left = peak_l; 204 peaks->right = peak_r; 205} 206 207void pcm_do_peak_calculation(struct pcm_peaks *peaks, bool active, 208 const void *addr, int count) 209{ 210 long tick = current_tick; 211 212 /* Peak no farther ahead than expected period to avoid overcalculation */ 213 long period = tick - peaks->tick; 214 215 /* Keep reasonable limits on period */ 216 if (period < 1) 217 period = 1; 218 else if (period > HZ/5) 219 period = HZ/5; 220 221 peaks->period = (3*peaks->period + period) / 4; 222 peaks->tick = tick; 223 224 if (active) 225 { 226 int framecount = peaks->period*pcm_curr_sampr / HZ; 227 count = MIN(framecount, count); 228 229 if (count > 0) 230 pcm_peak_peeker(addr, count, peaks); 231 /* else keep previous peak values */ 232 } 233 else 234 { 235 /* peaks are zero */ 236 peaks->left = peaks->right = 0; 237 } 238} 239 240bool pcm_is_playing(void) 241{ 242 return pcm_playing; 243} 244 245/**************************************************************************** 246 * Functions that do not require targeted implementation but only a targeted 247 * interface 248 */ 249 250/* This should only be called at startup before any audio playback or 251 recording is attempted */ 252void pcm_init(void) 253{ 254 logf("pcm_init"); 255 256 pcm_set_frequency(HW_SAMPR_DEFAULT); 257 258 logf(" pcm_play_dma_init"); 259 pcm_play_dma_init(); 260} 261 262/* Finish delayed init */ 263void pcm_postinit(void) 264{ 265 logf("pcm_postinit"); 266 267 logf(" pcm_play_dma_postinit"); 268 269 pcm_play_dma_postinit(); 270 271 pcm_is_ready = true; 272} 273 274bool pcm_is_initialized(void) 275{ 276 return pcm_is_ready; 277} 278 279void pcm_play_data(pcm_play_callback_type get_more, 280 pcm_status_callback_type status_cb, 281 const void *start, size_t size) 282{ 283 logf("pcm_play_data"); 284 285 pcm_play_lock(); 286 287 pcm_callback_for_more = get_more; 288 pcm_play_status_callback = status_cb; 289 290 logf(" pcm_play_data_start_int"); 291 pcm_play_data_start_int(start, size); 292 293 pcm_play_unlock(); 294} 295 296void pcm_play_stop(void) 297{ 298 logf("pcm_play_stop"); 299 300 pcm_play_lock(); 301 302 if (pcm_playing) 303 { 304 logf(" pcm_play_stop_int"); 305 pcm_play_stop_int(); 306 } 307 308 pcm_play_unlock(); 309} 310 311/**/ 312 313/* set frequency next frequency used by the audio hardware - 314 * what pcm_apply_settings will set */ 315void pcm_set_frequency(unsigned int samplerate) 316{ 317 logf("pcm_set_frequency %u", samplerate); 318 319 int index; 320 321#ifdef CONFIG_SAMPR_TYPES 322 unsigned int type = samplerate & SAMPR_TYPE_MASK; 323 samplerate &= ~SAMPR_TYPE_MASK; 324 325 /* For now, supported targets have direct conversion when configured with 326 * CONFIG_SAMPR_TYPES. 327 * Some hypothetical target with independent rates would need slightly 328 * different handling throughout this source. */ 329 samplerate = pcm_sampr_to_hw_sampr(samplerate, type); 330#endif /* CONFIG_SAMPR_TYPES */ 331 332 index = round_value_to_list32(samplerate, hw_freq_sampr, 333 HW_NUM_FREQ, false); 334 335 if (samplerate != hw_freq_sampr[index]) 336 index = HW_FREQ_DEFAULT; /* Invalid = default */ 337 338 pcm_sampr = hw_freq_sampr[index]; 339 pcm_fsel = index; 340} 341 342/* return last-set frequency */ 343unsigned int pcm_get_frequency(void) 344{ 345 return pcm_sampr; 346} 347 348/* apply pcm settings to the hardware */ 349void pcm_apply_settings(void) 350{ 351 logf("pcm_apply_settings"); 352 353 pcm_wait_for_init(); 354 355 if (pcm_sampr != pcm_curr_sampr) 356 { 357 logf(" pcm_dma_apply_settings"); 358 pcm_dma_apply_settings(); 359 pcm_curr_sampr = pcm_sampr; 360 } 361} 362 363#ifdef HAVE_RECORDING 364/** Low level pcm recording apis **/ 365 366/* Next start for recording peaks */ 367static const void * volatile pcm_rec_peak_addr SHAREDBSS_ATTR = NULL; 368/* the registered callback function for when more data is available */ 369static volatile pcm_rec_callback_type 370 pcm_callback_more_ready SHAREDBSS_ATTR = NULL; 371volatile pcm_status_callback_type 372 pcm_rec_status_callback SHAREDBSS_ATTR = NULL; 373/* DMA transfer in is currently active */ 374volatile bool pcm_recording SHAREDBSS_ATTR = false; 375 376/* Called internally by functions to reset the state */ 377static void pcm_recording_stopped(void) 378{ 379 pcm_recording = false; 380 pcm_callback_more_ready = NULL; 381 pcm_rec_status_callback = NULL; 382} 383 384/** 385 * Return recording peaks - From the end of the last peak up to 386 * current write position. 387 */ 388void pcm_calculate_rec_peaks(int *left, int *right) 389{ 390 static struct pcm_peaks peaks; 391 392 if (pcm_recording) 393 { 394 const int16_t *peak_addr = pcm_rec_peak_addr; 395 const int16_t *addr = pcm_rec_dma_get_peak_buffer(); 396 397 if (addr != NULL) 398 { 399 int count = (addr - peak_addr) / 2; /* Interleaved L+R */ 400 401 if (count > 0) 402 { 403 pcm_peak_peeker(peak_addr, count, &peaks); 404 405 if (peak_addr == pcm_rec_peak_addr) 406 pcm_rec_peak_addr = addr; 407 } 408 } 409 /* else keep previous peak values */ 410 } 411 else 412 { 413 peaks.left = peaks.right = 0; 414 } 415 416 if (left) 417 *left = peaks.left; 418 419 if (right) 420 *right = peaks.right; 421} 422 423bool pcm_is_recording(void) 424{ 425 return pcm_recording; 426} 427 428/**************************************************************************** 429 * Functions that do not require targeted implementation but only a targeted 430 * interface 431 */ 432 433void pcm_init_recording(void) 434{ 435 logf("pcm_init_recording"); 436 437 pcm_wait_for_init(); 438 439 /* Stop the beasty before attempting recording */ 440 mixer_reset(); 441 442 /* Recording init is locked unlike general pcm init since this is not 443 * just a one-time event at startup and it should and must be safe by 444 * now. */ 445 pcm_rec_lock(); 446 447 logf(" pcm_rec_dma_init"); 448 pcm_recording_stopped(); 449 pcm_rec_dma_init(); 450 451 pcm_rec_unlock(); 452} 453 454void pcm_close_recording(void) 455{ 456 logf("pcm_close_recording"); 457 458 pcm_rec_lock(); 459 460 if (pcm_recording) 461 { 462 logf(" pcm_rec_dma_stop"); 463 pcm_rec_dma_stop(); 464 pcm_recording_stopped(); 465 } 466 467 logf(" pcm_rec_dma_close"); 468 pcm_rec_dma_close(); 469 470 pcm_rec_unlock(); 471} 472 473void pcm_record_data(pcm_rec_callback_type more_ready, 474 pcm_status_callback_type status_cb, 475 void *addr, size_t size) 476{ 477 logf("pcm_record_data"); 478 479 ALIGN_AUDIOBUF(addr, size); 480 481 if (!(addr && size)) 482 { 483 logf(" no buffer"); 484 return; 485 } 486 487 pcm_rec_lock(); 488 489 pcm_callback_more_ready = more_ready; 490 pcm_rec_status_callback = status_cb; 491 492 /* Need a physical DMA address translation, if not already physical. */ 493 pcm_rec_peak_addr = pcm_rec_dma_addr(addr); 494 495 logf(" pcm_rec_dma_start"); 496 pcm_apply_settings(); 497 pcm_rec_dma_start(addr, size); 498 pcm_recording = true; 499 500 pcm_rec_unlock(); 501} /* pcm_record_data */ 502 503void pcm_stop_recording(void) 504{ 505 logf("pcm_stop_recording"); 506 507 pcm_rec_lock(); 508 509 if (pcm_recording) 510 { 511 logf(" pcm_rec_dma_stop"); 512 pcm_rec_dma_stop(); 513 pcm_recording_stopped(); 514 } 515 516 pcm_rec_unlock(); 517} /* pcm_stop_recording */ 518 519bool pcm_rec_dma_complete_callback(enum pcm_dma_status status, 520 void **addr, size_t *size) 521{ 522 /* Check status callback first if error */ 523 if (status < PCM_DMAST_OK) 524 status = pcm_rec_dma_status_callback(status); 525 526 pcm_rec_callback_type have_more = pcm_callback_more_ready; 527 528 if (have_more && status >= PCM_DMAST_OK) 529 { 530 /* Call registered callback to obtain next buffer */ 531 have_more(addr, size); 532 ALIGN_AUDIOBUF(*addr, *size); 533 534 if (*addr && *size) 535 { 536 /* Need a physical DMA address translation, if not already 537 * physical. */ 538 pcm_rec_peak_addr = pcm_rec_dma_addr(*addr); 539 return true; 540 } 541 } 542 543 /* Error, callback missing or no more DMA to do */ 544 pcm_rec_dma_stop(); 545 pcm_recording_stopped(); 546 547 return false; 548} 549 550#endif /* HAVE_RECORDING */