source: mainline/uspace/lib/pcm/src/format.c@ 3f7fe9e

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

Clean up headers

Depends on <limits.h> and <stdint.h> being provided, which is a step up from
depending on mostly undocumented predefined macros.
In principle, <limits.h> and <stdint.h> mostly describe properties of
the compiler, so even though we depend on certain values for their contents,
actually defining them in the library is kind of reversal of concerns.

  • Property mode set to 100644
File size: 9.6 KB
RevLine 
[737b4c0]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>
[43c40a3]39#include <macros.h>
[bb67def]40#include <stdio.h>
[3f7fe9e]41#include <inttypes.h>
[737b4c0]42
[ea6c838]43#include "format.h"
[737b4c0]44
[bb67def]45// TODO float endian?
46#define float_le2host(x) (x)
47#define float_be2host(x) (x)
48
49#define host2float_le(x) (x)
50#define host2float_be(x) (x)
51
52#define from(x, type, endian) (float)(type ## _ ## endian ## 2host(x))
53#define to(x, type, endian) (float)(host2 ## type ## _ ## endian(x))
54
[b1dfe13]55/** Default linear PCM format */
[fe0b448]56const pcm_format_t AUDIO_FORMAT_DEFAULT = {
57 .channels = 2,
58 .sampling_rate = 44100,
59 .sample_format = PCM_SAMPLE_SINT16_LE,
[1433ecda]60};
[fe0b448]61
[b1dfe13]62/** Special ANY PCM format.
63 * This format is used if the real format is no know or important.
64 */
[fe0b448]65const pcm_format_t AUDIO_FORMAT_ANY = {
66 .channels = 0,
67 .sampling_rate = 0,
68 .sample_format = 0,
[1433ecda]69};
[fe0b448]70
[bb67def]71static float get_normalized_sample(const void *buffer, size_t size,
[ea6c838]72 unsigned frame, unsigned channel, const pcm_format_t *f);
[bb67def]73
[5a6f362]74/**
75 * Compare PCM format attribtues.
76 * @param a Format description.
77 * @param b Format description.
78 * @return True if a and b describe the same format, false otherwise.
79 */
[1433ecda]80bool pcm_format_same(const pcm_format_t *a, const pcm_format_t *b)
[737b4c0]81{
82 assert(a);
83 assert(b);
84 return
85 a->sampling_rate == b->sampling_rate &&
86 a->channels == b->channels &&
87 a->sample_format == b->sample_format;
88}
89
[5a6f362]90/**
91 * Fill audio buffer with silence in the specified format.
92 * @param dst Destination audio buffer.
93 * @param size Size of the destination audio buffer.
94 * @param f Pointer to the format description.
95 */
[e6bba8f]96void pcm_format_silence(void *dst, size_t size, const pcm_format_t *f)
97{
98#define SET_NULL(type, endian, nullv) \
99do { \
100 type *buffer = dst; \
101 const size_t sample_count = size / sizeof(type); \
102 for (unsigned i = 0; i < sample_count; ++i) { \
103 buffer[i] = to((type)nullv, type, endian); \
104 } \
105} while (0)
106
107 switch (f->sample_format) {
108 case PCM_SAMPLE_UINT8:
[1433ecda]109 SET_NULL(uint8_t, le, INT8_MIN);
110 break;
[e6bba8f]111 case PCM_SAMPLE_SINT8:
[1433ecda]112 SET_NULL(int8_t, le, 0);
113 break;
[e6bba8f]114 case PCM_SAMPLE_UINT16_LE:
[1433ecda]115 SET_NULL(uint16_t, le, INT16_MIN);
116 break;
[e6bba8f]117 case PCM_SAMPLE_SINT16_LE:
[1433ecda]118 SET_NULL(int16_t, le, 0);
119 break;
[e6bba8f]120 case PCM_SAMPLE_UINT16_BE:
[1433ecda]121 SET_NULL(uint16_t, be, INT16_MIN);
122 break;
[e6bba8f]123 case PCM_SAMPLE_SINT16_BE:
[1433ecda]124 SET_NULL(int16_t, be, 0);
125 break;
[e6bba8f]126 case PCM_SAMPLE_UINT32_LE:
[1433ecda]127 SET_NULL(uint32_t, le, INT32_MIN);
128 break;
[e6bba8f]129 case PCM_SAMPLE_SINT32_LE:
[1433ecda]130 SET_NULL(int32_t, le, 0);
131 break;
[e6bba8f]132 case PCM_SAMPLE_UINT32_BE:
[1433ecda]133 SET_NULL(uint32_t, be, INT32_MIN);
134 break;
[e6bba8f]135 case PCM_SAMPLE_SINT32_BE:
[1433ecda]136 SET_NULL(int32_t, le, 0);
137 break;
[e6bba8f]138 case PCM_SAMPLE_UINT24_32_LE:
139 case PCM_SAMPLE_SINT24_32_LE:
140 case PCM_SAMPLE_UINT24_32_BE:
141 case PCM_SAMPLE_SINT24_32_BE:
142 case PCM_SAMPLE_UINT24_LE:
143 case PCM_SAMPLE_SINT24_LE:
144 case PCM_SAMPLE_UINT24_BE:
145 case PCM_SAMPLE_SINT24_BE:
146 case PCM_SAMPLE_FLOAT32:
[850fd32]147 default:
148 break;
[e6bba8f]149 }
150#undef SET_NULL
151}
152
[5a6f362]153/**
154 * Mix audio data of the same format and size.
155 * @param dst Destination buffer
156 * @param src Source buffer
157 * @param size Size of both the destination and the source buffer
158 * @param f Pointer to the format descriptor.
159 * @return Error code.
160 */
[5a6cc679]161errno_t pcm_format_mix(void *dst, const void *src, size_t size, const pcm_format_t *f)
[737b4c0]162{
[ea6c838]163 return pcm_format_convert_and_mix(dst, size, src, size, f, f);
[950110ee]164}
[5a6f362]165
166/**
167 * Add and mix audio data.
168 * @param dst Destination audio buffer
169 * @param dst_size Size of the destination buffer
170 * @param src Source audio buffer
171 * @param src_size Size of the source buffer.
172 * @param sf Pointer to the source format descriptor.
173 * @param df Pointer to the destination format descriptor.
174 * @return Error code.
175 *
176 * Buffers must contain entire frames. Destination buffer is always filled.
177 * If there are not enough data in the source buffer silent data is assumed.
178 */
[5a6cc679]179errno_t pcm_format_convert_and_mix(void *dst, size_t dst_size, const void *src,
[ea6c838]180 size_t src_size, const pcm_format_t *sf, const pcm_format_t *df)
[950110ee]181{
182 if (!dst || !src || !sf || !df)
[737b4c0]183 return EINVAL;
[ea6c838]184 const size_t src_frame_size = pcm_format_frame_size(sf);
[950110ee]185 if ((src_size % src_frame_size) != 0)
186 return EINVAL;
187
[ea6c838]188 const size_t dst_frame_size = pcm_format_frame_size(df);
[5a6f362]189 if ((dst_size % dst_frame_size) != 0)
[737b4c0]190 return EINVAL;
191
[7c3fb9b]192 /*
193 * This is so ugly it eats kittens, and puppies, and ducklings,
[737b4c0]194 * and all little fluffy things...
[bb67def]195 */
196#define LOOP_ADD(type, endian, low, high) \
[737b4c0]197do { \
[950110ee]198 const unsigned frame_count = dst_size / dst_frame_size; \
[bb67def]199 for (size_t i = 0; i < frame_count; ++i) { \
[950110ee]200 for (unsigned j = 0; j < df->channels; ++j) { \
[bb67def]201 const float a = \
[1f7da3b]202 get_normalized_sample(dst, dst_size, i, j, df);\
[bb67def]203 const float b = \
[1f7da3b]204 get_normalized_sample(src, src_size, i, j, sf);\
[bb67def]205 float c = (a + b); \
206 if (c < -1.0) c = -1.0; \
207 if (c > 1.0) c = 1.0; \
208 c += 1.0; \
209 c *= ((float)(type)high - (float)(type)low) / 2; \
210 c += (float)(type)low; \
211 type *dst_buf = dst; \
[950110ee]212 const unsigned pos = i * df->channels + j; \
213 if (pos < (dst_size / sizeof(type))) \
[bb67def]214 dst_buf[pos] = to((type)c, type, endian); \
215 } \
[737b4c0]216 } \
217} while (0)
218
[950110ee]219 switch (df->sample_format) {
[bb67def]220 case PCM_SAMPLE_UINT8:
[1433ecda]221 LOOP_ADD(uint8_t, le, UINT8_MIN, UINT8_MAX);
222 break;
[bb67def]223 case PCM_SAMPLE_SINT8:
[1433ecda]224 LOOP_ADD(uint8_t, le, INT8_MIN, INT8_MAX);
225 break;
[737b4c0]226 case PCM_SAMPLE_UINT16_LE:
[1433ecda]227 LOOP_ADD(uint16_t, le, UINT16_MIN, UINT16_MAX);
228 break;
[737b4c0]229 case PCM_SAMPLE_SINT16_LE:
[1433ecda]230 LOOP_ADD(int16_t, le, INT16_MIN, INT16_MAX);
231 break;
[737b4c0]232 case PCM_SAMPLE_UINT16_BE:
[1433ecda]233 LOOP_ADD(uint16_t, be, UINT16_MIN, UINT16_MAX);
234 break;
[737b4c0]235 case PCM_SAMPLE_SINT16_BE:
[1433ecda]236 LOOP_ADD(int16_t, be, INT16_MIN, INT16_MAX);
237 break;
[737b4c0]238 case PCM_SAMPLE_UINT24_32_LE:
[bb67def]239 case PCM_SAMPLE_UINT32_LE: // TODO this are not right for 24bit
[1433ecda]240 LOOP_ADD(uint32_t, le, UINT32_MIN, UINT32_MAX);
241 break;
[43c40a3]242 case PCM_SAMPLE_SINT24_32_LE:
[737b4c0]243 case PCM_SAMPLE_SINT32_LE:
[1433ecda]244 LOOP_ADD(int32_t, le, INT32_MIN, INT32_MAX);
245 break;
[737b4c0]246 case PCM_SAMPLE_UINT24_32_BE:
247 case PCM_SAMPLE_UINT32_BE:
[1433ecda]248 LOOP_ADD(uint32_t, be, UINT32_MIN, UINT32_MAX);
249 break;
[43c40a3]250 case PCM_SAMPLE_SINT24_32_BE:
[737b4c0]251 case PCM_SAMPLE_SINT32_BE:
[1433ecda]252 LOOP_ADD(int32_t, be, INT32_MIN, INT32_MAX);
253 break;
[737b4c0]254 case PCM_SAMPLE_UINT24_LE:
255 case PCM_SAMPLE_SINT24_LE:
256 case PCM_SAMPLE_UINT24_BE:
257 case PCM_SAMPLE_SINT24_BE:
258 case PCM_SAMPLE_FLOAT32:
259 default:
260 return ENOTSUP;
261 }
262 return EOK;
[bb67def]263#undef LOOP_ADD
[737b4c0]264}
265
[5a6f362]266/**
267 * Converts all sample formats to float <-1,1>
268 * @param buffer Audio data
269 * @param size Size of the buffer
270 * @param frame Index of the frame to read
271 * @param channel Channel within the frame
272 * @param f Pointer to a format descriptor
273 * @return Normalized sample <-1,1>, 0.0 if the data could not be read
274 */
[bb67def]275static float get_normalized_sample(const void *buffer, size_t size,
[ea6c838]276 unsigned frame, unsigned channel, const pcm_format_t *f)
[bb67def]277{
278 assert(f);
[aef1799]279 assert(buffer);
[bb67def]280 if (channel >= f->channels)
281 return 0.0f;
282#define GET(type, endian, low, high) \
283do { \
284 const type *src = buffer; \
285 const size_t sample_count = size / sizeof(type); \
286 const size_t sample_pos = frame * f->channels + channel; \
287 if (sample_pos >= sample_count) {\
288 return 0.0f; \
289 } \
290 float sample = from(src[sample_pos], type, endian); \
291 /* This makes it positive */ \
292 sample -= (float)(type)low; \
293 /* This makes it <0,2> */ \
294 sample /= (((float)(type)high - (float)(type)low) / 2.0f); \
295 return sample - 1.0f; \
296} while (0)
297
298 switch (f->sample_format) {
299 case PCM_SAMPLE_UINT8:
300 GET(uint8_t, le, UINT8_MIN, UINT8_MAX);
301 case PCM_SAMPLE_SINT8:
302 GET(int8_t, le, INT8_MIN, INT8_MAX);
303 case PCM_SAMPLE_UINT16_LE:
304 GET(uint16_t, le, UINT16_MIN, UINT16_MAX);
305 case PCM_SAMPLE_SINT16_LE:
306 GET(int16_t, le, INT16_MIN, INT16_MAX);
307 case PCM_SAMPLE_UINT16_BE:
308 GET(uint16_t, be, UINT16_MIN, UINT16_MAX);
309 case PCM_SAMPLE_SINT16_BE:
310 GET(int16_t, be, INT16_MIN, INT16_MAX);
311 case PCM_SAMPLE_UINT24_32_LE:
312 case PCM_SAMPLE_UINT32_LE:
313 GET(uint32_t, le, UINT32_MIN, UINT32_MAX);
314 case PCM_SAMPLE_SINT24_32_LE:
315 case PCM_SAMPLE_SINT32_LE:
316 GET(int32_t, le, INT32_MIN, INT32_MAX);
317 case PCM_SAMPLE_UINT24_32_BE:
318 case PCM_SAMPLE_UINT32_BE:
319 GET(uint32_t, be, UINT32_MIN, UINT32_MAX);
320 case PCM_SAMPLE_SINT24_32_BE:
321 case PCM_SAMPLE_SINT32_BE:
322 GET(int32_t, le, INT32_MIN, INT32_MAX);
323 case PCM_SAMPLE_UINT24_LE:
324 case PCM_SAMPLE_SINT24_LE:
325 case PCM_SAMPLE_UINT24_BE:
326 case PCM_SAMPLE_SINT24_BE:
327 case PCM_SAMPLE_FLOAT32:
[84239b1]328 default:
329 break;
[bb67def]330 }
331 return 0;
332#undef GET
333}
[737b4c0]334/**
335 * @}
336 */
Note: See TracBrowser for help on using the repository browser.