]> sigrok.org Git - libsigrok.git/blob - src/output/wav.c
srzip: Renumber analog channels from zero in output file.
[libsigrok.git] / src / output / wav.c
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
20 #include <config.h>
21 #include <string.h>
22 #include <libsigrok/libsigrok.h>
23 #include "libsigrok-internal.h"
24
25 #define LOG_PREFIX "output/wav"
26
27 /* Minimum/maximum number of samples per channel to put in a data chunk */
28 #define MIN_DATA_CHUNK_SAMPLES 10
29
30 struct out_context {
31         double scale;
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         float *fdata;
40 };
41
42 static 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++) {
49                 if (!(outc->chanbuf[i] = g_try_realloc(outc->chanbuf[i], sizeof(float) * size))) { 
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
60 static int flush_chanbufs(const struct sr_output *o, GString *out)
61 {
62         struct out_context *outc;
63         int num_samples, i, j;
64         char *buf, *bufp;
65
66         outc = o->priv;
67
68         /* Any one of them will do. */
69         num_samples = outc->chanbuf_used[0];
70         if (!(buf = g_try_malloc(4 * num_samples * outc->num_channels))) {
71                 sr_err("Unable to allocate enough interleaved output buffer memory.");
72                 return SR_ERR;
73         }
74
75         bufp = buf;
76         for (i = 0; i < num_samples; i++) {
77                 for (j = 0; j < outc->num_channels; j++) {
78                         memcpy(bufp, outc->chanbuf[j] + i * 4, 4);
79                         bufp += 4;
80                 }
81         }
82         g_string_append_len(out, buf, 4 * num_samples * outc->num_channels);
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
91 static int init(struct sr_output *o, GHashTable *options)
92 {
93         struct out_context *outc;
94         struct sr_channel *ch;
95         GSList *l;
96
97         outc = g_malloc0(sizeof(struct out_context));
98         o->priv = outc;
99         outc->scale = g_variant_get_double(g_hash_table_lookup(options, "scale"));
100
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
117         return SR_OK;
118 }
119
120 static 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
157 static 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  */
187 static void float_to_le(uint8_t *buf, float value)
188 {
189         char *old;
190
191         old = (char *)&value;
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  */
209 static 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;
222                         } else
223                                 /* New high water mark. */
224                                 size = outc->chanbuf_used[i];
225                 } else if (outc->chanbuf_used[i] != size) {
226                         /* All channel buffers are not equally full yet. */
227                         size = -1;
228                         break;
229                 }
230         }
231
232         return size;
233 }
234
235 static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
236                 GString **out)
237 {
238         struct out_context *outc;
239         const struct sr_datafeed_meta *meta;
240         const struct sr_datafeed_analog_old *analog_old;
241         const struct sr_datafeed_analog *analog;
242         const struct sr_config *src;
243         struct sr_channel *ch;
244         GSList *l;
245         const GSList *channels;
246         float f;
247         int num_channels, num_samples, size, *chan_idx, idx, i, j, ret;
248         float *data;
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;
265         case SR_DF_ANALOG_OLD:
266         case SR_DF_ANALOG:
267                 if (!outc->header_done) {
268                         *out = gen_header(o);
269                         outc->header_done = TRUE;
270                 } else
271                         *out = g_string_sized_new(512);
272
273                 analog_old = packet->payload;
274                 analog = packet->payload;
275
276                 if (packet->type == SR_DF_ANALOG_OLD) {
277                         num_samples = analog_old->num_samples;
278                         channels = analog_old->channels;
279                         num_channels = g_slist_length(analog_old->channels);
280                         data = analog_old->data;
281                 } else {
282                         num_samples = analog->num_samples;
283                         channels = analog->meaning->channels;
284                         num_channels = g_slist_length(analog->meaning->channels);
285                         if (!(data = g_try_realloc(outc->fdata, sizeof(float) * num_samples * num_channels)))
286                                 return SR_ERR_MALLOC;
287                         outc->fdata = data;
288                         ret = sr_analog_to_float(analog, data);
289                         if (ret != SR_OK)
290                                 return ret;
291                 }
292
293                 if (num_samples == 0)
294                         return SR_OK;
295
296                 if (num_channels > outc->num_channels) {
297                         sr_err("Packet has %d channels, but only %d were enabled.",
298                                         num_channels, outc->num_channels);
299                         return SR_ERR;
300                 }
301
302                 if (num_samples > outc->chanbuf_size) {
303                         if (realloc_chanbufs(o, analog_old->num_samples) != SR_OK)
304                                 return SR_ERR_MALLOC;
305                 }
306
307                 /* Index the channels in this packet, so we can interleave quicker. */
308                 chan_idx = g_malloc(sizeof(int) * outc->num_channels);
309                 for (i = 0; i < num_channels; i++) {
310                         ch = g_slist_nth_data((GSList *) channels, i);
311                         chan_idx[i] = g_slist_index(outc->channels, ch);
312                 }
313
314                 for (i = 0; i < num_samples; i++) {
315                         for (j = 0; j < num_channels; j++) {
316                                 idx = chan_idx[j];
317                                 buf = outc->chanbuf[idx] + outc->chanbuf_used[idx]++ * 4;
318                                 f = data[i * num_channels + j];
319                                 if (outc->scale != 0.0)
320                                         f /= outc->scale;
321                                 float_to_le(buf, f);
322                         }
323                 }
324                 g_free(chan_idx);
325
326                 size = check_chanbuf_size(o);
327                 if (size > MIN_DATA_CHUNK_SAMPLES)
328                         if (flush_chanbufs(o, *out) != SR_OK)
329                                 return SR_ERR;
330                 break;
331         case SR_DF_END:
332                 size = check_chanbuf_size(o);
333                 if (size > 0) {
334                         *out = g_string_sized_new(4 * size * outc->num_channels);
335                         if (flush_chanbufs(o, *out) != SR_OK)
336                                 return SR_ERR;
337                 }
338                 break;
339         }
340
341         return SR_OK;
342 }
343
344 static struct sr_option options[] = {
345         { "scale", "Scale", "Scale values by factor", NULL, NULL },
346         ALL_ZERO
347 };
348
349 static const struct sr_option *get_options(void)
350 {
351         if (!options[0].def)
352                 options[0].def = g_variant_ref_sink(g_variant_new_double(0.0));
353
354         return options;
355 }
356
357 static int cleanup(struct sr_output *o)
358 {
359         struct out_context *outc;
360         int i;
361
362         outc = o->priv;
363         g_slist_free(outc->channels);
364         g_variant_unref(options[0].def);
365         for (i = 0; i < outc->num_channels; i++)
366                 g_free(outc->chanbuf[i]);
367         g_free(outc->chanbuf_used);
368         g_free(outc->chanbuf);
369         g_free(outc->fdata);
370         g_free(outc);
371         o->priv = NULL;
372
373         return SR_OK;
374 }
375
376 SR_PRIV struct sr_output_module output_wav = {
377         .id = "wav",
378         .name = "WAV",
379         .desc = "Microsoft WAV file format",
380         .exts = (const char*[]){"wav", NULL},
381         .flags = 0,
382         .options = get_options,
383         .init = init,
384         .receive = receive,
385         .cleanup = cleanup,
386 };