diff options
Diffstat (limited to 'firmware/pcm_sw_volume.c')
-rw-r--r-- | firmware/pcm_sw_volume.c | 121 |
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 */ |
30 | static const void * volatile src_buf_addr = NULL; | 30 | static uint32_t vol_factor_l = 0, vol_factor_r = 0; |
31 | static 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 */ | ||
36 | static int16_t pcm_dbl_buf[2][PCM_PLAY_DBL_BUF_SAMPLES*2] | ||
37 | PCM_DBL_BUF_BSS MEM_ALIGN_ATTR; | ||
38 | static size_t pcm_dbl_buf_size[2]; | ||
39 | static int pcm_dbl_buf_num = 0; | ||
40 | static size_t frame_size; | ||
41 | static unsigned int frame_count, frame_err, frame_frac; | ||
42 | |||
43 | static int32_t vol_factor_l = 0, vol_factor_r = 0; | ||
44 | #ifdef AUDIOHW_HAVE_PRESCALER | 32 | #ifdef AUDIOHW_HAVE_PRESCALER |
45 | static int32_t prescale_factor = PCM_FACTOR_UNITY; | 33 | /* prescale factor set by pcm_set_prescaler */ |
34 | static 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 */ |
49 | static int32_t pcm_factor_l = 0, pcm_factor_r = 0; | 38 | static 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 */ |
58 | static 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 | |||
53 | static 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 | ||
60 | static inline | ||
61 | #endif | ||
62 | void 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 */ | ||
99 | static const void * volatile src_buf_addr = NULL; | ||
100 | static 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 */ | ||
105 | static int16_t pcm_dbl_buf[2][PCM_PLAY_DBL_BUF_SAMPLES*2] | ||
106 | PCM_DBL_BUF_BSS MEM_ALIGN_ATTR; | ||
107 | static size_t pcm_dbl_buf_size[2]; | ||
108 | static int pcm_dbl_buf_num = 0; | ||
109 | static size_t frame_size; | ||
110 | static unsigned int frame_count, frame_err, frame_frac; | ||
111 | |||
112 | /** Overrides of certain functions in pcm.c and pcm-internal.h **/ | ||
113 | |||
86 | bool pcm_play_dma_complete_callback(enum pcm_dma_status status, | 114 | bool 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 */ |
220 | static int32_t pcm_centibels_to_factor(int volume) | 253 | static 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 */ |
230 | static void pcm_sync_prescaler(void) | 267 | static 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 |