]> sigrok.org Git - libsigrok.git/blame - src/output/wav.c
output/analog: Fix channel deinterleaving.
[libsigrok.git] / src / output / wav.c
CommitLineData
afaa75b9
BV
1/*
2 * This file is part of the libsigrok project.
3 *
4 * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
0605f874 20#include <string.h>
afaa75b9
BV
21#include "libsigrok.h"
22#include "libsigrok-internal.h"
23
24#define LOG_PREFIX "output/wav"
25
0605f874
BV
26/* Minimum/maximum number of samples per channel to put in a data chunk */
27#define MIN_DATA_CHUNK_SAMPLES 10
28
29struct out_context {
30 gboolean header_done;
31 uint64_t samplerate;
32 int num_channels;
33 GSList *channels;
34 int chanbuf_size;
35 int *chanbuf_used;
36 uint8_t **chanbuf;
37};
38
39static int realloc_chanbufs(const struct sr_output *o, int size)
40{
41 struct out_context *outc;
42 int i;
43
44 outc = o->priv;
45 for (i = 0; i < outc->num_channels; i++) {
46 if (!(outc->chanbuf[i] = g_try_realloc(outc->chanbuf[i], sizeof(float) * size))) {
47 sr_err("Unable to allocate enough output buffer memory.");
48 return SR_ERR;
49 }
50 outc->chanbuf_used[i] = 0;
51 }
52 outc->chanbuf_size = size;
53
54 return SR_OK;
55}
56
57static int flush_chanbufs(const struct sr_output *o, GString *out)
58{
59 struct out_context *outc;
60 int size, i, j;
61 char *buf, **bufp;
62
63 outc = o->priv;
64
65 /* Any one of them will do. */
66 size = outc->chanbuf_used[0];
67 if (!(buf = g_try_malloc(4 * size * outc->num_channels))) {
68 sr_err("Unable to allocate enough interleaved output buffer memory.");
69 return SR_ERR;
70 }
71 bufp = g_malloc(sizeof(char *) * outc->num_channels);
72 for (i = 0; i < outc->num_channels; i++)
73 bufp[i] = buf + i * 4;
74
75 for (i = 0; i < size * 4; i += 4) {
76 for (j = 0; j < outc->num_channels; j++) {
77 memcpy(bufp[j], outc->chanbuf[j] + i, 4);
78 bufp[j] += outc->num_channels * 4;
79 }
80 }
81 g_string_append_len(out, buf, 4 * size * outc->num_channels);
82 g_free(bufp);
83 g_free(buf);
84
85 for (i = 0; i < outc->num_channels; i++)
86 outc->chanbuf_used[i] = 0;
87
88 return SR_OK;
89}
90
afaa75b9
BV
91static int init(struct sr_output *o, GHashTable *options)
92{
0605f874
BV
93 struct out_context *outc;
94 struct sr_channel *ch;
95 GSList *l;
96
afaa75b9
BV
97 (void)options;
98
0605f874
BV
99 outc = g_malloc0(sizeof(struct out_context));
100 o->priv = outc;
101
102 for (l = o->sdi->channels; l; l = l->next) {
103 ch = l->data;
104 if (ch->type != SR_CHANNEL_ANALOG)
105 continue;
106 if (!ch->enabled)
107 continue;
108 outc->channels = g_slist_append(outc->channels, ch);
109 outc->num_channels++;
110 }
111
112 outc->chanbuf = g_malloc0(sizeof(float *) * outc->num_channels);
113 outc->chanbuf_used = g_malloc0(sizeof(int) * outc->num_channels);
114
115 /* Start off the interleaved buffer with 100 samples/channel. */
116 realloc_chanbufs(o, 100);
117
afaa75b9
BV
118 return SR_OK;
119}
120
0605f874
BV
121static void add_data_chunk(const struct sr_output *o, GString *gs)
122{
123 struct out_context *outc;
124 char tmp[4];
125
126 outc = o->priv;
127 g_string_append(gs, "fmt ");
128 /* Remaining chunk size */
129 WL32(tmp, 0x12);
130 g_string_append_len(gs, tmp, 4);
131 /* Format code 3 = IEEE float */
132 WL16(tmp, 0x0003);
133 g_string_append_len(gs, tmp, 2);
134 /* Number of channels */
135 WL16(tmp, outc->num_channels);
136 g_string_append_len(gs, tmp, 2);
137 /* Samplerate */
138 WL32(tmp, outc->samplerate);
139 g_string_append_len(gs, tmp, 4);
140 /* Byterate, using 32-bit floats. */
141 WL32(tmp, outc->samplerate * outc->num_channels * 4);
142 g_string_append_len(gs, tmp, 4);
143 /* Blockalign */
144 WL16(tmp, outc->num_channels * 4);
145 g_string_append_len(gs, tmp, 2);
146 /* Bits per sample */
147 WL16(tmp, 32);
148 g_string_append_len(gs, tmp, 2);
149 WL16(tmp, 0);
150 g_string_append_len(gs, tmp, 2);
151
152 g_string_append(gs, "data");
153 /* Data chunk size, max it out. */
154 WL32(tmp, 0xffffffff);
155 g_string_append_len(gs, tmp, 4);
156}
157
158static GString *gen_header(const struct sr_output *o)
159{
160 struct out_context *outc;
161 GVariant *gvar;
162 GString *header;
163 char tmp[4];
164
165 outc = o->priv;
166 if (outc->samplerate == 0) {
167 if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
168 &gvar) == SR_OK) {
169 outc->samplerate = g_variant_get_uint64(gvar);
170 g_variant_unref(gvar);
171 }
172 }
173
174 header = g_string_sized_new(512);
175 g_string_append(header, "RIFF");
176 /* Total size. Max out the field. */
177 WL32(tmp, 0xffffffff);
178 g_string_append_len(header, tmp, 4);
179 g_string_append(header, "WAVE");
180 add_data_chunk(o, header);
181
182 return header;
183}
184
185/*
186 * Stores the float in little-endian BINARY32 IEEE-754 2008 format.
187 */
188static void float_to_le(uint8_t *buf, float value)
189{
190 char *old;
191
192 old = (char *)&value;
193#ifdef WORDS_BIGENDIAN
194 buf[0] = old[3];
195 buf[1] = old[2];
196 buf[2] = old[1];
197 buf[3] = old[0];
198#else
199 buf[0] = old[0];
200 buf[1] = old[1];
201 buf[2] = old[2];
202 buf[3] = old[3];
203#endif
204}
205
206/*
207 * Returns the number of samples used in the current channel buffers,
208 * or -1 if they're not all the same.
209 */
210static int check_chanbuf_size(const struct sr_output *o)
211{
212 struct out_context *outc;
213 int size, i;
214
215 outc = o->priv;
216 size = 0;
217 for (i = 0; i < outc->num_channels; i++) {
218 if (size == 0) {
219 if (outc->chanbuf_used[i] == 0) {
220 /* Nothing in all the buffers yet. */
221 size = -1;
222 break;
223 } else
224 /* New high water mark. */
225 size = outc->chanbuf_used[i];
226 } else if (outc->chanbuf_used[i] != size) {
227 /* All channel buffers are not equally full yet. */
228 size = -1;
229 break;
230 }
231 }
232
233 return size;
234}
afaa75b9
BV
235static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
236 GString **out)
237{
0605f874
BV
238 struct out_context *outc;
239 const struct sr_datafeed_meta *meta;
240 const struct sr_datafeed_analog *analog;
241 const struct sr_config *src;
242 struct sr_channel *ch;
243 GSList *l;
244 int num_channels, size, *chan_idx, idx, i, j;
245 uint8_t *buf;
246
247 *out = NULL;
248 if (!o || !o->sdi || !(outc = o->priv))
249 return SR_ERR_ARG;
250
251 switch (packet->type) {
252 case SR_DF_META:
253 meta = packet->payload;
254 for (l = meta->config; l; l = l->next) {
255 src = l->data;
256 if (src->key != SR_CONF_SAMPLERATE)
257 continue;
258 outc->samplerate = g_variant_get_uint64(src->data);
259 }
260 break;
261 case SR_DF_ANALOG:
262 if (!outc->header_done) {
263 *out = gen_header(o);
264 outc->header_done = TRUE;
265 } else
266 *out = g_string_sized_new(512);
267
268 analog = packet->payload;
269 if (analog->num_samples == 0)
270 return SR_OK;
271
272 num_channels = g_slist_length(analog->channels);
273 if (num_channels > outc->num_channels) {
274 sr_err("Packet has %d channels, but only %d were enabled.",
275 num_channels, outc->num_channels);
276 return SR_ERR;
277 }
278
279 if (analog->num_samples > outc->chanbuf_size) {
280 if (realloc_chanbufs(o, analog->num_samples) != SR_OK)
281 return SR_ERR_MALLOC;
282 }
283
284 /* Index the channels in this packet, so we can interleave quicker. */
285 chan_idx = g_malloc(sizeof(int) * outc->num_channels);
286 for (i = 0; i < num_channels; i++) {
287 ch = g_slist_nth_data(analog->channels, i);
288 chan_idx[i] = g_slist_index(outc->channels, ch);
289 }
290
291 for (i = 0; i < analog->num_samples; i++) {
292 for (j = 0; j < num_channels; j++) {
293 idx = chan_idx[j];
294 buf = outc->chanbuf[idx] + outc->chanbuf_used[idx]++ * 4;
295 float_to_le(buf, analog->data[i + j]);
296 }
297 }
298 g_free(chan_idx);
299
300 size = check_chanbuf_size(o);
301 if (size > MIN_DATA_CHUNK_SAMPLES)
302 if (flush_chanbufs(o, *out) != SR_OK)
303 return SR_ERR;
304 break;
305 case SR_DF_END:
306 size = check_chanbuf_size(o);
307 if (size > 0) {
308 *out = g_string_sized_new(4 * size * outc->num_channels);
309 if (flush_chanbufs(o, *out) != SR_OK)
310 return SR_ERR;
311 }
312 break;
313 }
afaa75b9
BV
314
315 return SR_OK;
316}
317
318static int cleanup(struct sr_output *o)
319{
0605f874
BV
320 struct out_context *outc;
321 int i;
322
323 outc = o->priv;
324 g_slist_free(outc->channels);
325 for (i = 0; i < outc->num_channels; i++)
326 g_free(outc->chanbuf[i]);
327 g_free(outc->chanbuf_used);
328 g_free(outc->chanbuf);
329 g_free(outc);
330 o->priv = NULL;
afaa75b9
BV
331
332 return SR_OK;
333}
334
335SR_PRIV struct sr_output_module output_wav = {
336 .id = "wav",
337 .name = "WAV",
0605f874 338 .desc = "WAVE file format",
afaa75b9
BV
339 .init = init,
340 .receive = receive,
341 .cleanup = cleanup,
342};
343