A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
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 */