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) 2013 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 "config.h"
22#include "system.h"
23#include "pcm.h"
24#include "pcm-internal.h"
25#include "dsp-util.h"
26#include "fixedpoint.h"
27#include "pcm_sw_volume.h"
28
29/*
30 * NOTE: With the addition of 32-bit software scaling to this
31 * file, sometimes the "size" variable gets a little confusing.
32 *
33 * The source buffer (as of right now) is always 16-bit, and the
34 * destination buffer can potentially be 32-bit. I've tried to
35 * make it consistent: when passed in a function call, try to use
36 * the source buffer (16-bit) size.
37 */
38
39/* volume factors set by pcm_set_master_volume */
40static uint32_t vol_factor_l = 0, vol_factor_r = 0;
41
42#ifdef AUDIOHW_HAVE_PRESCALER
43/* prescale factor set by pcm_set_prescaler */
44static uint32_t prescale_factor = PCM_FACTOR_UNITY;
45#endif /* AUDIOHW_HAVE_PRESCALER */
46
47/* final pcm scaling factors */
48static uint32_t pcm_new_factor_l = 0, pcm_new_factor_r = 0;
49static uint32_t pcm_factor_l = 0, pcm_factor_r = 0;
50static typeof (memcpy) *pcm_scaling_fn = NULL;
51
52/* take care of some defines for 32-bit software vol */
53#if (PCM_NATIVE_BITDEPTH > 16) /* >16-bit */
54
55# define HAVE_SWVOL_32
56# define PCM_VOL_SAMPLE_SIZE (2 * sizeof (int32_t))
57# define PCM_DBL_BUF_SIZE_T int32_t
58
59# if !defined(PCM_DC_OFFSET_VALUE)
60/* PCM_DC_OFFSET_VALUE is only needed due to hardware quirk on Eros Q */
61# define PCM_DC_OFFSET_VALUE 0
62# endif
63
64#else /* 16-BIT */
65
66# define PCM_VOL_SAMPLE_SIZE PCM_SAMPLE_SIZE
67# define PCM_DBL_BUF_SIZE_T int16_t
68
69#endif /* 16-BIT */
70
71/***
72 ** Volume scaling routines
73 ** If unbuffered, called externally by pcm driver
74 **/
75
76/* TODO: #include CPU-optimized routines and move this to /firmware/asm */
77#if PCM_SW_VOLUME_FRACBITS <= 16
78#define PCM_F_T int32_t
79#else
80#define PCM_F_T int64_t /* Requires large integer math */
81#endif /* PCM_SW_VOLUME_FRACBITS */
82
83/* Scale sample by PCM factor */
84static inline int32_t pcm_scale_sample(PCM_F_T f, int32_t s)
85{
86#if defined(HAVE_SWVOL_32)
87 return (f * s + PCM_DC_OFFSET_VALUE) >> (32 - PCM_NATIVE_BITDEPTH);
88#else
89 return (f * s) >> PCM_SW_VOLUME_FRACBITS;
90#endif
91}
92
93/* Either cut (both <= UNITY), no clipping needed */
94static void * pcm_scale_buffer_cut(void *dst, const void *src, size_t src_size)
95{
96 PCM_DBL_BUF_SIZE_T *d = dst;
97 const int16_t *s = src;
98 uint32_t factor_l = pcm_factor_l, factor_r = pcm_factor_r;
99
100 while (src_size)
101 {
102 *d++ = pcm_scale_sample(factor_l, *s++);
103 *d++ = pcm_scale_sample(factor_r, *s++);
104 src_size -= PCM_SAMPLE_SIZE;
105 }
106
107 return dst;
108}
109
110#if !defined(HAVE_SWVOL_32) /* NOTE: 32-bit scaling is hardcoded to the cut function! */
111/* Either boost (any > UNITY) requires clipping */
112static void * pcm_scale_buffer_boost(void *dst, const void *src, size_t src_size)
113{
114 int16_t *d = dst;
115 const int16_t *s = src;
116 uint32_t factor_l = pcm_factor_l, factor_r = pcm_factor_r;
117
118 while (src_size)
119 {
120 *d++ = clip_sample_16(pcm_scale_sample(factor_l, *s++));
121 *d++ = clip_sample_16(pcm_scale_sample(factor_r, *s++));
122 src_size -= PCM_SAMPLE_SIZE;
123 }
124
125 return dst;
126}
127#endif
128
129/* Transition the volume change smoothly across a frame */
130static void * pcm_scale_buffer_trans(void *dst, const void *src, size_t src_size)
131{
132 PCM_DBL_BUF_SIZE_T *d = dst;
133 const int16_t *s = src;
134 uint32_t factor_l = pcm_factor_l, factor_r = pcm_factor_r;
135
136 /* Transition from the old value to the new value using an inverted cosinus
137 from PI..0 in order to minimize amplitude-modulated harmonics generation
138 (zipper effects). */
139 uint32_t new_factor_l = pcm_new_factor_l;
140 uint32_t new_factor_r = pcm_new_factor_r;
141
142 int32_t diff_l = (int32_t)new_factor_l - (int32_t)factor_l;
143 int32_t diff_r = (int32_t)new_factor_r - (int32_t)factor_r;
144
145 for (size_t done = 0; done < src_size; done += PCM_SAMPLE_SIZE)
146 {
147 int32_t sweep = (1 << 14) - fp14_cos(180*done / src_size); /* 0.0..2.0 */
148 uint32_t f_l = fp_mul(sweep, diff_l, 15) + factor_l;
149 uint32_t f_r = fp_mul(sweep, diff_r, 15) + factor_r;
150#if defined(HAVE_SWVOL_32)
151 /* do not clip to 16 bits */
152 *d++ = pcm_scale_sample(f_l, *s++);
153 *d++ = pcm_scale_sample(f_r, *s++);
154#else
155 *d++ = clip_sample_16(pcm_scale_sample(f_l, *s++));
156 *d++ = clip_sample_16(pcm_scale_sample(f_r, *s++));
157#endif
158 }
159
160 /* Select steady-state operation */
161 pcm_sync_pcm_factors();
162
163 return dst;
164}
165
166/* Called by completion routine to scale the next buffer of samples */
167#ifndef PCM_SW_VOLUME_UNBUFFERED
168static inline
169#endif
170void pcm_sw_volume_copy_buffer(void *dst, const void *src, size_t src_size)
171{
172 pcm_scaling_fn(dst, src, src_size);
173}
174
175/* Assign the new scaling function for normal steady-state operation */
176void pcm_sync_pcm_factors(void)
177{
178 uint32_t new_factor_l = pcm_new_factor_l;
179 uint32_t new_factor_r = pcm_new_factor_r;
180
181 pcm_factor_l = new_factor_l;
182 pcm_factor_r = new_factor_r;
183
184/* NOTE: 32-bit scaling is limited to 0 db <--> -74 db, we will hardcode to cut.
185 * MEMCPY CANNOT BE USED, because we do need to at minimum multiply each
186 * sample up to 32-bit size. */
187#if defined(HAVE_SWVOL_32)
188 pcm_scaling_fn = pcm_scale_buffer_cut;
189#else
190
191 if (new_factor_l == PCM_FACTOR_UNITY &&
192 new_factor_r == PCM_FACTOR_UNITY)
193 {
194 pcm_scaling_fn = memcpy;
195 }
196 else if (new_factor_l <= PCM_FACTOR_UNITY &&
197 new_factor_r <= PCM_FACTOR_UNITY)
198 {
199 pcm_scaling_fn = pcm_scale_buffer_cut;
200 }
201 else
202 {
203 pcm_scaling_fn = pcm_scale_buffer_boost;
204 }
205#endif
206}
207
208#ifndef PCM_SW_VOLUME_UNBUFFERED
209/* source buffer from client */
210static const void * volatile src_buf_addr = NULL;
211static size_t volatile src_buf_rem = 0;
212
213#define PCM_PLAY_DBL_BUF_SIZE (PCM_PLAY_DBL_BUF_SAMPLE*PCM_VOL_SAMPLE_SIZE)
214
215/* double buffer and frame length control */
216static PCM_DBL_BUF_SIZE_T pcm_dbl_buf[2][PCM_PLAY_DBL_BUF_SAMPLES*2]
217 PCM_DBL_BUF_BSS MEM_ALIGN_ATTR;
218static size_t pcm_dbl_buf_size[2];
219static int pcm_dbl_buf_num = 0;
220static size_t frame_size;
221static unsigned int frame_count, frame_err, frame_frac;
222
223/** Overrides of certain functions in pcm.c and pcm-internal.h **/
224
225bool pcm_play_dma_complete_callback(enum pcm_dma_status status,
226 const void **addr, size_t *size)
227{
228 /* Check status callback first if error */
229 if (status < PCM_DMAST_OK)
230 status = pcm_play_call_status_cb(status);
231
232 size_t sz = pcm_dbl_buf_size[pcm_dbl_buf_num];
233
234 if (status >= PCM_DMAST_OK && sz)
235 {
236 /* Do next chunk */
237 *addr = pcm_dbl_buf[pcm_dbl_buf_num];
238 *size = sz;
239 return true;
240 }
241 else
242 {
243 /* This is a stop chunk or error */
244 pcm_play_stop_int();
245 return false;
246 }
247}
248
249/* Equitably divide large source buffers amongst double buffer frames;
250 frames smaller than or equal to the double buffer chunk size will play
251 in one chunk */
252static void update_frame_params(size_t size)
253{
254 /* multiply by 2 for 32 bit, optimize away to 1 for 16 bit */
255 int count = (size * (sizeof(PCM_DBL_BUF_SIZE_T)/sizeof(int16_t))) / PCM_VOL_SAMPLE_SIZE;
256 frame_count = (count + PCM_PLAY_DBL_BUF_SAMPLES - 1) /
257 PCM_PLAY_DBL_BUF_SAMPLES;
258 int perframe = count / frame_count;
259 frame_size = perframe * PCM_VOL_SAMPLE_SIZE;
260 frame_frac = count - perframe * frame_count;
261 frame_err = 0;
262}
263
264/* Obtain the next buffer and prepare it for pcm driver playback */
265enum pcm_dma_status
266pcm_play_dma_status_callback_int(enum pcm_dma_status status)
267{
268 if (status != PCM_DMAST_STARTED)
269 return status;
270
271 /* divide by 2 for 32 bit, optimize away to 1 for 16 bit */
272 size_t size = pcm_dbl_buf_size[pcm_dbl_buf_num] / (sizeof(PCM_DBL_BUF_SIZE_T)/sizeof(int16_t));
273 const void *addr = src_buf_addr + size;
274
275 size = src_buf_rem - size;
276
277 if (size == 0 && pcm_get_more_int(&addr, &size))
278 {
279 update_frame_params(size);
280 pcm_play_call_status_cb(PCM_DMAST_STARTED);
281 }
282
283 src_buf_addr = addr;
284 src_buf_rem = size;
285
286 if (size != 0)
287 {
288 /* multiply by 2 for 32 bit, optimize away to 1 for 16 bit */
289 size = frame_size / (sizeof(PCM_DBL_BUF_SIZE_T)/sizeof(int16_t));
290
291 if ((frame_err += frame_frac) >= frame_count)
292 {
293 frame_err -= frame_count;
294 size += PCM_SAMPLE_SIZE;
295 }
296 }
297
298 pcm_dbl_buf_num ^= 1;
299 /* multiply by 2 for 32 bit, optimize away to 1 for 16 bit */
300 pcm_dbl_buf_size[pcm_dbl_buf_num] = size * (sizeof(PCM_DBL_BUF_SIZE_T)/sizeof(int16_t));
301 pcm_sw_volume_copy_buffer(pcm_dbl_buf[pcm_dbl_buf_num], addr, size);
302
303 return PCM_DMAST_OK;
304}
305
306/* Prefill double buffer and start pcm driver */
307static void start_pcm(bool reframe)
308{
309 /* Smoothed transition might not have happened so sync now */
310 pcm_sync_pcm_factors();
311
312 pcm_dbl_buf_num = 0;
313 pcm_dbl_buf_size[0] = 0;
314
315 if (reframe)
316 update_frame_params(src_buf_rem);
317
318
319 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
320 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
321
322 pcm_play_dma_start(pcm_dbl_buf[1], pcm_dbl_buf_size[1]);
323}
324
325void pcm_play_dma_start_int(const void *addr, size_t size)
326{
327 src_buf_addr = addr;
328 /* divide by 2 for 32 bit, optimize away to 1 for 16 bit */
329 src_buf_rem = size / (sizeof(PCM_DBL_BUF_SIZE_T)/sizeof(int16_t));
330 start_pcm(true);
331}
332
333void pcm_play_dma_stop_int(void)
334{
335 pcm_play_dma_stop();
336 src_buf_addr = NULL;
337 src_buf_rem = 0;
338}
339
340#endif /* PCM_SW_VOLUME_UNBUFFERED */
341
342
343/** Internal **/
344
345/* Return the scale factor corresponding to the centibel level */
346static uint32_t pcm_centibels_to_factor(int volume)
347{
348 if (volume == PCM_MUTE_LEVEL)
349 return 0; /* mute */
350#if defined(HAVE_SWVOL_32)
351 /*
352 * 32-bit software volume taken from pcm-alsa.c
353 */
354 volume += 48; /* -42dB .. 0dB => 5dB .. 48dB */
355 /* NOTE if vol_dB = 5 then vol_shift = 1 but r = 1 so we do vol_shift - 1 >= 0
356 * otherwise vol_dB >= 0 implies vol_shift >= 2 so vol_shift - 2 >= 0 */
357 int vol_shift = volume / 3;
358 int r = volume % 3;
359 int32_t dig_vol_mult;
360 if(r == 0)
361 dig_vol_mult = 1 << vol_shift;
362 else if(r == 1)
363 dig_vol_mult = 1 << vol_shift | 1 << (vol_shift - 2);
364 else
365 dig_vol_mult = 1 << vol_shift | 1 << (vol_shift - 1);
366 return dig_vol_mult;
367#else /* standard software volume */
368 /* Centibels -> fixedpoint */
369 return (uint32_t)fp_factor(fp_div(volume, 10, PCM_SW_VOLUME_FRACBITS),
370 PCM_SW_VOLUME_FRACBITS);
371#endif /* HAVE_SWVOL_32 */
372}
373
374
375
376/** Public functions **/
377
378/* Produce final pcm scale factor */
379static void pcm_sync_prescaler(void)
380{
381 uint32_t factor_l = vol_factor_l;
382 uint32_t factor_r = vol_factor_r;
383#ifdef AUDIOHW_HAVE_PRESCALER
384 factor_l = fp_mul(prescale_factor, factor_l, PCM_SW_VOLUME_FRACBITS);
385 factor_r = fp_mul(prescale_factor, factor_r, PCM_SW_VOLUME_FRACBITS);
386#endif
387 pcm_play_lock();
388
389 pcm_new_factor_l = MIN(factor_l, PCM_FACTOR_MAX);
390 pcm_new_factor_r = MIN(factor_r, PCM_FACTOR_MAX);
391
392 if (pcm_new_factor_l != pcm_factor_l || pcm_new_factor_r != pcm_factor_r)
393 pcm_scaling_fn = pcm_scale_buffer_trans;
394
395 pcm_play_unlock();
396}
397
398#ifdef AUDIOHW_HAVE_PRESCALER
399/* Set the prescaler value for all PCM playback */
400void pcm_set_prescaler(int prescale)
401{
402 prescale_factor = pcm_centibels_to_factor(-prescale);
403 pcm_sync_prescaler();
404}
405#endif /* AUDIOHW_HAVE_PRESCALER */
406
407/* Set the per-channel volume cut/gain for all PCM playback */
408void pcm_set_master_volume(int vol_l, int vol_r)
409{
410 vol_factor_l = pcm_centibels_to_factor(vol_l);
411 vol_factor_r = pcm_centibels_to_factor(vol_r);
412 pcm_sync_prescaler();
413}