]> sigrok.org Git - libsigrok.git/blob - src/output/wav.c
output/wav: Initial module implementation.
[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         gboolean header_done;
31         uint64_t samplerate;
32         int num_channels;
33         GSList *channels;
34         int chanbuf_size;
35         int *chanbuf_used;
36         uint8_t **chanbuf;
37 };
38
39 static int realloc_chanbufs(const struct sr_output *o, int size)
40 {
41         struct out_context *outc;
42         int i;
43
44         outc = o->priv;
45         for (i = 0; i < outc->num_channels; i++) {
46                 if (!(outc->chanbuf[i] = g_try_realloc(outc->chanbuf[i], sizeof(float) * size))) { 
47                         sr_err("Unable to allocate enough output buffer memory.");
48                         return SR_ERR;
49                 }
50                 outc->chanbuf_used[i] = 0;
51         }
52         outc->chanbuf_size = size;
53
54         return SR_OK;
55 }
56
57 static int flush_chanbufs(const struct sr_output *o, GString *out)
58 {
59         struct out_context *outc;
60         int size, i, j;
61         char *buf, **bufp;
62
63         outc = o->priv;
64
65         /* Any one of them will do. */
66         size = outc->chanbuf_used[0];
67         if (!(buf = g_try_malloc(4 * size * outc->num_channels))) {
68                 sr_err("Unable to allocate enough interleaved output buffer memory.");
69                 return SR_ERR;
70         }
71         bufp = g_malloc(sizeof(char *) * outc->num_channels);
72         for (i = 0; i < outc->num_channels; i++)
73                 bufp[i] = buf + i * 4;
74
75         for (i = 0; i < size * 4; i += 4) {
76                 for (j = 0; j < outc->num_channels; j++) {
77                         memcpy(bufp[j], outc->chanbuf[j] + i, 4);
78                         bufp[j] += outc->num_channels * 4;
79                 }
80         }
81         g_string_append_len(out, buf, 4 * size * outc->num_channels);
82         g_free(bufp);
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         (void)options;
98
99         outc = g_malloc0(sizeof(struct out_context));
100         o->priv = outc;
101
102         for (l = o->sdi->channels; l; l = l->next) {
103                 ch = l->data;
104                 if (ch->type != SR_CHANNEL_ANALOG)
105                         continue;
106                 if (!ch->enabled)
107                         continue;
108                 outc->channels = g_slist_append(outc->channels, ch);
109                 outc->num_channels++;
110         }
111
112         outc->chanbuf = g_malloc0(sizeof(float *) * outc->num_channels);
113         outc->chanbuf_used = g_malloc0(sizeof(int) * outc->num_channels);
114
115         /* Start off the interleaved buffer with 100 samples/channel. */
116         realloc_chanbufs(o, 100);
117
118         return SR_OK;
119 }
120
121 static void add_data_chunk(const struct sr_output *o, GString *gs)
122 {
123         struct out_context *outc;
124         char tmp[4];
125
126         outc = o->priv;
127         g_string_append(gs, "fmt ");
128         /* Remaining chunk size */
129         WL32(tmp, 0x12);
130         g_string_append_len(gs, tmp, 4);
131         /* Format code 3 = IEEE float */
132         WL16(tmp, 0x0003);
133         g_string_append_len(gs, tmp, 2);
134         /* Number of channels */
135         WL16(tmp, outc->num_channels);
136         g_string_append_len(gs, tmp, 2);
137         /* Samplerate */
138         WL32(tmp, outc->samplerate);
139         g_string_append_len(gs, tmp, 4);
140         /* Byterate, using 32-bit floats. */
141         WL32(tmp, outc->samplerate * outc->num_channels * 4);
142         g_string_append_len(gs, tmp, 4);
143         /* Blockalign */
144         WL16(tmp, outc->num_channels * 4);
145         g_string_append_len(gs, tmp, 2);
146         /* Bits per sample */
147         WL16(tmp, 32);
148         g_string_append_len(gs, tmp, 2);
149         WL16(tmp, 0);
150         g_string_append_len(gs, tmp, 2);
151
152         g_string_append(gs, "data");
153         /* Data chunk size, max it out. */
154         WL32(tmp, 0xffffffff);
155         g_string_append_len(gs, tmp, 4);
156 }
157
158 static GString *gen_header(const struct sr_output *o)
159 {
160         struct out_context *outc;
161         GVariant *gvar;
162         GString *header;
163         char tmp[4];
164
165         outc = o->priv;
166         if (outc->samplerate == 0) {
167                 if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
168                                 &gvar) == SR_OK) {
169                         outc->samplerate = g_variant_get_uint64(gvar);
170                         g_variant_unref(gvar);
171                 }
172         }
173
174         header = g_string_sized_new(512);
175         g_string_append(header, "RIFF");
176         /* Total size. Max out the field. */
177         WL32(tmp, 0xffffffff);
178         g_string_append_len(header, tmp, 4);
179         g_string_append(header, "WAVE");
180         add_data_chunk(o, header);
181
182         return header;
183 }
184
185 /*
186  * Stores the float in little-endian BINARY32 IEEE-754 2008 format.
187  */
188 static void float_to_le(uint8_t *buf, float value)
189 {
190         char *old;
191
192         old = (char *)&value;
193 #ifdef WORDS_BIGENDIAN
194         buf[0] = old[3];
195         buf[1] = old[2];
196         buf[2] = old[1];
197         buf[3] = old[0];
198 #else
199         buf[0] = old[0];
200         buf[1] = old[1];
201         buf[2] = old[2];
202         buf[3] = old[3];
203 #endif
204 }
205
206 /*
207  * Returns the number of samples used in the current channel buffers,
208  * or -1 if they're not all the same.
209  */
210 static int check_chanbuf_size(const struct sr_output *o)
211 {
212         struct out_context *outc;
213         int size, i;
214
215         outc = o->priv;
216         size = 0;
217         for (i = 0; i < outc->num_channels; i++) {
218                 if (size == 0) {
219                         if (outc->chanbuf_used[i] == 0) {
220                                 /* Nothing in all the buffers yet. */
221                                 size = -1;
222                                 break;
223                         } else
224                                 /* New high water mark. */
225                                 size = outc->chanbuf_used[i];
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 }
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 *analog;
241         const struct sr_config *src;
242         struct sr_channel *ch;
243         GSList *l;
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;
295                                 float_to_le(buf, analog->data[i + j]);
296                         }
297                 }
298                 g_free(chan_idx);
299
300                 size = check_chanbuf_size(o);
301                 if (size > MIN_DATA_CHUNK_SAMPLES)
302                         if (flush_chanbufs(o, *out) != SR_OK)
303                                 return SR_ERR;
304                 break;
305         case SR_DF_END:
306                 size = check_chanbuf_size(o);
307                 if (size > 0) {
308                         *out = g_string_sized_new(4 * size * outc->num_channels);
309                         if (flush_chanbufs(o, *out) != SR_OK)
310                                 return SR_ERR;
311                 }
312                 break;
313         }
314
315         return SR_OK;
316 }
317
318 static int cleanup(struct sr_output *o)
319 {
320         struct out_context *outc;
321         int i;
322
323         outc = o->priv;
324         g_slist_free(outc->channels);
325         for (i = 0; i < outc->num_channels; i++)
326                 g_free(outc->chanbuf[i]);
327         g_free(outc->chanbuf_used);
328         g_free(outc->chanbuf);
329         g_free(outc);
330         o->priv = NULL;
331
332         return SR_OK;
333 }
334
335 SR_PRIV struct sr_output_module output_wav = {
336         .id = "wav",
337         .name = "WAV",
338         .desc = "WAVE file format",
339         .init = init,
340         .receive = receive,
341         .cleanup = cleanup,
342 };
343