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.c131
1 files changed, 103 insertions, 28 deletions
diff --git a/firmware/pcm_sw_volume.c b/firmware/pcm_sw_volume.c
index eb77fe0c6b..8b6c9220fd 100644
--- a/firmware/pcm_sw_volume.c
+++ b/firmware/pcm_sw_volume.c
@@ -35,62 +35,127 @@ static uint32_t prescale_factor = PCM_FACTOR_UNITY;
35#endif /* AUDIOHW_HAVE_PRESCALER */ 35#endif /* AUDIOHW_HAVE_PRESCALER */
36 36
37/* final pcm scaling factors */ 37/* final pcm scaling factors */
38static uint32_t pcm_new_factor_l = 0, pcm_new_factor_r = 0;
38static uint32_t pcm_factor_l = 0, pcm_factor_r = 0; 39static uint32_t pcm_factor_l = 0, pcm_factor_r = 0;
40static typeof (memcpy) *pcm_scaling_fn = NULL;
39 41
40/*** 42/***
41 ** Volume scaling routine 43 ** Volume scaling routines
42 ** If unbuffered, called externally by pcm driver 44 ** If unbuffered, called externally by pcm driver
43 **/ 45 **/
44 46
45/* TODO: #include CPU-optimized routines and move this to /firmware/asm */ 47/* TODO: #include CPU-optimized routines and move this to /firmware/asm */
46
47#if PCM_SW_VOLUME_FRACBITS <= 16 48#if PCM_SW_VOLUME_FRACBITS <= 16
48#define PCM_F_T int32_t 49#define PCM_F_T int32_t
49#else 50#else
50#define PCM_F_T int64_t /* Requires large integer math */ 51#define PCM_F_T int64_t /* Requires large integer math */
51#endif /* PCM_SW_VOLUME_FRACBITS */ 52#endif /* PCM_SW_VOLUME_FRACBITS */
52 53
54/* Scale and round sample by PCM factor */
53static inline int32_t pcm_scale_sample(PCM_F_T f, int32_t s) 55static inline int32_t pcm_scale_sample(PCM_F_T f, int32_t s)
54{ 56{
55 return (f * s + (PCM_F_T)PCM_FACTOR_UNITY/2) >> PCM_SW_VOLUME_FRACBITS; 57 return (f * s + (PCM_F_T)PCM_FACTOR_UNITY/2) >> PCM_SW_VOLUME_FRACBITS;
56} 58}
57 59
58/* Copies buffer with volume scaling applied */ 60/* Both UNITY, use direct copy */
61/* static void * memcpy(void *dst, const void *src, size_t size); */
62
63/* Either cut (both <= UNITY), no clipping needed */
64static void * pcm_scale_buffer_cut(void *dst, const void *src, size_t size)
65{
66 int16_t *d = dst;
67 const int16_t *s = src;
68 uint32_t factor_l = pcm_factor_l, factor_r = pcm_factor_r;
69
70 while (size)
71 {
72 *d++ = pcm_scale_sample(factor_l, *s++);
73 *d++ = pcm_scale_sample(factor_r, *s++);
74 size -= PCM_SAMPLE_SIZE;
75 }
76
77 return dst;
78}
79
80/* Either boost (any > UNITY) requires clipping */
81static void * pcm_scale_buffer_boost(void *dst, const void *src, size_t size)
82{
83 int16_t *d = dst;
84 const int16_t *s = src;
85 uint32_t factor_l = pcm_factor_l, factor_r = pcm_factor_r;
86
87 while (size)
88 {
89 *d++ = clip_sample_16(pcm_scale_sample(factor_l, *s++));
90 *d++ = clip_sample_16(pcm_scale_sample(factor_r, *s++));
91 size -= PCM_SAMPLE_SIZE;
92 }
93
94 return dst;
95}
96
97/* Transition the volume change smoothly across a frame */
98static void * pcm_scale_buffer_trans(void *dst, const void *src, size_t size)
99{
100 int16_t *d = dst;
101 const int16_t *s = src;
102 uint32_t factor_l = pcm_factor_l, factor_r = pcm_factor_r;
103
104 /* Transition from the old value to the new value using an inverted cosinus
105 from PI..0 in order to minimize amplitude-modulated harmonics generation
106 (zipper effects). */
107 uint32_t new_factor_l = pcm_new_factor_l;
108 uint32_t new_factor_r = pcm_new_factor_r;
109
110 int32_t diff_l = (int32_t)new_factor_l - (int32_t)factor_l;
111 int32_t diff_r = (int32_t)new_factor_r - (int32_t)factor_r;
112
113 for (size_t done = 0; done < size; done += PCM_SAMPLE_SIZE)
114 {
115 int32_t sweep = (1 << 14) - fp14_cos(180*done / size); /* 0.0..2.0 */
116 uint32_t f_l = fp_mul(sweep, diff_l, 15) + factor_l;
117 uint32_t f_r = fp_mul(sweep, diff_r, 15) + factor_r;
118 *d++ = clip_sample_16(pcm_scale_sample(f_l, *s++));
119 *d++ = clip_sample_16(pcm_scale_sample(f_r, *s++));
120 }
121
122 /* Select steady-state operation */
123 pcm_sync_pcm_factors();
124
125 return dst;
126}
127
128/* Called by completion routine to scale the next buffer of samples */
59#ifndef PCM_SW_VOLUME_UNBUFFERED 129#ifndef PCM_SW_VOLUME_UNBUFFERED
60static inline 130static inline
61#endif 131#endif
62void pcm_sw_volume_copy_buffer(void *dst, const void *src, size_t size) 132void pcm_sw_volume_copy_buffer(void *dst, const void *src, size_t size)
63{ 133{
64 int16_t *d = dst; 134 pcm_scaling_fn(dst, src, size);
65 const int16_t *s = src; 135}
66 uint32_t factor_l = pcm_factor_l;
67 uint32_t factor_r = pcm_factor_r;
68 136
69 if (factor_l == PCM_FACTOR_UNITY && factor_r == PCM_FACTOR_UNITY) 137/* Assign the new scaling function for normal steady-state operation */
138void pcm_sync_pcm_factors(void)
139{
140 uint32_t new_factor_l = pcm_new_factor_l;
141 uint32_t new_factor_r = pcm_new_factor_r;
142
143 pcm_factor_l = new_factor_l;
144 pcm_factor_r = new_factor_r;
145
146 if (new_factor_l == PCM_FACTOR_UNITY &&
147 new_factor_r == PCM_FACTOR_UNITY)
70 { 148 {
71 /* Both unity */ 149 pcm_scaling_fn = memcpy;
72 memcpy(dst, src, size);
73 } 150 }
74 else if (LIKELY(factor_l <= PCM_FACTOR_UNITY && 151 else if (new_factor_l <= PCM_FACTOR_UNITY &&
75 factor_r <= PCM_FACTOR_UNITY)) 152 new_factor_r <= PCM_FACTOR_UNITY)
76 { 153 {
77 /* Either cut, both <= UNITY */ 154 pcm_scaling_fn = pcm_scale_buffer_cut;
78 while (size)
79 {
80 *d++ = pcm_scale_sample(factor_l, *s++);
81 *d++ = pcm_scale_sample(factor_r, *s++);
82 size -= PCM_SAMPLE_SIZE;
83 }
84 } 155 }
85 else 156 else
86 { 157 {
87 /* Either positive gain, requires clipping */ 158 pcm_scaling_fn = pcm_scale_buffer_boost;
88 while (size)
89 {
90 *d++ = clip_sample_16(pcm_scale_sample(factor_l, *s++));
91 *d++ = clip_sample_16(pcm_scale_sample(factor_r, *s++));
92 size -= PCM_SAMPLE_SIZE;
93 }
94 } 159 }
95} 160}
96 161
@@ -191,6 +256,9 @@ pcm_play_dma_status_callback_int(enum pcm_dma_status status)
191/* Prefill double buffer and start pcm driver */ 256/* Prefill double buffer and start pcm driver */
192static void start_pcm(bool reframe) 257static void start_pcm(bool reframe)
193{ 258{
259 /* Smoothed transition might not have happened so sync now */
260 pcm_sync_pcm_factors();
261
194 pcm_dbl_buf_num = 0; 262 pcm_dbl_buf_num = 0;
195 pcm_dbl_buf_size[0] = 0; 263 pcm_dbl_buf_size[0] = 0;
196 264
@@ -272,8 +340,15 @@ static void pcm_sync_prescaler(void)
272 factor_l = fp_mul(prescale_factor, factor_l, PCM_SW_VOLUME_FRACBITS); 340 factor_l = fp_mul(prescale_factor, factor_l, PCM_SW_VOLUME_FRACBITS);
273 factor_r = fp_mul(prescale_factor, factor_r, PCM_SW_VOLUME_FRACBITS); 341 factor_r = fp_mul(prescale_factor, factor_r, PCM_SW_VOLUME_FRACBITS);
274#endif 342#endif
275 pcm_factor_l = MIN(factor_l, PCM_FACTOR_MAX); 343 pcm_play_lock();
276 pcm_factor_r = MIN(factor_r, PCM_FACTOR_MAX); 344
345 pcm_new_factor_l = MIN(factor_l, PCM_FACTOR_MAX);
346 pcm_new_factor_r = MIN(factor_r, PCM_FACTOR_MAX);
347
348 if (pcm_new_factor_l != pcm_factor_l || pcm_new_factor_r != pcm_factor_r)
349 pcm_scaling_fn = pcm_scale_buffer_trans;
350
351 pcm_play_unlock();
277} 352}
278 353
279#ifdef AUDIOHW_HAVE_PRESCALER 354#ifdef AUDIOHW_HAVE_PRESCALER