]> sigrok.org Git - libsigrok.git/blame - src/output/wav.c
Build: Include <config.h> first in all source files
[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;
39};
40
41static int realloc_chanbufs(const struct sr_output *o, int size)
42{
43 struct out_context *outc;
44 int i;
45
46 outc = o->priv;
47 for (i = 0; i < outc->num_channels; i++) {
48 if (!(outc->chanbuf[i] = g_try_realloc(outc->chanbuf[i], sizeof(float) * size))) {
49 sr_err("Unable to allocate enough output buffer memory.");
50 return SR_ERR;
51 }
52 outc->chanbuf_used[i] = 0;
53 }
54 outc->chanbuf_size = size;
55
56 return SR_OK;
57}
58
59static int flush_chanbufs(const struct sr_output *o, GString *out)
60{
61 struct out_context *outc;
364859ac
BV
62 int num_samples, i, j;
63 char *buf, *bufp;
0605f874
BV
64
65 outc = o->priv;
66
67 /* Any one of them will do. */
364859ac
BV
68 num_samples = outc->chanbuf_used[0];
69 if (!(buf = g_try_malloc(4 * num_samples * outc->num_channels))) {
0605f874
BV
70 sr_err("Unable to allocate enough interleaved output buffer memory.");
71 return SR_ERR;
72 }
0605f874 73
364859ac
BV
74 bufp = buf;
75 for (i = 0; i < num_samples; i++) {
0605f874 76 for (j = 0; j < outc->num_channels; j++) {
364859ac
BV
77 memcpy(bufp, outc->chanbuf[j] + i * 4, 4);
78 bufp += 4;
0605f874
BV
79 }
80 }
364859ac 81 g_string_append_len(out, buf, 4 * num_samples * outc->num_channels);
0605f874
BV
82 g_free(buf);
83
84 for (i = 0; i < outc->num_channels; i++)
85 outc->chanbuf_used[i] = 0;
86
87 return SR_OK;
88}
89
afaa75b9
BV
90static int init(struct sr_output *o, GHashTable *options)
91{
0605f874
BV
92 struct out_context *outc;
93 struct sr_channel *ch;
94 GSList *l;
afaa75b9 95
0605f874
BV
96 outc = g_malloc0(sizeof(struct out_context));
97 o->priv = outc;
dcc55fe9 98 outc->scale = g_variant_get_double(g_hash_table_lookup(options, "scale"));
7ea75009 99
0605f874
BV
100 for (l = o->sdi->channels; l; l = l->next) {
101 ch = l->data;
102 if (ch->type != SR_CHANNEL_ANALOG)
103 continue;
104 if (!ch->enabled)
105 continue;
106 outc->channels = g_slist_append(outc->channels, ch);
107 outc->num_channels++;
108 }
109
110 outc->chanbuf = g_malloc0(sizeof(float *) * outc->num_channels);
111 outc->chanbuf_used = g_malloc0(sizeof(int) * outc->num_channels);
112
113 /* Start off the interleaved buffer with 100 samples/channel. */
114 realloc_chanbufs(o, 100);
115
afaa75b9
BV
116 return SR_OK;
117}
118
0605f874
BV
119static void add_data_chunk(const struct sr_output *o, GString *gs)
120{
121 struct out_context *outc;
122 char tmp[4];
123
124 outc = o->priv;
125 g_string_append(gs, "fmt ");
126 /* Remaining chunk size */
127 WL32(tmp, 0x12);
128 g_string_append_len(gs, tmp, 4);
129 /* Format code 3 = IEEE float */
130 WL16(tmp, 0x0003);
131 g_string_append_len(gs, tmp, 2);
132 /* Number of channels */
133 WL16(tmp, outc->num_channels);
134 g_string_append_len(gs, tmp, 2);
135 /* Samplerate */
136 WL32(tmp, outc->samplerate);
137 g_string_append_len(gs, tmp, 4);
138 /* Byterate, using 32-bit floats. */
139 WL32(tmp, outc->samplerate * outc->num_channels * 4);
140 g_string_append_len(gs, tmp, 4);
141 /* Blockalign */
142 WL16(tmp, outc->num_channels * 4);
143 g_string_append_len(gs, tmp, 2);
144 /* Bits per sample */
145 WL16(tmp, 32);
146 g_string_append_len(gs, tmp, 2);
147 WL16(tmp, 0);
148 g_string_append_len(gs, tmp, 2);
149
150 g_string_append(gs, "data");
151 /* Data chunk size, max it out. */
152 WL32(tmp, 0xffffffff);
153 g_string_append_len(gs, tmp, 4);
154}
155
156static GString *gen_header(const struct sr_output *o)
157{
158 struct out_context *outc;
159 GVariant *gvar;
160 GString *header;
161 char tmp[4];
162
163 outc = o->priv;
164 if (outc->samplerate == 0) {
165 if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
166 &gvar) == SR_OK) {
167 outc->samplerate = g_variant_get_uint64(gvar);
168 g_variant_unref(gvar);
169 }
170 }
171
172 header = g_string_sized_new(512);
173 g_string_append(header, "RIFF");
174 /* Total size. Max out the field. */
175 WL32(tmp, 0xffffffff);
176 g_string_append_len(header, tmp, 4);
177 g_string_append(header, "WAVE");
178 add_data_chunk(o, header);
179
180 return header;
181}
182
183/*
184 * Stores the float in little-endian BINARY32 IEEE-754 2008 format.
185 */
186static void float_to_le(uint8_t *buf, float value)
187{
188 char *old;
189
190 old = (char *)&value;
191#ifdef WORDS_BIGENDIAN
192 buf[0] = old[3];
193 buf[1] = old[2];
194 buf[2] = old[1];
195 buf[3] = old[0];
196#else
197 buf[0] = old[0];
198 buf[1] = old[1];
199 buf[2] = old[2];
200 buf[3] = old[3];
201#endif
202}
203
204/*
205 * Returns the number of samples used in the current channel buffers,
206 * or -1 if they're not all the same.
207 */
208static int check_chanbuf_size(const struct sr_output *o)
209{
210 struct out_context *outc;
211 int size, i;
212
213 outc = o->priv;
214 size = 0;
215 for (i = 0; i < outc->num_channels; i++) {
216 if (size == 0) {
217 if (outc->chanbuf_used[i] == 0) {
218 /* Nothing in all the buffers yet. */
219 size = -1;
220 break;
221 } else
222 /* New high water mark. */
223 size = outc->chanbuf_used[i];
224 } else if (outc->chanbuf_used[i] != size) {
225 /* All channel buffers are not equally full yet. */
226 size = -1;
227 break;
228 }
229 }
230
231 return size;
232}
441e9eae 233
afaa75b9
BV
234static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
235 GString **out)
236{
0605f874
BV
237 struct out_context *outc;
238 const struct sr_datafeed_meta *meta;
239 const struct sr_datafeed_analog *analog;
240 const struct sr_config *src;
241 struct sr_channel *ch;
242 GSList *l;
7ea75009 243 float f;
0605f874
BV
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;
7ea75009
BV
295 f = analog->data[i * num_channels + j];
296 if (outc->scale != 0.0)
297 f /= outc->scale;
298 float_to_le(buf, f);
0605f874
BV
299 }
300 }
301 g_free(chan_idx);
302
303 size = check_chanbuf_size(o);
304 if (size > MIN_DATA_CHUNK_SAMPLES)
305 if (flush_chanbufs(o, *out) != SR_OK)
306 return SR_ERR;
307 break;
308 case SR_DF_END:
309 size = check_chanbuf_size(o);
310 if (size > 0) {
311 *out = g_string_sized_new(4 * size * outc->num_channels);
312 if (flush_chanbufs(o, *out) != SR_OK)
313 return SR_ERR;
314 }
315 break;
316 }
afaa75b9
BV
317
318 return SR_OK;
319}
320
321static int cleanup(struct sr_output *o)
322{
0605f874
BV
323 struct out_context *outc;
324 int i;
325
326 outc = o->priv;
327 g_slist_free(outc->channels);
328 for (i = 0; i < outc->num_channels; i++)
329 g_free(outc->chanbuf[i]);
330 g_free(outc->chanbuf_used);
331 g_free(outc->chanbuf);
332 g_free(outc);
333 o->priv = NULL;
afaa75b9
BV
334
335 return SR_OK;
336}
337
7ea75009
BV
338static struct sr_option options[] = {
339 { "scale", "Scale", "Scale values by factor", NULL, NULL },
1685c276 340 ALL_ZERO
7ea75009
BV
341};
342
af7d656d 343static const struct sr_option *get_options(void)
7ea75009 344{
441e9eae
BV
345 if (!options[0].def)
346 options[0].def = g_variant_ref_sink(g_variant_new_double(0.0));
7ea75009
BV
347
348 return options;
349}
350
afaa75b9
BV
351SR_PRIV struct sr_output_module output_wav = {
352 .id = "wav",
353 .name = "WAV",
a82c83c6 354 .desc = "Microsoft WAV file format",
8a174d23 355 .exts = (const char*[]){"wav", NULL},
3cd4b381 356 .flags = 0,
7ea75009 357 .options = get_options,
afaa75b9
BV
358 .init = init,
359 .receive = receive,
360 .cleanup = cleanup,
361};