1/***
2  This file is part of PulseAudio.
3
4  Copyright 2004-2006 Lennart Poettering
5  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7  PulseAudio is free software; you can redistribute it and/or modify
8  it under the terms of the GNU Lesser General Public License as published
9  by the Free Software Foundation; either version 2.1 of the License,
10  or (at your option) any later version.
11
12  PulseAudio is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  General Public License for more details.
16
17  You should have received a copy of the GNU Lesser General Public License
18  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19***/
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include <pulsecore/macro.h>
26#include <pulsecore/g711.h>
27#include <pulsecore/endianmacros.h>
28
29#include "sample-util.h"
30
31static void pa_volume_u8_c(uint8_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
32    unsigned channel;
33
34    for (channel = 0; length; length--) {
35        int32_t t = pa_mult_s16_volume(*samples - 0x80, volumes[channel]);
36
37        t = PA_CLAMP_UNLIKELY(t, -0x80, 0x7F);
38        *samples++ = (uint8_t) (t + 0x80);
39
40        if (PA_UNLIKELY(++channel >= channels))
41            channel = 0;
42    }
43}
44
45static void pa_volume_alaw_c(uint8_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
46    unsigned channel;
47
48    for (channel = 0; length; length--) {
49        int32_t t = pa_mult_s16_volume(st_alaw2linear16(*samples), volumes[channel]);
50
51        t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
52        *samples++ = (uint8_t) st_13linear2alaw((int16_t) t >> 3);
53
54        if (PA_UNLIKELY(++channel >= channels))
55            channel = 0;
56    }
57}
58
59static void pa_volume_ulaw_c(uint8_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
60    unsigned channel;
61
62    for (channel = 0; length; length--) {
63        int32_t t = pa_mult_s16_volume(st_ulaw2linear16(*samples), volumes[channel]);
64
65        t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
66        *samples++ = (uint8_t) st_14linear2ulaw((int16_t) t >> 2);
67
68        if (PA_UNLIKELY(++channel >= channels))
69            channel = 0;
70    }
71}
72
73static void pa_volume_s16ne_c(int16_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
74    unsigned channel;
75
76    length /= sizeof(int16_t);
77
78    for (channel = 0; length; length--) {
79        int32_t t = pa_mult_s16_volume(*samples, volumes[channel]);
80
81        t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
82        *samples++ = (int16_t) t;
83
84        if (PA_UNLIKELY(++channel >= channels))
85            channel = 0;
86    }
87}
88
89static void pa_volume_s16re_c(int16_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
90    unsigned channel;
91
92    length /= sizeof(int16_t);
93
94    for (channel = 0; length; length--) {
95        int32_t t = pa_mult_s16_volume(PA_INT16_SWAP(*samples), volumes[channel]);
96
97        t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
98        *samples++ = PA_INT16_SWAP((int16_t) t);
99
100        if (PA_UNLIKELY(++channel >= channels))
101            channel = 0;
102    }
103}
104
105static void pa_volume_float32ne_c(float *samples, const float *volumes, unsigned channels, unsigned length) {
106    unsigned channel;
107
108    length /= sizeof(float);
109
110    for (channel = 0; length; length--) {
111        *samples++ *= volumes[channel];
112
113        if (PA_UNLIKELY(++channel >= channels))
114            channel = 0;
115    }
116}
117
118static void pa_volume_float32re_c(float *samples, const float *volumes, unsigned channels, unsigned length) {
119    unsigned channel;
120
121    length /= sizeof(float);
122
123    for (channel = 0; length; length--) {
124        float t;
125
126        t = PA_READ_FLOAT32RE(samples);
127        t *= volumes[channel];
128        PA_WRITE_FLOAT32RE(samples++, t);
129
130        if (PA_UNLIKELY(++channel >= channels))
131            channel = 0;
132    }
133}
134
135static void pa_volume_s32ne_c(int32_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
136    unsigned channel;
137
138    length /= sizeof(int32_t);
139
140    for (channel = 0; length; length--) {
141        int64_t t;
142
143        t = (int64_t)(*samples);
144        t = (t * volumes[channel]) >> 16;
145        t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
146        *samples++ = (int32_t) t;
147
148        if (PA_UNLIKELY(++channel >= channels))
149            channel = 0;
150    }
151}
152
153static void pa_volume_s32re_c(int32_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
154    unsigned channel;
155
156    length /= sizeof(int32_t);
157
158    for (channel = 0; length; length--) {
159        int64_t t;
160
161        t = (int64_t) PA_INT32_SWAP(*samples);
162        t = (t * volumes[channel]) >> 16;
163        t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
164        *samples++ = PA_INT32_SWAP((int32_t) t);
165
166        if (PA_UNLIKELY(++channel >= channels))
167            channel = 0;
168    }
169}
170
171static void pa_volume_s24ne_c(uint8_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
172    unsigned channel;
173    uint8_t *e;
174
175    e = samples + length;
176
177    for (channel = 0; samples < e; samples += 3) {
178        int64_t t;
179
180        t = (int64_t)((int32_t) (PA_READ24NE(samples) << 8));
181        t = (t * volumes[channel]) >> 16;
182        t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
183        PA_WRITE24NE(samples, ((uint32_t) (int32_t) t) >> 8);
184
185        if (PA_UNLIKELY(++channel >= channels))
186            channel = 0;
187    }
188}
189
190static void pa_volume_s24re_c(uint8_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
191    unsigned channel;
192    uint8_t *e;
193
194    e = samples + length;
195
196    for (channel = 0; samples < e; samples += 3) {
197        int64_t t;
198
199        t = (int64_t)((int32_t) (PA_READ24RE(samples) << 8));
200        t = (t * volumes[channel]) >> 16;
201        t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
202        PA_WRITE24RE(samples, ((uint32_t) (int32_t) t) >> 8);
203
204        if (PA_UNLIKELY(++channel >= channels))
205            channel = 0;
206    }
207}
208
209static void pa_volume_s24_32ne_c(uint32_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
210    unsigned channel;
211
212    length /= sizeof(uint32_t);
213
214    for (channel = 0; length; length--) {
215        int64_t t;
216
217        t = (int64_t) ((int32_t) (*samples << 8));
218        t = (t * volumes[channel]) >> 16;
219        t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
220        *samples++ = ((uint32_t) ((int32_t) t)) >> 8;
221
222        if (PA_UNLIKELY(++channel >= channels))
223            channel = 0;
224    }
225}
226
227static void pa_volume_s24_32re_c(uint32_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
228    unsigned channel;
229
230    length /= sizeof(uint32_t);
231
232    for (channel = 0; length; length--) {
233        int64_t t;
234
235        t = (int64_t) ((int32_t) (PA_UINT32_SWAP(*samples) << 8));
236        t = (t * volumes[channel]) >> 16;
237        t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
238        *samples++ = PA_UINT32_SWAP(((uint32_t) ((int32_t) t)) >> 8);
239
240        if (PA_UNLIKELY(++channel >= channels))
241            channel = 0;
242    }
243}
244
245static pa_do_volume_func_t do_volume_table[] = {
246    [PA_SAMPLE_U8]        = (pa_do_volume_func_t) pa_volume_u8_c,
247    [PA_SAMPLE_ALAW]      = (pa_do_volume_func_t) pa_volume_alaw_c,
248    [PA_SAMPLE_ULAW]      = (pa_do_volume_func_t) pa_volume_ulaw_c,
249    [PA_SAMPLE_S16NE]     = (pa_do_volume_func_t) pa_volume_s16ne_c,
250    [PA_SAMPLE_S16RE]     = (pa_do_volume_func_t) pa_volume_s16re_c,
251    [PA_SAMPLE_FLOAT32NE] = (pa_do_volume_func_t) pa_volume_float32ne_c,
252    [PA_SAMPLE_FLOAT32RE] = (pa_do_volume_func_t) pa_volume_float32re_c,
253    [PA_SAMPLE_S32NE]     = (pa_do_volume_func_t) pa_volume_s32ne_c,
254    [PA_SAMPLE_S32RE]     = (pa_do_volume_func_t) pa_volume_s32re_c,
255    [PA_SAMPLE_S24NE]     = (pa_do_volume_func_t) pa_volume_s24ne_c,
256    [PA_SAMPLE_S24RE]     = (pa_do_volume_func_t) pa_volume_s24re_c,
257    [PA_SAMPLE_S24_32NE]  = (pa_do_volume_func_t) pa_volume_s24_32ne_c,
258    [PA_SAMPLE_S24_32RE]  = (pa_do_volume_func_t) pa_volume_s24_32re_c
259};
260
261pa_do_volume_func_t pa_get_volume_func(pa_sample_format_t f) {
262    pa_assert(pa_sample_format_valid(f));
263
264    return do_volume_table[f];
265}
266
267void pa_set_volume_func(pa_sample_format_t f, pa_do_volume_func_t func) {
268    pa_assert(pa_sample_format_valid(f));
269
270    do_volume_table[f] = func;
271}
272