source: mainline/uspace/lib/pcm/src/format.c@ 47b2d7e3

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 47b2d7e3 was 84239b1, checked in by Jiri Svoboda <jiri@…>, 7 years ago

And there was much fixing.

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