source: mainline/uspace/lib/pcm/src/format.c@ 959b7ec

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 959b7ec was bc56f30, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 6 years ago

Make some libc and libposix headers usable in C++

These headers either get included from standard C++ headers,
or are standard themselves, which means any unnamespaced nonstandard
identifiers are a problem. This commit attempts to fix those
issues, and removes hacks previously used in libcpp to work around it.

  • Property mode set to 100644
File size: 9.7 KB
Line 
1/*
2 * Copyright (c) 2012 Jan Vesely
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup audio
30 * @brief HelenOS sound server
31 * @{
32 */
33/** @file
34 */
35
36#include <assert.h>
37#include <byteorder.h>
38#include <errno.h>
39#include <macros.h>
40#include <stdio.h>
41#include <inttypes.h>
42#include <limits.h>
43
44#include "format.h"
45
46// TODO float endian?
47#define float_le2host(x) (x)
48#define float_be2host(x) (x)
49
50#define host2float_le(x) (x)
51#define host2float_be(x) (x)
52
53#define from(x, type, endian) (float)(type ## _ ## endian ## 2host(x))
54#define to(x, type, endian) (float)(host2 ## type ## _ ## endian(x))
55
56/** Default linear PCM format */
57const pcm_format_t AUDIO_FORMAT_DEFAULT = {
58 .channels = 2,
59 .sampling_rate = 44100,
60 .sample_format = PCM_SAMPLE_SINT16_LE,
61};
62
63/** Special ANY PCM format.
64 * This format is used if the real format is no know or important.
65 */
66const pcm_format_t AUDIO_FORMAT_ANY = {
67 .channels = 0,
68 .sampling_rate = 0,
69 .sample_format = 0,
70};
71
72static float get_normalized_sample(const void *buffer, size_t size,
73 unsigned frame, unsigned channel, const pcm_format_t *f);
74
75/**
76 * Compare PCM format attribtues.
77 * @param a Format description.
78 * @param b Format description.
79 * @return True if a and b describe the same format, false otherwise.
80 */
81bool pcm_format_same(const pcm_format_t *a, const pcm_format_t *b)
82{
83 assert(a);
84 assert(b);
85 return
86 a->sampling_rate == b->sampling_rate &&
87 a->channels == b->channels &&
88 a->sample_format == b->sample_format;
89}
90
91/**
92 * Fill audio buffer with silence in the specified format.
93 * @param dst Destination audio buffer.
94 * @param size Size of the destination audio buffer.
95 * @param f Pointer to the format description.
96 */
97void pcm_format_silence(void *dst, size_t size, const pcm_format_t *f)
98{
99#define SET_NULL(type, endian, nullv) \
100do { \
101 type *buffer = dst; \
102 const size_t sample_count = size / sizeof(type); \
103 for (unsigned i = 0; i < sample_count; ++i) { \
104 buffer[i] = to((type)nullv, type, endian); \
105 } \
106} while (0)
107
108 switch (f->sample_format) {
109 case PCM_SAMPLE_UINT8:
110 SET_NULL(uint8_t, le, INT8_MIN);
111 break;
112 case PCM_SAMPLE_SINT8:
113 SET_NULL(int8_t, le, 0);
114 break;
115 case PCM_SAMPLE_UINT16_LE:
116 SET_NULL(uint16_t, le, INT16_MIN);
117 break;
118 case PCM_SAMPLE_SINT16_LE:
119 SET_NULL(int16_t, le, 0);
120 break;
121 case PCM_SAMPLE_UINT16_BE:
122 SET_NULL(uint16_t, be, INT16_MIN);
123 break;
124 case PCM_SAMPLE_SINT16_BE:
125 SET_NULL(int16_t, be, 0);
126 break;
127 case PCM_SAMPLE_UINT32_LE:
128 SET_NULL(uint32_t, le, INT32_MIN);
129 break;
130 case PCM_SAMPLE_SINT32_LE:
131 SET_NULL(int32_t, le, 0);
132 break;
133 case PCM_SAMPLE_UINT32_BE:
134 SET_NULL(uint32_t, be, INT32_MIN);
135 break;
136 case PCM_SAMPLE_SINT32_BE:
137 SET_NULL(int32_t, le, 0);
138 break;
139 case PCM_SAMPLE_UINT24_32_LE:
140 case PCM_SAMPLE_SINT24_32_LE:
141 case PCM_SAMPLE_UINT24_32_BE:
142 case PCM_SAMPLE_SINT24_32_BE:
143 case PCM_SAMPLE_UINT24_LE:
144 case PCM_SAMPLE_SINT24_LE:
145 case PCM_SAMPLE_UINT24_BE:
146 case PCM_SAMPLE_SINT24_BE:
147 case PCM_SAMPLE_FLOAT32:
148 default:
149 break;
150 }
151#undef SET_NULL
152}
153
154/**
155 * Mix audio data of the same format and size.
156 * @param dst Destination buffer
157 * @param src Source buffer
158 * @param size Size of both the destination and the source buffer
159 * @param f Pointer to the format descriptor.
160 * @return Error code.
161 */
162errno_t pcm_format_mix(void *dst, const void *src, size_t size, const pcm_format_t *f)
163{
164 return pcm_format_convert_and_mix(dst, size, src, size, f, f);
165}
166
167/**
168 * Add and mix audio data.
169 * @param dst Destination audio buffer
170 * @param dst_size Size of the destination buffer
171 * @param src Source audio buffer
172 * @param src_size Size of the source buffer.
173 * @param sf Pointer to the source format descriptor.
174 * @param df Pointer to the destination format descriptor.
175 * @return Error code.
176 *
177 * Buffers must contain entire frames. Destination buffer is always filled.
178 * If there are not enough data in the source buffer silent data is assumed.
179 */
180errno_t pcm_format_convert_and_mix(void *dst, size_t dst_size, const void *src,
181 size_t src_size, const pcm_format_t *sf, const pcm_format_t *df)
182{
183 if (!dst || !src || !sf || !df)
184 return EINVAL;
185 const size_t src_frame_size = pcm_format_frame_size(sf);
186 if ((src_size % src_frame_size) != 0)
187 return EINVAL;
188
189 const size_t dst_frame_size = pcm_format_frame_size(df);
190 if ((dst_size % dst_frame_size) != 0)
191 return EINVAL;
192
193 /*
194 * This is so ugly it eats kittens, and puppies, and ducklings,
195 * and all little fluffy things...
196 */
197#define LOOP_ADD(type, endian, low, high) \
198do { \
199 const unsigned frame_count = dst_size / dst_frame_size; \
200 for (size_t i = 0; i < frame_count; ++i) { \
201 for (unsigned j = 0; j < df->channels; ++j) { \
202 const float a = \
203 get_normalized_sample(dst, dst_size, i, j, df);\
204 const float b = \
205 get_normalized_sample(src, src_size, i, j, sf);\
206 float c = (a + b); \
207 if (c < -1.0) c = -1.0; \
208 if (c > 1.0) c = 1.0; \
209 c += 1.0; \
210 c *= ((float)(type)high - (float)(type)low) / 2; \
211 c += (float)(type)low; \
212 type *dst_buf = dst; \
213 const unsigned pos = i * df->channels + j; \
214 if (pos < (dst_size / sizeof(type))) \
215 dst_buf[pos] = to((type)c, type, endian); \
216 } \
217 } \
218} while (0)
219
220 switch (df->sample_format) {
221 case PCM_SAMPLE_UINT8:
222 LOOP_ADD(uint8_t, le, UINT8_MIN, UINT8_MAX);
223 break;
224 case PCM_SAMPLE_SINT8:
225 LOOP_ADD(uint8_t, le, INT8_MIN, INT8_MAX);
226 break;
227 case PCM_SAMPLE_UINT16_LE:
228 LOOP_ADD(uint16_t, le, UINT16_MIN, UINT16_MAX);
229 break;
230 case PCM_SAMPLE_SINT16_LE:
231 LOOP_ADD(int16_t, le, INT16_MIN, INT16_MAX);
232 break;
233 case PCM_SAMPLE_UINT16_BE:
234 LOOP_ADD(uint16_t, be, UINT16_MIN, UINT16_MAX);
235 break;
236 case PCM_SAMPLE_SINT16_BE:
237 LOOP_ADD(int16_t, be, INT16_MIN, INT16_MAX);
238 break;
239 case PCM_SAMPLE_UINT24_32_LE:
240 case PCM_SAMPLE_UINT32_LE: // TODO this are not right for 24bit
241 LOOP_ADD(uint32_t, le, UINT32_MIN, UINT32_MAX);
242 break;
243 case PCM_SAMPLE_SINT24_32_LE:
244 case PCM_SAMPLE_SINT32_LE:
245 LOOP_ADD(int32_t, le, INT32_MIN, INT32_MAX);
246 break;
247 case PCM_SAMPLE_UINT24_32_BE:
248 case PCM_SAMPLE_UINT32_BE:
249 LOOP_ADD(uint32_t, be, UINT32_MIN, UINT32_MAX);
250 break;
251 case PCM_SAMPLE_SINT24_32_BE:
252 case PCM_SAMPLE_SINT32_BE:
253 LOOP_ADD(int32_t, be, INT32_MIN, INT32_MAX);
254 break;
255 case PCM_SAMPLE_UINT24_LE:
256 case PCM_SAMPLE_SINT24_LE:
257 case PCM_SAMPLE_UINT24_BE:
258 case PCM_SAMPLE_SINT24_BE:
259 case PCM_SAMPLE_FLOAT32:
260 default:
261 return ENOTSUP;
262 }
263 return EOK;
264#undef LOOP_ADD
265}
266
267/**
268 * Converts all sample formats to float <-1,1>
269 * @param buffer Audio data
270 * @param size Size of the buffer
271 * @param frame Index of the frame to read
272 * @param channel Channel within the frame
273 * @param f Pointer to a format descriptor
274 * @return Normalized sample <-1,1>, 0.0 if the data could not be read
275 */
276static float get_normalized_sample(const void *buffer, size_t size,
277 unsigned frame, unsigned channel, const pcm_format_t *f)
278{
279 assert(f);
280 assert(buffer);
281 if (channel >= f->channels)
282 return 0.0f;
283#define GET(type, endian, low, high) \
284do { \
285 const type *src = buffer; \
286 const size_t sample_count = size / sizeof(type); \
287 const size_t sample_pos = frame * f->channels + channel; \
288 if (sample_pos >= sample_count) {\
289 return 0.0f; \
290 } \
291 float sample = from(src[sample_pos], type, endian); \
292 /* This makes it positive */ \
293 sample -= (float)(type)low; \
294 /* This makes it <0,2> */ \
295 sample /= (((float)(type)high - (float)(type)low) / 2.0f); \
296 return sample - 1.0f; \
297} while (0)
298
299 switch (f->sample_format) {
300 case PCM_SAMPLE_UINT8:
301 GET(uint8_t, le, UINT8_MIN, UINT8_MAX);
302 case PCM_SAMPLE_SINT8:
303 GET(int8_t, le, INT8_MIN, INT8_MAX);
304 case PCM_SAMPLE_UINT16_LE:
305 GET(uint16_t, le, UINT16_MIN, UINT16_MAX);
306 case PCM_SAMPLE_SINT16_LE:
307 GET(int16_t, le, INT16_MIN, INT16_MAX);
308 case PCM_SAMPLE_UINT16_BE:
309 GET(uint16_t, be, UINT16_MIN, UINT16_MAX);
310 case PCM_SAMPLE_SINT16_BE:
311 GET(int16_t, be, INT16_MIN, INT16_MAX);
312 case PCM_SAMPLE_UINT24_32_LE:
313 case PCM_SAMPLE_UINT32_LE:
314 GET(uint32_t, le, UINT32_MIN, UINT32_MAX);
315 case PCM_SAMPLE_SINT24_32_LE:
316 case PCM_SAMPLE_SINT32_LE:
317 GET(int32_t, le, INT32_MIN, INT32_MAX);
318 case PCM_SAMPLE_UINT24_32_BE:
319 case PCM_SAMPLE_UINT32_BE:
320 GET(uint32_t, be, UINT32_MIN, UINT32_MAX);
321 case PCM_SAMPLE_SINT24_32_BE:
322 case PCM_SAMPLE_SINT32_BE:
323 GET(int32_t, le, INT32_MIN, INT32_MAX);
324 case PCM_SAMPLE_UINT24_LE:
325 case PCM_SAMPLE_SINT24_LE:
326 case PCM_SAMPLE_UINT24_BE:
327 case PCM_SAMPLE_SINT24_BE:
328 case PCM_SAMPLE_FLOAT32:
329 default:
330 break;
331 }
332 return 0;
333#undef GET
334}
335/**
336 * @}
337 */
Note: See TracBrowser for help on using the repository browser.