]> sigrok.org Git - libsigrok.git/blob - src/output/wav.c
output/wav: Add 'scale' option.
[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 <string.h>
21 #include "libsigrok.h"
22 #include "libsigrok-internal.h"
23
24 #define LOG_PREFIX "output/wav"
25
26 /* Minimum/maximum number of samples per channel to put in a data chunk */
27 #define MIN_DATA_CHUNK_SAMPLES 10
28
29 struct out_context {
30         double scale;
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
40 static 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
58 static int flush_chanbufs(const struct sr_output *o, GString *out)
59 {
60         struct out_context *outc;
61         int num_samples, i, j;
62         char *buf, *bufp;
63
64         outc = o->priv;
65
66         /* Any one of them will do. */
67         num_samples = outc->chanbuf_used[0];
68         if (!(buf = g_try_malloc(4 * num_samples * outc->num_channels))) {
69                 sr_err("Unable to allocate enough interleaved output buffer memory.");
70                 return SR_ERR;
71         }
72
73         bufp = buf;
74         for (i = 0; i < num_samples; i++) {
75                 for (j = 0; j < outc->num_channels; j++) {
76                         memcpy(bufp, outc->chanbuf[j] + i * 4, 4);
77                         bufp += 4;
78                 }
79         }
80         g_string_append_len(out, buf, 4 * num_samples * outc->num_channels);
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
89 static int init(struct sr_output *o, GHashTable *options)
90 {
91         struct out_context *outc;
92         struct sr_channel *ch;
93         GSList *l;
94         GHashTableIter iter;
95         gpointer key, value;
96
97         outc = g_malloc0(sizeof(struct out_context));
98         o->priv = outc;
99
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
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
133         return SR_OK;
134 }
135
136 static 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
173 static 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  */
203 static 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  */
225 static 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 }
250 static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
251                 GString **out)
252 {
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;
259         float f;
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;
311                                 f = analog->data[i * num_channels + j];
312                                 if (outc->scale != 0.0)
313                                         f /= outc->scale;
314                                 float_to_le(buf, f);
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         }
333
334         return SR_OK;
335 }
336
337 static int cleanup(struct sr_output *o)
338 {
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;
350
351         return SR_OK;
352 }
353
354 static struct sr_option options[] = {
355         { "scale", "Scale", "Scale values by factor", NULL, NULL },
356         { 0 }
357 };
358
359 static struct sr_option *get_options(gboolean cached)
360 {
361         if (cached)
362                 return options;
363
364         options[0].def = g_variant_new_double(0);
365         g_variant_ref_sink(options[0].def);
366
367         return options;
368 }
369
370 SR_PRIV struct sr_output_module output_wav = {
371         .id = "wav",
372         .name = "WAV",
373         .desc = "WAVE file format",
374         .options = get_options,
375         .init = init,
376         .receive = receive,
377         .cleanup = cleanup,
378 };
379