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