summaryrefslogtreecommitdiff
path: root/firmware/pcm_sw_volume.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/pcm_sw_volume.c')
-rw-r--r--firmware/pcm_sw_volume.c121
1 files changed, 79 insertions, 42 deletions
diff --git a/firmware/pcm_sw_volume.c b/firmware/pcm_sw_volume.c
index 473e63c7cb..eb77fe0c6b 100644
--- a/firmware/pcm_sw_volume.c
+++ b/firmware/pcm_sw_volume.c
@@ -26,63 +26,91 @@
26#include "fixedpoint.h" 26#include "fixedpoint.h"
27#include "pcm_sw_volume.h" 27#include "pcm_sw_volume.h"
28 28
29/* source buffer from client */ 29/* volume factors set by pcm_set_master_volume */
30static const void * volatile src_buf_addr = NULL; 30static uint32_t vol_factor_l = 0, vol_factor_r = 0;
31static size_t volatile src_buf_rem = 0;
32 31
33#define PCM_PLAY_DBL_BUF_SIZE (PCM_PLAY_DBL_BUF_SAMPLE*PCM_SAMPLE_SIZE)
34
35/* double buffer and frame length control */
36static int16_t pcm_dbl_buf[2][PCM_PLAY_DBL_BUF_SAMPLES*2]
37 PCM_DBL_BUF_BSS MEM_ALIGN_ATTR;
38static size_t pcm_dbl_buf_size[2];
39static int pcm_dbl_buf_num = 0;
40static size_t frame_size;
41static unsigned int frame_count, frame_err, frame_frac;
42
43static int32_t vol_factor_l = 0, vol_factor_r = 0;
44#ifdef AUDIOHW_HAVE_PRESCALER 32#ifdef AUDIOHW_HAVE_PRESCALER
45static int32_t prescale_factor = PCM_FACTOR_UNITY; 33/* prescale factor set by pcm_set_prescaler */
34static uint32_t prescale_factor = PCM_FACTOR_UNITY;
46#endif /* AUDIOHW_HAVE_PRESCALER */ 35#endif /* AUDIOHW_HAVE_PRESCALER */
47 36
48/* pcm scaling factors */ 37/* final pcm scaling factors */
49static int32_t pcm_factor_l = 0, pcm_factor_r = 0; 38static uint32_t pcm_factor_l = 0, pcm_factor_r = 0;
50
51#define PCM_FACTOR_CLIP(f) \
52 MAX(MIN((f), PCM_FACTOR_MAX), PCM_FACTOR_MIN)
53#define PCM_SCALE_SAMPLE(f, s) \
54 (((f) * (s) + PCM_FACTOR_UNITY/2) >> PCM_FACTOR_BITS)
55 39
40/***
41 ** Volume scaling routine
42 ** If unbuffered, called externally by pcm driver
43 **/
56 44
57/* TODO: #include CPU-optimized routines and move this to /firmware/asm */ 45/* TODO: #include CPU-optimized routines and move this to /firmware/asm */
58static inline void pcm_copy_buffer(int16_t *dst, const int16_t *src, 46
59 size_t size) 47#if PCM_SW_VOLUME_FRACBITS <= 16
48#define PCM_F_T int32_t
49#else
50#define PCM_F_T int64_t /* Requires large integer math */
51#endif /* PCM_SW_VOLUME_FRACBITS */
52
53static inline int32_t pcm_scale_sample(PCM_F_T f, int32_t s)
60{ 54{
61 int32_t factor_l = pcm_factor_l; 55 return (f * s + (PCM_F_T)PCM_FACTOR_UNITY/2) >> PCM_SW_VOLUME_FRACBITS;
62 int32_t factor_r = pcm_factor_r; 56}
63 57
64 if (LIKELY(factor_l <= PCM_FACTOR_UNITY && factor_r <= PCM_FACTOR_UNITY)) 58/* Copies buffer with volume scaling applied */
59#ifndef PCM_SW_VOLUME_UNBUFFERED
60static inline
61#endif
62void pcm_sw_volume_copy_buffer(void *dst, const void *src, size_t size)
63{
64 int16_t *d = dst;
65 const int16_t *s = src;
66 uint32_t factor_l = pcm_factor_l;
67 uint32_t factor_r = pcm_factor_r;
68
69 if (factor_l == PCM_FACTOR_UNITY && factor_r == PCM_FACTOR_UNITY)
70 {
71 /* Both unity */
72 memcpy(dst, src, size);
73 }
74 else if (LIKELY(factor_l <= PCM_FACTOR_UNITY &&
75 factor_r <= PCM_FACTOR_UNITY))
65 { 76 {
66 /* All cut or unity */ 77 /* Either cut, both <= UNITY */
67 while (size) 78 while (size)
68 { 79 {
69 *dst++ = PCM_SCALE_SAMPLE(factor_l, *src++); 80 *d++ = pcm_scale_sample(factor_l, *s++);
70 *dst++ = PCM_SCALE_SAMPLE(factor_r, *src++); 81 *d++ = pcm_scale_sample(factor_r, *s++);
71 size -= PCM_SAMPLE_SIZE; 82 size -= PCM_SAMPLE_SIZE;
72 } 83 }
73 } 84 }
74 else 85 else
75 { 86 {
76 /* Any positive gain requires clipping */ 87 /* Either positive gain, requires clipping */
77 while (size) 88 while (size)
78 { 89 {
79 *dst++ = clip_sample_16(PCM_SCALE_SAMPLE(factor_l, *src++)); 90 *d++ = clip_sample_16(pcm_scale_sample(factor_l, *s++));
80 *dst++ = clip_sample_16(PCM_SCALE_SAMPLE(factor_r, *src++)); 91 *d++ = clip_sample_16(pcm_scale_sample(factor_r, *s++));
81 size -= PCM_SAMPLE_SIZE; 92 size -= PCM_SAMPLE_SIZE;
82 } 93 }
83 } 94 }
84} 95}
85 96
97#ifndef PCM_SW_VOLUME_UNBUFFERED
98/* source buffer from client */
99static const void * volatile src_buf_addr = NULL;
100static size_t volatile src_buf_rem = 0;
101
102#define PCM_PLAY_DBL_BUF_SIZE (PCM_PLAY_DBL_BUF_SAMPLE*PCM_SAMPLE_SIZE)
103
104/* double buffer and frame length control */
105static int16_t pcm_dbl_buf[2][PCM_PLAY_DBL_BUF_SAMPLES*2]
106 PCM_DBL_BUF_BSS MEM_ALIGN_ATTR;
107static size_t pcm_dbl_buf_size[2];
108static int pcm_dbl_buf_num = 0;
109static size_t frame_size;
110static unsigned int frame_count, frame_err, frame_frac;
111
112/** Overrides of certain functions in pcm.c and pcm-internal.h **/
113
86bool pcm_play_dma_complete_callback(enum pcm_dma_status status, 114bool pcm_play_dma_complete_callback(enum pcm_dma_status status,
87 const void **addr, size_t *size) 115 const void **addr, size_t *size)
88{ 116{
@@ -155,7 +183,7 @@ pcm_play_dma_status_callback_int(enum pcm_dma_status status)
155 183
156 pcm_dbl_buf_num ^= 1; 184 pcm_dbl_buf_num ^= 1;
157 pcm_dbl_buf_size[pcm_dbl_buf_num] = size; 185 pcm_dbl_buf_size[pcm_dbl_buf_num] = size;
158 pcm_copy_buffer(pcm_dbl_buf[pcm_dbl_buf_num], addr, size); 186 pcm_sw_volume_copy_buffer(pcm_dbl_buf[pcm_dbl_buf_num], addr, size);
159 187
160 return PCM_DMAST_OK; 188 return PCM_DMAST_OK;
161} 189}
@@ -216,27 +244,36 @@ const void * pcm_play_dma_get_peak_buffer_int(int *count)
216 return NULL; 244 return NULL;
217} 245}
218 246
247#endif /* PCM_SW_VOLUME_UNBUFFERED */
248
249
250/** Internal **/
251
219/* Return the scale factor corresponding to the centibel level */ 252/* Return the scale factor corresponding to the centibel level */
220static int32_t pcm_centibels_to_factor(int volume) 253static uint32_t pcm_centibels_to_factor(int volume)
221{ 254{
222 if (volume == PCM_MUTE_LEVEL) 255 if (volume == PCM_MUTE_LEVEL)
223 return 0; /* mute */ 256 return 0; /* mute */
224 257
225 /* Centibels -> fixedpoint */ 258 /* Centibels -> fixedpoint */
226 return fp_factor(fp_div(volume, 10, PCM_FACTOR_BITS), PCM_FACTOR_BITS); 259 return (uint32_t)fp_factor(fp_div(volume, 10, PCM_SW_VOLUME_FRACBITS),
260 PCM_SW_VOLUME_FRACBITS);
227} 261}
228 262
263
264/** Public functions **/
265
229/* Produce final pcm scale factor */ 266/* Produce final pcm scale factor */
230static void pcm_sync_prescaler(void) 267static void pcm_sync_prescaler(void)
231{ 268{
232 int32_t factor_l = vol_factor_l; 269 uint32_t factor_l = vol_factor_l;
233 int32_t factor_r = vol_factor_r; 270 uint32_t factor_r = vol_factor_r;
234#ifdef AUDIOHW_HAVE_PRESCALER 271#ifdef AUDIOHW_HAVE_PRESCALER
235 factor_l = fp_mul(prescale_factor, factor_l, PCM_FACTOR_BITS); 272 factor_l = fp_mul(prescale_factor, factor_l, PCM_SW_VOLUME_FRACBITS);
236 factor_r = fp_mul(prescale_factor, factor_r, PCM_FACTOR_BITS); 273 factor_r = fp_mul(prescale_factor, factor_r, PCM_SW_VOLUME_FRACBITS);
237#endif 274#endif
238 pcm_factor_l = PCM_FACTOR_CLIP(factor_l); 275 pcm_factor_l = MIN(factor_l, PCM_FACTOR_MAX);
239 pcm_factor_r = PCM_FACTOR_CLIP(factor_r); 276 pcm_factor_r = MIN(factor_r, PCM_FACTOR_MAX);
240} 277}
241 278
242#ifdef AUDIOHW_HAVE_PRESCALER 279#ifdef AUDIOHW_HAVE_PRESCALER