]> code.delx.au - pulseaudio/blob - src/pulsecore/sample-util.c
remap: Change remapping function argument type from void to int16_t / float as approp...
[pulseaudio] / src / pulsecore / sample-util.c
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, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <string.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <errno.h>
31 #include <math.h>
32
33 #include <pulse/timeval.h>
34
35 #include <pulsecore/log.h>
36 #include <pulsecore/core-error.h>
37 #include <pulsecore/macro.h>
38 #include <pulsecore/g711.h>
39 #include <pulsecore/core-util.h>
40 #include <pulsecore/endianmacros.h>
41
42 #include "sample-util.h"
43
44 #define PA_SILENCE_MAX (PA_PAGE_SIZE*16)
45
46 pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec) {
47 void *data;
48
49 pa_assert(b);
50 pa_assert(spec);
51
52 data = pa_memblock_acquire(b);
53 pa_silence_memory(data, pa_memblock_get_length(b), spec);
54 pa_memblock_release(b);
55
56 return b;
57 }
58
59 pa_memchunk* pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec) {
60 void *data;
61
62 pa_assert(c);
63 pa_assert(c->memblock);
64 pa_assert(spec);
65
66 data = pa_memblock_acquire(c->memblock);
67 pa_silence_memory((uint8_t*) data+c->index, c->length, spec);
68 pa_memblock_release(c->memblock);
69
70 return c;
71 }
72
73 static uint8_t silence_byte(pa_sample_format_t format) {
74 switch (format) {
75 case PA_SAMPLE_U8:
76 return 0x80;
77 case PA_SAMPLE_S16LE:
78 case PA_SAMPLE_S16BE:
79 case PA_SAMPLE_S32LE:
80 case PA_SAMPLE_S32BE:
81 case PA_SAMPLE_FLOAT32LE:
82 case PA_SAMPLE_FLOAT32BE:
83 case PA_SAMPLE_S24LE:
84 case PA_SAMPLE_S24BE:
85 case PA_SAMPLE_S24_32LE:
86 case PA_SAMPLE_S24_32BE:
87 return 0;
88 case PA_SAMPLE_ALAW:
89 return 0xd5;
90 case PA_SAMPLE_ULAW:
91 return 0xff;
92 default:
93 pa_assert_not_reached();
94 }
95 }
96
97 void* pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) {
98 pa_assert(p);
99 pa_assert(length > 0);
100 pa_assert(spec);
101
102 memset(p, silence_byte(spec->format), length);
103 return p;
104 }
105
106 size_t pa_frame_align(size_t l, const pa_sample_spec *ss) {
107 size_t fs;
108
109 pa_assert(ss);
110
111 fs = pa_frame_size(ss);
112
113 return (l/fs) * fs;
114 }
115
116 bool pa_frame_aligned(size_t l, const pa_sample_spec *ss) {
117 size_t fs;
118
119 pa_assert(ss);
120
121 fs = pa_frame_size(ss);
122
123 return l % fs == 0;
124 }
125
126 void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, unsigned n) {
127 unsigned c;
128 size_t fs;
129
130 pa_assert(src);
131 pa_assert(channels > 0);
132 pa_assert(dst);
133 pa_assert(ss > 0);
134 pa_assert(n > 0);
135
136 fs = ss * channels;
137
138 for (c = 0; c < channels; c++) {
139 unsigned j;
140 void *d;
141 const void *s;
142
143 s = src[c];
144 d = (uint8_t*) dst + c * ss;
145
146 for (j = 0; j < n; j ++) {
147 memcpy(d, s, (int) ss);
148 s = (uint8_t*) s + ss;
149 d = (uint8_t*) d + fs;
150 }
151 }
152 }
153
154 void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss, unsigned n) {
155 size_t fs;
156 unsigned c;
157
158 pa_assert(src);
159 pa_assert(dst);
160 pa_assert(channels > 0);
161 pa_assert(ss > 0);
162 pa_assert(n > 0);
163
164 fs = ss * channels;
165
166 for (c = 0; c < channels; c++) {
167 unsigned j;
168 const void *s;
169 void *d;
170
171 s = (uint8_t*) src + c * ss;
172 d = dst[c];
173
174 for (j = 0; j < n; j ++) {
175 memcpy(d, s, (int) ss);
176 s = (uint8_t*) s + fs;
177 d = (uint8_t*) d + ss;
178 }
179 }
180 }
181
182 static pa_memblock *silence_memblock_new(pa_mempool *pool, uint8_t c) {
183 pa_memblock *b;
184 size_t length;
185 void *data;
186
187 pa_assert(pool);
188
189 length = PA_MIN(pa_mempool_block_size_max(pool), PA_SILENCE_MAX);
190
191 b = pa_memblock_new(pool, length);
192
193 data = pa_memblock_acquire(b);
194 memset(data, c, length);
195 pa_memblock_release(b);
196
197 pa_memblock_set_is_silence(b, true);
198
199 return b;
200 }
201
202 void pa_silence_cache_init(pa_silence_cache *cache) {
203 pa_assert(cache);
204
205 memset(cache, 0, sizeof(pa_silence_cache));
206 }
207
208 void pa_silence_cache_done(pa_silence_cache *cache) {
209 pa_sample_format_t f;
210 pa_assert(cache);
211
212 for (f = 0; f < PA_SAMPLE_MAX; f++)
213 if (cache->blocks[f])
214 pa_memblock_unref(cache->blocks[f]);
215
216 memset(cache, 0, sizeof(pa_silence_cache));
217 }
218
219 pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, pa_memchunk* ret, const pa_sample_spec *spec, size_t length) {
220 pa_memblock *b;
221 size_t l;
222
223 pa_assert(cache);
224 pa_assert(pa_sample_spec_valid(spec));
225
226 if (!(b = cache->blocks[spec->format]))
227
228 switch (spec->format) {
229 case PA_SAMPLE_U8:
230 cache->blocks[PA_SAMPLE_U8] = b = silence_memblock_new(pool, 0x80);
231 break;
232 case PA_SAMPLE_S16LE:
233 case PA_SAMPLE_S16BE:
234 case PA_SAMPLE_S32LE:
235 case PA_SAMPLE_S32BE:
236 case PA_SAMPLE_S24LE:
237 case PA_SAMPLE_S24BE:
238 case PA_SAMPLE_S24_32LE:
239 case PA_SAMPLE_S24_32BE:
240 case PA_SAMPLE_FLOAT32LE:
241 case PA_SAMPLE_FLOAT32BE:
242 cache->blocks[PA_SAMPLE_S16LE] = b = silence_memblock_new(pool, 0);
243 cache->blocks[PA_SAMPLE_S16BE] = pa_memblock_ref(b);
244 cache->blocks[PA_SAMPLE_S32LE] = pa_memblock_ref(b);
245 cache->blocks[PA_SAMPLE_S32BE] = pa_memblock_ref(b);
246 cache->blocks[PA_SAMPLE_S24LE] = pa_memblock_ref(b);
247 cache->blocks[PA_SAMPLE_S24BE] = pa_memblock_ref(b);
248 cache->blocks[PA_SAMPLE_S24_32LE] = pa_memblock_ref(b);
249 cache->blocks[PA_SAMPLE_S24_32BE] = pa_memblock_ref(b);
250 cache->blocks[PA_SAMPLE_FLOAT32LE] = pa_memblock_ref(b);
251 cache->blocks[PA_SAMPLE_FLOAT32BE] = pa_memblock_ref(b);
252 break;
253 case PA_SAMPLE_ALAW:
254 cache->blocks[PA_SAMPLE_ALAW] = b = silence_memblock_new(pool, 0xd5);
255 break;
256 case PA_SAMPLE_ULAW:
257 cache->blocks[PA_SAMPLE_ULAW] = b = silence_memblock_new(pool, 0xff);
258 break;
259 default:
260 pa_assert_not_reached();
261 }
262
263 pa_assert(b);
264
265 ret->memblock = pa_memblock_ref(b);
266
267 l = pa_memblock_get_length(b);
268 if (length > l || length == 0)
269 length = l;
270
271 ret->length = pa_frame_align(length, spec);
272 ret->index = 0;
273
274 return ret;
275 }
276
277 void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const void *src, size_t sstr, unsigned n) {
278 const float *s;
279 float *d;
280
281 s = src; d = dst;
282
283 if (format == PA_SAMPLE_FLOAT32NE) {
284 for (; n > 0; n--) {
285 float f;
286
287 f = *s;
288 *d = PA_CLAMP_UNLIKELY(f, -1.0f, 1.0f);
289
290 s = (const float*) ((const uint8_t*) s + sstr);
291 d = (float*) ((uint8_t*) d + dstr);
292 }
293 } else {
294 pa_assert(format == PA_SAMPLE_FLOAT32RE);
295
296 for (; n > 0; n--) {
297 float f;
298
299 f = PA_FLOAT32_SWAP(*s);
300 f = PA_CLAMP_UNLIKELY(f, -1.0f, 1.0f);
301 *d = PA_FLOAT32_SWAP(f);
302
303 s = (const float*) ((const uint8_t*) s + sstr);
304 d = (float*) ((uint8_t*) d + dstr);
305 }
306 }
307 }
308
309 /* Similar to pa_bytes_to_usec() but rounds up, not down */
310
311 pa_usec_t pa_bytes_to_usec_round_up(uint64_t length, const pa_sample_spec *spec) {
312 size_t fs;
313 pa_usec_t usec;
314
315 pa_assert(spec);
316
317 fs = pa_frame_size(spec);
318 length = (length + fs - 1) / fs;
319
320 usec = (pa_usec_t) length * PA_USEC_PER_SEC;
321
322 return (usec + spec->rate - 1) / spec->rate;
323 }
324
325 /* Similar to pa_usec_to_bytes() but rounds up, not down */
326
327 size_t pa_usec_to_bytes_round_up(pa_usec_t t, const pa_sample_spec *spec) {
328 uint64_t u;
329 pa_assert(spec);
330
331 u = (uint64_t) t * (uint64_t) spec->rate;
332
333 u = (u + PA_USEC_PER_SEC - 1) / PA_USEC_PER_SEC;
334
335 u *= pa_frame_size(spec);
336
337 return (size_t) u;
338 }
339
340 void pa_memchunk_dump_to_file(pa_memchunk *c, const char *fn) {
341 FILE *f;
342 void *p;
343
344 pa_assert(c);
345 pa_assert(fn);
346
347 /* Only for debugging purposes */
348
349 f = pa_fopen_cloexec(fn, "a");
350
351 if (!f) {
352 pa_log_warn("Failed to open '%s': %s", fn, pa_cstrerror(errno));
353 return;
354 }
355
356 p = pa_memblock_acquire(c->memblock);
357
358 if (fwrite((uint8_t*) p + c->index, 1, c->length, f) != c->length)
359 pa_log_warn("Failed to write to '%s': %s", fn, pa_cstrerror(errno));
360
361 pa_memblock_release(c->memblock);
362
363 fclose(f);
364 }
365
366 static void calc_sine(float *f, size_t l, double freq) {
367 size_t i;
368
369 l /= sizeof(float);
370
371 for (i = 0; i < l; i++)
372 *(f++) = (float) 0.5f * sin((double) i*M_PI*2*freq / (double) l);
373 }
374
375 void pa_memchunk_sine(pa_memchunk *c, pa_mempool *pool, unsigned rate, unsigned freq) {
376 size_t l;
377 unsigned gcd, n;
378 void *p;
379
380 pa_memchunk_reset(c);
381
382 gcd = pa_gcd(rate, freq);
383 n = rate / gcd;
384
385 l = pa_mempool_block_size_max(pool) / sizeof(float);
386
387 l /= n;
388 if (l <= 0) l = 1;
389 l *= n;
390
391 c->length = l * sizeof(float);
392 c->memblock = pa_memblock_new(pool, c->length);
393
394 p = pa_memblock_acquire(c->memblock);
395 calc_sine(p, c->length, freq * l / rate);
396 pa_memblock_release(c->memblock);
397 }
398
399 size_t pa_convert_size(size_t size, const pa_sample_spec *from, const pa_sample_spec *to) {
400 pa_usec_t usec;
401
402 pa_assert(from);
403 pa_assert(to);
404
405 usec = pa_bytes_to_usec_round_up(size, from);
406 return pa_usec_to_bytes_round_up(usec, to);
407 }