]> sigrok.org Git - libsigrok.git/blobdiff - src/output/wav.c
kingst-la2016: fix segfault that often occurs when a capture is aborted
[libsigrok.git] / src / output / wav.c
index e6688ed08a8619bd941d9bfcc80bae098b98899c..732fe976f0df420c2cae8b8e4b9a935ca0201b45 100644 (file)
@@ -17,8 +17,9 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <string.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "output/wav"
@@ -27,6 +28,7 @@
 #define MIN_DATA_CHUNK_SAMPLES 10
 
 struct out_context {
+       double scale;
        gboolean header_done;
        uint64_t samplerate;
        int num_channels;
@@ -34,6 +36,7 @@ struct out_context {
        int chanbuf_size;
        int *chanbuf_used;
        uint8_t **chanbuf;
+       float *fdata;
 };
 
 static int realloc_chanbufs(const struct sr_output *o, int size)
@@ -43,7 +46,7 @@ static int realloc_chanbufs(const struct sr_output *o, int size)
 
        outc = o->priv;
        for (i = 0; i < outc->num_channels; i++) {
-               if (!(outc->chanbuf[i] = g_try_realloc(outc->chanbuf[i], sizeof(float) * size))) { 
+               if (!(outc->chanbuf[i] = g_try_realloc(outc->chanbuf[i], sizeof(float) * size))) {
                        sr_err("Unable to allocate enough output buffer memory.");
                        return SR_ERR;
                }
@@ -57,29 +60,26 @@ static int realloc_chanbufs(const struct sr_output *o, int size)
 static int flush_chanbufs(const struct sr_output *o, GString *out)
 {
        struct out_context *outc;
-       int size, i, j;
-       char *buf, **bufp;
+       int num_samples, i, j;
+       char *buf, *bufp;
 
        outc = o->priv;
 
        /* Any one of them will do. */
-       size = outc->chanbuf_used[0];
-       if (!(buf = g_try_malloc(4 * size * outc->num_channels))) {
+       num_samples = outc->chanbuf_used[0];
+       if (!(buf = g_try_malloc(4 * num_samples * outc->num_channels))) {
                sr_err("Unable to allocate enough interleaved output buffer memory.");
                return SR_ERR;
        }
-       bufp = g_malloc(sizeof(char *) * outc->num_channels);
-       for (i = 0; i < outc->num_channels; i++)
-               bufp[i] = buf + i * 4;
 
-       for (i = 0; i < size * 4; i += 4) {
+       bufp = buf;
+       for (i = 0; i < num_samples; i++) {
                for (j = 0; j < outc->num_channels; j++) {
-                       memcpy(bufp[j], outc->chanbuf[j] + i, 4);
-                       bufp[j] += outc->num_channels * 4;
+                       memcpy(bufp, outc->chanbuf[j] + i * 4, 4);
+                       bufp += 4;
                }
        }
-       g_string_append_len(out, buf, 4 * size * outc->num_channels);
-       g_free(bufp);
+       g_string_append_len(out, buf, 4 * num_samples * outc->num_channels);
        g_free(buf);
 
        for (i = 0; i < outc->num_channels; i++)
@@ -94,10 +94,9 @@ static int init(struct sr_output *o, GHashTable *options)
        struct sr_channel *ch;
        GSList *l;
 
-       (void)options;
-
        outc = g_malloc0(sizeof(struct out_context));
        o->priv = outc;
+       outc->scale = g_variant_get_double(g_hash_table_lookup(options, "scale"));
 
        for (l = o->sdi->channels; l; l = l->next) {
                ch = l->data;
@@ -187,9 +186,9 @@ static GString *gen_header(const struct sr_output *o)
  */
 static void float_to_le(uint8_t *buf, float value)
 {
-       char *old;
+       uint8_t *old;
 
-       old = (char *)&value;
+       old = (uint8_t *)&value;
 #ifdef WORDS_BIGENDIAN
        buf[0] = old[3];
        buf[1] = old[2];
@@ -220,9 +219,10 @@ static int check_chanbuf_size(const struct sr_output *o)
                                /* Nothing in all the buffers yet. */
                                size = -1;
                                break;
-                       } else
+                       } else {
                                /* New high water mark. */
                                size = outc->chanbuf_used[i];
+                       }
                } else if (outc->chanbuf_used[i] != size) {
                        /* All channel buffers are not equally full yet. */
                        size = -1;
@@ -232,6 +232,7 @@ static int check_chanbuf_size(const struct sr_output *o)
 
        return size;
 }
+
 static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
                GString **out)
 {
@@ -241,7 +242,10 @@ static int receive(const struct sr_output *o, const struct sr_datafeed_packet *p
        const struct sr_config *src;
        struct sr_channel *ch;
        GSList *l;
-       int num_channels, size, *chan_idx, idx, i, j;
+       const GSList *channels;
+       float f;
+       int num_channels, num_samples, size, *chan_idx, idx, i, j, ret;
+       float *data;
        uint8_t *buf;
 
        *out = NULL;
@@ -262,21 +266,31 @@ static int receive(const struct sr_output *o, const struct sr_datafeed_packet *p
                if (!outc->header_done) {
                        *out = gen_header(o);
                        outc->header_done = TRUE;
-               } else
+               } else {
                        *out = g_string_sized_new(512);
+               }
 
                analog = packet->payload;
-               if (analog->num_samples == 0)
+               num_samples = analog->num_samples;
+               channels = analog->meaning->channels;
+               num_channels = g_slist_length(analog->meaning->channels);
+               if (!(data = g_try_realloc(outc->fdata, sizeof(float) * num_samples * num_channels)))
+                       return SR_ERR_MALLOC;
+               outc->fdata = data;
+               ret = sr_analog_to_float(analog, data);
+               if (ret != SR_OK)
+                       return ret;
+
+               if (num_samples == 0)
                        return SR_OK;
 
-               num_channels = g_slist_length(analog->channels);
                if (num_channels > outc->num_channels) {
                        sr_err("Packet has %d channels, but only %d were enabled.",
                                        num_channels, outc->num_channels);
                        return SR_ERR;
                }
 
-               if (analog->num_samples > outc->chanbuf_size) {
+               if (num_samples > outc->chanbuf_size) {
                        if (realloc_chanbufs(o, analog->num_samples) != SR_OK)
                                return SR_ERR_MALLOC;
                }
@@ -284,15 +298,18 @@ static int receive(const struct sr_output *o, const struct sr_datafeed_packet *p
                /* Index the channels in this packet, so we can interleave quicker. */
                chan_idx = g_malloc(sizeof(int) * outc->num_channels);
                for (i = 0; i < num_channels; i++) {
-                       ch = g_slist_nth_data(analog->channels, i);
+                       ch = g_slist_nth_data((GSList *) channels, i);
                        chan_idx[i] = g_slist_index(outc->channels, ch);
                }
 
-               for (i = 0; i < analog->num_samples; i++) {
+               for (i = 0; i < num_samples; i++) {
                        for (j = 0; j < num_channels; j++) {
                                idx = chan_idx[j];
                                buf = outc->chanbuf[idx] + outc->chanbuf_used[idx]++ * 4;
-                               float_to_le(buf, analog->data[i + j]);
+                               f = data[i * num_channels + j];
+                               if (outc->scale != 1.0)
+                                       f /= outc->scale;
+                               float_to_le(buf, f);
                        }
                }
                g_free(chan_idx);
@@ -315,6 +332,19 @@ static int receive(const struct sr_output *o, const struct sr_datafeed_packet *p
        return SR_OK;
 }
 
+static struct sr_option options[] = {
+       { "scale", "Scale", "Scale values by factor", NULL, NULL },
+       ALL_ZERO
+};
+
+static const struct sr_option *get_options(void)
+{
+       if (!options[0].def)
+               options[0].def = g_variant_ref_sink(g_variant_new_double(1.0));
+
+       return options;
+}
+
 static int cleanup(struct sr_output *o)
 {
        struct out_context *outc;
@@ -322,10 +352,12 @@ static int cleanup(struct sr_output *o)
 
        outc = o->priv;
        g_slist_free(outc->channels);
+       g_variant_unref(options[0].def);
        for (i = 0; i < outc->num_channels; i++)
                g_free(outc->chanbuf[i]);
        g_free(outc->chanbuf_used);
        g_free(outc->chanbuf);
+       g_free(outc->fdata);
        g_free(outc);
        o->priv = NULL;
 
@@ -335,9 +367,11 @@ static int cleanup(struct sr_output *o)
 SR_PRIV struct sr_output_module output_wav = {
        .id = "wav",
        .name = "WAV",
-       .desc = "WAVE file format",
+       .desc = "Microsoft WAV file format data",
+       .exts = (const char*[]){"wav", NULL},
+       .flags = 0,
+       .options = get_options,
        .init = init,
        .receive = receive,
        .cleanup = cleanup,
 };
-