2 * This file is part of the libsigrok project.
4 * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
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.
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.
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/>.
20 #include <sys/types.h>
26 #include "libsigrok.h"
27 #include "libsigrok-internal.h"
29 #define LOG_PREFIX "input/wav"
31 /* How many bytes at a time to process and send to the session bus. */
32 #define CHUNK_SIZE 4096
33 /* Expect to find the "data" chunk within this offset from the start. */
34 #define MAX_DATA_CHUNK_OFFSET 256
36 #define WAVE_FORMAT_PCM 1
37 #define WAVE_FORMAT_IEEE_FLOAT 3
47 static int get_wav_header(const char *filename, char *buf)
53 if (l <= 4 || strcasecmp(filename + l - 4, ".wav"))
56 if (stat(filename, &st) == -1)
59 /* Minimum size of header + 1 8-bit mono PCM sample. */
62 if ((fd = open(filename, O_RDONLY)) == -1)
65 l = read(fd, buf, 40);
73 static int format_match(const char *filename)
78 if (get_wav_header(filename, buf) != SR_OK)
81 if (strncmp(buf, "RIFF", 4))
83 if (strncmp(buf + 8, "WAVE", 4))
85 if (strncmp(buf + 12, "fmt ", 4))
87 fmt_code = GUINT16_FROM_LE(*(uint16_t *)(buf + 20));
88 if (fmt_code != WAVE_FORMAT_PCM
89 && fmt_code != WAVE_FORMAT_IEEE_FLOAT)
95 static int init(struct sr_input *in, const char *filename)
97 struct sr_channel *ch;
99 char buf[40], channelname[8];
102 if (get_wav_header(filename, buf) != SR_OK)
105 if (!(ctx = g_try_malloc0(sizeof(struct context))))
106 return SR_ERR_MALLOC;
108 /* Create a virtual device. */
109 in->sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, NULL, NULL, NULL);
112 ctx->fmt_code = GUINT16_FROM_LE(*(uint16_t *)(buf + 20));
113 ctx->samplerate = GUINT32_FROM_LE(*(uint32_t *)(buf + 24));
114 ctx->samplesize = GUINT16_FROM_LE(*(uint16_t *)(buf + 32));
115 ctx->num_channels = GUINT16_FROM_LE(*(uint16_t *)(buf + 22));
116 ctx->unitsize = ctx->samplesize / ctx->num_channels;
118 if (ctx->fmt_code == WAVE_FORMAT_PCM) {
119 if (ctx->samplesize != 1 && ctx->samplesize != 2
120 && ctx->samplesize != 4) {
121 sr_err("only 8, 16 or 32 bits per sample supported.");
125 /* WAVE_FORMAT_IEEE_FLOAT */
126 if (ctx->samplesize / ctx->num_channels != 4) {
127 sr_err("only 32-bit floats supported.");
132 for (i = 0; i < ctx->num_channels; i++) {
133 snprintf(channelname, 8, "CH%d", i + 1);
134 ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE, channelname);
135 in->sdi->channels = g_slist_append(in->sdi->channels, ch);
141 static int find_data_chunk(uint8_t *buf, int initial_offset)
145 offset = initial_offset;
146 while(offset < MAX_DATA_CHUNK_OFFSET) {
147 if (!memcmp(buf + offset, "data", 4))
148 /* Skip into the samples. */
150 for (i = 0; i < 4; i++) {
151 if (!isalpha(buf[offset + i])
152 && !isascii(buf[offset + i])
153 && !isblank(buf[offset + i]))
154 /* Doesn't look like a chunk ID. */
157 /* Skip past this chunk. */
158 offset += 8 + GUINT32_FROM_LE(*(uint32_t *)(buf + offset + 4));
164 static int loadfile(struct sr_input *in, const char *filename)
166 struct sr_datafeed_packet packet;
167 struct sr_datafeed_meta meta;
168 struct sr_datafeed_analog analog;
169 struct sr_config *src;
171 float fdata[CHUNK_SIZE];
173 int offset, chunk_samples, samplenum, fd, l, i;
174 uint8_t buf[CHUNK_SIZE], *s, *d;
178 /* Send header packet to the session bus. */
179 std_session_send_df_header(in->sdi, LOG_PREFIX);
181 /* Send the samplerate. */
182 packet.type = SR_DF_META;
183 packet.payload = &meta;
184 src = sr_config_new(SR_CONF_SAMPLERATE,
185 g_variant_new_uint64(ctx->samplerate));
186 meta.config = g_slist_append(NULL, src);
187 sr_session_send(in->sdi, &packet);
190 if ((fd = open(filename, O_RDONLY)) == -1)
192 if (read(fd, buf, MAX_DATA_CHUNK_OFFSET) < MAX_DATA_CHUNK_OFFSET)
195 /* Skip past size of 'fmt ' chunk. */
196 i = 20 + GUINT32_FROM_LE(*(uint32_t *)(buf + 16));
197 offset = find_data_chunk(buf, i);
199 sr_err("Couldn't find data chunk.");
202 if (lseek(fd, offset, SEEK_SET) == -1)
205 memset(fdata, 0, CHUNK_SIZE);
207 if ((l = read(fd, buf, CHUNK_SIZE)) < 1)
209 chunk_samples = l / ctx->num_channels / ctx->unitsize;
211 d = (uint8_t *)fdata;
212 for (samplenum = 0; samplenum < chunk_samples * ctx->num_channels; samplenum++) {
213 if (ctx->fmt_code == WAVE_FORMAT_PCM) {
215 memcpy(&sample, s, ctx->unitsize);
216 switch (ctx->samplesize) {
218 /* 8-bit PCM samples are unsigned. */
219 fdata[samplenum] = (uint8_t)sample / 255.0;
222 fdata[samplenum] = GINT16_FROM_LE(sample) / 32767.0;
225 fdata[samplenum] = GINT32_FROM_LE(sample) / 65535.0;
230 #ifdef WORDS_BIGENDIAN
231 for (i = 0; i < ctx->unitsize; i++)
232 d[i] = s[ctx->unitsize - i];
234 memcpy(d, s, ctx->unitsize);
241 packet.type = SR_DF_ANALOG;
242 packet.payload = &analog;
243 analog.channels = in->sdi->channels;
244 analog.num_samples = chunk_samples;
248 sr_session_send(in->sdi, &packet);
252 packet.type = SR_DF_END;
253 sr_session_send(in->sdi, &packet);
259 SR_PRIV struct sr_input_format input_wav = {
261 .description = "WAV file",
262 .format_match = format_match,
264 .loadfile = loadfile,