]> sigrok.org Git - libsigrok.git/blame - src/output/wav.c
output/csv: use intermediate time_t var, silence compiler warning
[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
6ec6c43b 20#include <config.h>
0605f874 21#include <string.h>
c1aae900 22#include <libsigrok/libsigrok.h>
afaa75b9
BV
23#include "libsigrok-internal.h"
24
25#define LOG_PREFIX "output/wav"
26
0605f874
BV
27/* Minimum/maximum number of samples per channel to put in a data chunk */
28#define MIN_DATA_CHUNK_SAMPLES 10
29
30struct out_context {
7ea75009 31 double scale;
0605f874
BV
32 gboolean header_done;
33 uint64_t samplerate;
34 int num_channels;
35 GSList *channels;
36 int chanbuf_size;
37 int *chanbuf_used;
38 uint8_t **chanbuf;
c7224164 39 float *fdata;
0605f874
BV
40};
41
42static int realloc_chanbufs(const struct sr_output *o, int size)
43{
44 struct out_context *outc;
45 int i;
46
47 outc = o->priv;
48 for (i = 0; i < outc->num_channels; i++) {
176d785d 49 if (!(outc->chanbuf[i] = g_try_realloc(outc->chanbuf[i], sizeof(float) * size))) {
0605f874
BV
50 sr_err("Unable to allocate enough output buffer memory.");
51 return SR_ERR;
52 }
53 outc->chanbuf_used[i] = 0;
54 }
55 outc->chanbuf_size = size;
56
57 return SR_OK;
58}
59
60static int flush_chanbufs(const struct sr_output *o, GString *out)
61{
62 struct out_context *outc;
364859ac
BV
63 int num_samples, i, j;
64 char *buf, *bufp;
0605f874
BV
65
66 outc = o->priv;
67
68 /* Any one of them will do. */
364859ac
BV
69 num_samples = outc->chanbuf_used[0];
70 if (!(buf = g_try_malloc(4 * num_samples * outc->num_channels))) {
0605f874
BV
71 sr_err("Unable to allocate enough interleaved output buffer memory.");
72 return SR_ERR;
73 }
0605f874 74
364859ac
BV
75 bufp = buf;
76 for (i = 0; i < num_samples; i++) {
0605f874 77 for (j = 0; j < outc->num_channels; j++) {
364859ac
BV
78 memcpy(bufp, outc->chanbuf[j] + i * 4, 4);
79 bufp += 4;
0605f874
BV
80 }
81 }
364859ac 82 g_string_append_len(out, buf, 4 * num_samples * outc->num_channels);
0605f874
BV
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;
afaa75b9 96
0605f874
BV
97 outc = g_malloc0(sizeof(struct out_context));
98 o->priv = outc;
dcc55fe9 99 outc->scale = g_variant_get_double(g_hash_table_lookup(options, "scale"));
7ea75009 100
0605f874
BV
101 for (l = o->sdi->channels; l; l = l->next) {
102 ch = l->data;
103 if (ch->type != SR_CHANNEL_ANALOG)
104 continue;
105 if (!ch->enabled)
106 continue;
107 outc->channels = g_slist_append(outc->channels, ch);
108 outc->num_channels++;
109 }
110
111 outc->chanbuf = g_malloc0(sizeof(float *) * outc->num_channels);
112 outc->chanbuf_used = g_malloc0(sizeof(int) * outc->num_channels);
113
114 /* Start off the interleaved buffer with 100 samples/channel. */
115 realloc_chanbufs(o, 100);
116
afaa75b9
BV
117 return SR_OK;
118}
119
0605f874
BV
120static void add_data_chunk(const struct sr_output *o, GString *gs)
121{
122 struct out_context *outc;
123 char tmp[4];
124
125 outc = o->priv;
126 g_string_append(gs, "fmt ");
127 /* Remaining chunk size */
128 WL32(tmp, 0x12);
129 g_string_append_len(gs, tmp, 4);
130 /* Format code 3 = IEEE float */
131 WL16(tmp, 0x0003);
132 g_string_append_len(gs, tmp, 2);
133 /* Number of channels */
134 WL16(tmp, outc->num_channels);
135 g_string_append_len(gs, tmp, 2);
136 /* Samplerate */
137 WL32(tmp, outc->samplerate);
138 g_string_append_len(gs, tmp, 4);
139 /* Byterate, using 32-bit floats. */
140 WL32(tmp, outc->samplerate * outc->num_channels * 4);
141 g_string_append_len(gs, tmp, 4);
142 /* Blockalign */
143 WL16(tmp, outc->num_channels * 4);
144 g_string_append_len(gs, tmp, 2);
145 /* Bits per sample */
146 WL16(tmp, 32);
147 g_string_append_len(gs, tmp, 2);
148 WL16(tmp, 0);
149 g_string_append_len(gs, tmp, 2);
150
151 g_string_append(gs, "data");
152 /* Data chunk size, max it out. */
153 WL32(tmp, 0xffffffff);
154 g_string_append_len(gs, tmp, 4);
155}
156
157static GString *gen_header(const struct sr_output *o)
158{
159 struct out_context *outc;
160 GVariant *gvar;
161 GString *header;
162 char tmp[4];
163
164 outc = o->priv;
165 if (outc->samplerate == 0) {
166 if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
167 &gvar) == SR_OK) {
168 outc->samplerate = g_variant_get_uint64(gvar);
169 g_variant_unref(gvar);
170 }
171 }
172
173 header = g_string_sized_new(512);
174 g_string_append(header, "RIFF");
175 /* Total size. Max out the field. */
176 WL32(tmp, 0xffffffff);
177 g_string_append_len(header, tmp, 4);
178 g_string_append(header, "WAVE");
179 add_data_chunk(o, header);
180
181 return header;
182}
183
184/*
185 * Stores the float in little-endian BINARY32 IEEE-754 2008 format.
186 */
187static void float_to_le(uint8_t *buf, float value)
188{
972398f4 189 uint8_t *old;
0605f874 190
972398f4 191 old = (uint8_t *)&value;
0605f874
BV
192#ifdef WORDS_BIGENDIAN
193 buf[0] = old[3];
194 buf[1] = old[2];
195 buf[2] = old[1];
196 buf[3] = old[0];
197#else
198 buf[0] = old[0];
199 buf[1] = old[1];
200 buf[2] = old[2];
201 buf[3] = old[3];
202#endif
203}
204
205/*
206 * Returns the number of samples used in the current channel buffers,
207 * or -1 if they're not all the same.
208 */
209static int check_chanbuf_size(const struct sr_output *o)
210{
211 struct out_context *outc;
212 int size, i;
213
214 outc = o->priv;
215 size = 0;
216 for (i = 0; i < outc->num_channels; i++) {
217 if (size == 0) {
218 if (outc->chanbuf_used[i] == 0) {
219 /* Nothing in all the buffers yet. */
220 size = -1;
221 break;
972398f4 222 } else {
0605f874
BV
223 /* New high water mark. */
224 size = outc->chanbuf_used[i];
972398f4 225 }
0605f874
BV
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}
441e9eae 235
afaa75b9
BV
236static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
237 GString **out)
238{
0605f874
BV
239 struct out_context *outc;
240 const struct sr_datafeed_meta *meta;
edb691fc 241 const struct sr_datafeed_analog *analog;
0605f874
BV
242 const struct sr_config *src;
243 struct sr_channel *ch;
244 GSList *l;
b73cac75 245 const GSList *channels;
7ea75009 246 float f;
b73cac75
ML
247 int num_channels, num_samples, size, *chan_idx, idx, i, j, ret;
248 float *data;
0605f874
BV
249 uint8_t *buf;
250
251 *out = NULL;
252 if (!o || !o->sdi || !(outc = o->priv))
253 return SR_ERR_ARG;
254
255 switch (packet->type) {
256 case SR_DF_META:
257 meta = packet->payload;
258 for (l = meta->config; l; l = l->next) {
259 src = l->data;
260 if (src->key != SR_CONF_SAMPLERATE)
261 continue;
262 outc->samplerate = g_variant_get_uint64(src->data);
263 }
264 break;
edb691fc 265 case SR_DF_ANALOG:
0605f874
BV
266 if (!outc->header_done) {
267 *out = gen_header(o);
268 outc->header_done = TRUE;
972398f4 269 } else {
0605f874 270 *out = g_string_sized_new(512);
972398f4 271 }
0605f874 272
edb691fc 273 analog = packet->payload;
c1634386
UH
274 num_samples = analog->num_samples;
275 channels = analog->meaning->channels;
276 num_channels = g_slist_length(analog->meaning->channels);
277 if (!(data = g_try_realloc(outc->fdata, sizeof(float) * num_samples * num_channels)))
278 return SR_ERR_MALLOC;
279 outc->fdata = data;
280 ret = sr_analog_to_float(analog, data);
281 if (ret != SR_OK)
282 return ret;
b73cac75
ML
283
284 if (num_samples == 0)
0605f874
BV
285 return SR_OK;
286
0605f874
BV
287 if (num_channels > outc->num_channels) {
288 sr_err("Packet has %d channels, but only %d were enabled.",
289 num_channels, outc->num_channels);
290 return SR_ERR;
291 }
292
b73cac75 293 if (num_samples > outc->chanbuf_size) {
c1634386 294 if (realloc_chanbufs(o, analog->num_samples) != SR_OK)
0605f874
BV
295 return SR_ERR_MALLOC;
296 }
297
298 /* Index the channels in this packet, so we can interleave quicker. */
299 chan_idx = g_malloc(sizeof(int) * outc->num_channels);
300 for (i = 0; i < num_channels; i++) {
b73cac75 301 ch = g_slist_nth_data((GSList *) channels, i);
0605f874
BV
302 chan_idx[i] = g_slist_index(outc->channels, ch);
303 }
304
b73cac75 305 for (i = 0; i < num_samples; i++) {
0605f874
BV
306 for (j = 0; j < num_channels; j++) {
307 idx = chan_idx[j];
308 buf = outc->chanbuf[idx] + outc->chanbuf_used[idx]++ * 4;
17124cf9 309 f = data[i * num_channels + j];
23eeac46 310 if (outc->scale != 1.0)
7ea75009
BV
311 f /= outc->scale;
312 float_to_le(buf, f);
0605f874
BV
313 }
314 }
315 g_free(chan_idx);
316
317 size = check_chanbuf_size(o);
318 if (size > MIN_DATA_CHUNK_SAMPLES)
319 if (flush_chanbufs(o, *out) != SR_OK)
320 return SR_ERR;
321 break;
322 case SR_DF_END:
323 size = check_chanbuf_size(o);
324 if (size > 0) {
325 *out = g_string_sized_new(4 * size * outc->num_channels);
326 if (flush_chanbufs(o, *out) != SR_OK)
327 return SR_ERR;
328 }
329 break;
330 }
afaa75b9
BV
331
332 return SR_OK;
333}
334
da3d141f
SB
335static struct sr_option options[] = {
336 { "scale", "Scale", "Scale values by factor", NULL, NULL },
337 ALL_ZERO
338};
339
340static const struct sr_option *get_options(void)
341{
342 if (!options[0].def)
23eeac46 343 options[0].def = g_variant_ref_sink(g_variant_new_double(1.0));
da3d141f
SB
344
345 return options;
346}
347
afaa75b9
BV
348static int cleanup(struct sr_output *o)
349{
0605f874
BV
350 struct out_context *outc;
351 int i;
352
353 outc = o->priv;
354 g_slist_free(outc->channels);
da3d141f 355 g_variant_unref(options[0].def);
0605f874
BV
356 for (i = 0; i < outc->num_channels; i++)
357 g_free(outc->chanbuf[i]);
358 g_free(outc->chanbuf_used);
359 g_free(outc->chanbuf);
c7224164 360 g_free(outc->fdata);
0605f874
BV
361 g_free(outc);
362 o->priv = NULL;
afaa75b9
BV
363
364 return SR_OK;
365}
366
367SR_PRIV struct sr_output_module output_wav = {
368 .id = "wav",
369 .name = "WAV",
b20eb520 370 .desc = "Microsoft WAV file format data",
8a174d23 371 .exts = (const char*[]){"wav", NULL},
3cd4b381 372 .flags = 0,
7ea75009 373 .options = get_options,
afaa75b9
BV
374 .init = init,
375 .receive = receive,
376 .cleanup = cleanup,
377};