input: Introduce new input module API.

This is a work in progress.
This commit is contained in:
Bert Vermeulen 2014-08-10 16:57:04 +02:00
parent d514d35dab
commit 17bfaca62a
12 changed files with 725 additions and 187 deletions

View File

@ -42,12 +42,12 @@ libsigrok_la_SOURCES = \
src/std.c
# Input modules
# src/input/binary.c \
# src/input/chronovu_la8.c \
# src/input/csv.c \
# src/input/vcd.c
libsigrok_la_SOURCES += \
src/input/binary.c \
src/input/chronovu_la8.c \
src/input/csv.c \
src/input/input.c \
src/input/vcd.c \
src/input/wav.c
# Output modules

View File

@ -443,6 +443,21 @@ struct sr_option {
GSList *values;
};
/** Input module metadata keys. */
enum sr_input_meta_keys {
/** The input filename, if there is one. */
SR_INPUT_META_FILENAME = 0x01,
/** The input file's size in bytes. */
SR_INPUT_META_FILESIZE = 0x02,
/** The first 128 bytes of the file, provided as a GString. */
SR_INPUT_META_HEADER = 0x04,
/** The file's MIME type. */
SR_INPUT_META_MIMETYPE = 0x08,
/** The module cannot identify a file without this metadata. */
SR_INPUT_META_REQUIRED = 0x80,
};
struct sr_input;
struct sr_input_module;
struct sr_output;

View File

@ -124,7 +124,20 @@ SR_API int sr_session_source_remove_channel(struct sr_session *session,
/*--- input/input.c ---------------------------------------------------------*/
SR_API struct sr_input_module **sr_input_list(void);
SR_API const struct sr_input_module **sr_input_list(void);
SR_API const char *sr_input_id_get(const struct sr_input_module *in);
SR_API const char *sr_input_name_get(const struct sr_input_module *in);
SR_API const char *sr_input_description_get(const struct sr_input_module *in);
SR_API const struct sr_input_module *sr_input_find(char *id);
SR_API const struct sr_option *sr_input_options_get(const struct sr_input_module *in);
SR_API void sr_input_options_free(const struct sr_input_module *in);
SR_API struct sr_input *sr_input_new(const struct sr_input_module *imod,
GHashTable *options);
SR_API const struct sr_input *sr_input_scan_buffer(GString *buf);
SR_API const struct sr_input *sr_input_scan_file(const char *filename);
SR_API struct sr_dev_inst *sr_input_dev_inst_get(const struct sr_input *in);
SR_API int sr_input_send(const struct sr_input *in, GString *buf);
SR_API int sr_input_free(const struct sr_input *in);
/*--- output/output.c -------------------------------------------------------*/

View File

@ -215,7 +215,7 @@ static int sanity_check_all_drivers(void)
static int sanity_check_all_input_modules(void)
{
int i, errors, ret = SR_OK;
struct sr_input_module **inputs;
const struct sr_input_module **inputs;
const char *d;
sr_spew("Sanity-checking all input modules.");
@ -230,7 +230,11 @@ static int sanity_check_all_input_modules(void)
sr_err("No ID in module %d ('%s').", i, d);
errors++;
}
if (!inputs[i]->description) {
if (!inputs[i]->name) {
sr_err("No name in module %d ('%s').", i, d);
errors++;
}
if (!inputs[i]->desc) {
sr_err("No description in module %d ('%s').", i, d);
errors++;
}
@ -242,8 +246,8 @@ static int sanity_check_all_input_modules(void)
sr_err("No init in module %d ('%s').", i, d);
errors++;
}
if (!inputs[i]->loadfile) {
sr_err("No loadfile in module %d ('%s').", i, d);
if (!inputs[i]->receive) {
sr_err("No receive in module %d ('%s').", i, d);
errors++;
}

View File

@ -144,7 +144,7 @@ static int loadfile(struct sr_input *in, const char *filename)
SR_PRIV struct sr_input_module input_binary = {
.id = "binary",
.description = "Raw binary",
.desc = "Raw binary",
.format_match = format_match,
.init = init,
.loadfile = loadfile,

View File

@ -199,7 +199,7 @@ static int loadfile(struct sr_input *in, const char *filename)
SR_PRIV struct sr_input_module input_chronovu_la8 = {
.id = "chronovu-la8",
.description = "ChronoVu LA8",
.desc = "ChronoVu LA8",
.format_match = format_match,
.init = init,
.loadfile = loadfile,

View File

@ -865,7 +865,7 @@ static int loadfile(struct sr_input *in, const char *filename)
SR_PRIV struct sr_input_module input_csv = {
.id = "csv",
.description = "Comma-separated values (CSV)",
.desc = "Comma-separated values (CSV)",
.format_match = format_match,
.init = init,
.loadfile = loadfile,

View File

@ -1,7 +1,7 @@
/*
* This file is part of the libsigrok project.
*
* Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
* Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -17,9 +17,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include "libsigrok.h"
#include "libsigrok-internal.h"
#define LOG_PREFIX "input"
/**
* @file
*
@ -57,20 +65,427 @@ extern SR_PRIV struct sr_input_module input_vcd;
extern SR_PRIV struct sr_input_module input_wav;
/* @endcond */
static struct sr_input_module *input_module_list[] = {
&input_vcd,
&input_chronovu_la8,
static const struct sr_input_module *input_module_list[] = {
// &input_vcd,
// &input_chronovu_la8,
&input_wav,
&input_csv,
// &input_csv,
/* This one has to be last, because it will take any input. */
&input_binary,
// &input_binary,
NULL,
};
/** @since 0.1.0 */
SR_API struct sr_input_module **sr_input_list(void)
/** @since 0.4.0 */
SR_API const struct sr_input_module **sr_input_list(void)
{
return input_module_list;
}
/**
* Returns the specified input module's ID.
*
* @since 0.4.0
*/
SR_API const char *sr_input_id_get(const struct sr_input_module *o)
{
if (!o) {
sr_err("Invalid input module NULL!");
return NULL;
}
return o->id;
}
/**
* Returns the specified input module's name.
*
* @since 0.4.0
*/
SR_API const char *sr_input_name_get(const struct sr_input_module *o)
{
if (!o) {
sr_err("Invalid input module NULL!");
return NULL;
}
return o->name;
}
/**
* Returns the specified input module's description.
*
* @since 0.4.0
*/
SR_API const char *sr_input_description_get(const struct sr_input_module *o)
{
if (!o) {
sr_err("Invalid input module NULL!");
return NULL;
}
return o->desc;
}
/**
* Return the input module with the specified ID, or NULL if no module
* with that id is found.
*
* @since 0.4.0
*/
SR_API const struct sr_input_module *sr_input_find(char *id)
{
int i;
for (i = 0; input_module_list[i]; i++) {
if (!strcmp(input_module_list[i]->id, id))
return input_module_list[i];
}
return NULL;
}
/**
* Returns a NULL-terminated array of struct sr_option, or NULL if the
* module takes no options.
*
* Each call to this function must be followed by a call to
* sr_input_options_free().
*
* @since 0.4.0
*/
SR_API const struct sr_option *sr_input_options_get(const struct sr_input_module *o)
{
if (!o || !o->options)
return NULL;
return o->options();
}
/**
* After a call to sr_input_options_get(), this function cleans up all
* the resources allocated by that call.
*
* @since 0.4.0
*/
SR_API void sr_input_options_free(const struct sr_input_module *o)
{
struct sr_option *opt;
if (!o || !o->options)
return;
for (opt = o->options(); opt->id; opt++) {
if (opt->def) {
g_variant_unref(opt->def);
opt->def = NULL;
}
if (opt->values) {
g_slist_free_full(opt->values, (GDestroyNotify)g_variant_unref);
opt->values = NULL;
}
}
}
/**
* Create a new input instance using the specified input module.
*
* This function is used when a client wants to use a specific input
* module to parse a stream. No effort is made to identify the format.
*
* <code>options</code> is a *HashTable with the keys corresponding with
* the module options' <code>id</code> field. The values should be GVariant
* pointers with sunk * references, of the same GVariantType as the option's
* default value.
*
* @since 0.4.0
*/
SR_API struct sr_input *sr_input_new(const struct sr_input_module *imod,
GHashTable *options)
{
struct sr_input *in;
struct sr_option *mod_opts;
const GVariantType *gvt;
GHashTable *new_opts;
GHashTableIter iter;
gpointer key, value;
int i;
in = g_malloc0(sizeof(struct sr_input));
in->module = imod;
if (options) {
new_opts = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
(GDestroyNotify)g_variant_unref);
mod_opts = imod->options();
for (i = 0; mod_opts[i].id; i++) {
if (g_hash_table_lookup_extended(options, mod_opts[i].id,
&key, &value)) {
/* Option not given: insert the default value. */
gvt = g_variant_get_type(mod_opts[i].def);
if (!g_variant_is_of_type(value, gvt)) {
sr_err("Invalid type for '%s' option.", key);
g_free(in);
return NULL;
}
g_hash_table_insert(new_opts, g_strdup(mod_opts[i].id),
g_variant_ref(value));
} else {
/* Pass option along. */
g_hash_table_insert(new_opts, g_strdup(mod_opts[i].id),
g_variant_ref(mod_opts[i].def));
}
}
/* Make sure no invalid options were given. */
g_hash_table_iter_init(&iter, options);
while (g_hash_table_iter_next(&iter, &key, &value)) {
if (!g_hash_table_lookup(new_opts, key)) {
sr_err("Input module '%s' has no option '%s'", imod->id, key);
g_hash_table_destroy(new_opts);
g_free(in);
return NULL;
}
}
} else
new_opts = NULL;
if (in->module->init && in->module->init(in, new_opts) != SR_OK) {
g_hash_table_destroy(new_opts);
g_free(in);
in = NULL;
}
if (new_opts)
g_hash_table_destroy(new_opts);
return in;
}
/* Returns TRUE is all required meta items are available. */
static gboolean check_required_metadata(const uint8_t *metadata, uint8_t *avail)
{
int m, a;
uint8_t reqd;
for (m = 0; metadata[m]; m++) {
if (!metadata[m] & SR_INPUT_META_REQUIRED)
continue;
reqd = metadata[m] & ~SR_INPUT_META_REQUIRED;
for (a = 0; avail[a]; a++) {
if (avail[a] == reqd)
break;
}
if (!avail[a])
/* Found a required meta item that isn't available. */
return FALSE;
}
return TRUE;
}
/**
* Try to find an input module that can parse the given buffer.
*
* The buffer must contain enough of the beginning of the file for
* the input modules to find a match. This is format-dependent, but
* 128 bytes is normally enough.
*
* If an input module is found, an instance is created and returned.
* Otherwise, NULL is returned.
*
*/
SR_API const struct sr_input *sr_input_scan_buffer(GString *buf)
{
struct sr_input *in;
const struct sr_input_module *imod;
GHashTable *meta;
unsigned int m, i;
uint8_t mitem, avail_metadata[8];
/* No more metadata to be had from a buffer. */
avail_metadata[0] = SR_INPUT_META_HEADER;
avail_metadata[1] = 0;
in = NULL;
for (i = 0; input_module_list[i]; i++) {
imod = input_module_list[i];
if (!imod->metadata[0]) {
/* Module has no metadata for matching so will take
* any input. No point in letting it try to match. */
continue;
}
if (!check_required_metadata(imod->metadata, avail_metadata))
/* Cannot satisfy this module's requirements. */
continue;
meta = g_hash_table_new(NULL, NULL);
for (m = 0; m < sizeof(imod->metadata); m++) {
mitem = imod->metadata[m] & ~SR_INPUT_META_REQUIRED;
if (mitem == SR_INPUT_META_HEADER)
g_hash_table_insert(meta, GINT_TO_POINTER(mitem), buf);
}
if (g_hash_table_size(meta) == 0) {
/* No metadata for this module, so nothing to match. */
g_hash_table_destroy(meta);
continue;
}
if (!imod->format_match(meta)) {
/* Module didn't recognize this buffer. */
g_hash_table_destroy(meta);
continue;
}
g_hash_table_destroy(meta);
/* Found a matching module. */
in = sr_input_new(imod, NULL);
in->buf = g_string_new_len(buf->str, buf->len);
break;
}
return in;
}
/**
* Try to find an input module that can parse the given file.
*
* If an input module is found, an instance is created and returned.
* Otherwise, NULL is returned.
*
*/
SR_API const struct sr_input *sr_input_scan_file(const char *filename)
{
struct sr_input *in;
const struct sr_input_module *imod;
GHashTable *meta;
GString *buf;
struct stat st;
unsigned int midx, m, i;
int fd;
ssize_t size;
uint8_t mitem, avail_metadata[8];
if (!filename || !filename[0]) {
sr_err("Invalid filename.");
return NULL;
}
if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
sr_err("No such file.");
return NULL;
}
if (stat(filename, &st) < 0) {
sr_err("%s", strerror(errno));
return NULL;
}
midx = 0;
avail_metadata[midx++] = SR_INPUT_META_FILENAME;
avail_metadata[midx++] = SR_INPUT_META_FILESIZE;
avail_metadata[midx++] = SR_INPUT_META_HEADER;
/* TODO: MIME type */
in = NULL;
buf = NULL;
for (i = 0; input_module_list[i]; i++) {
imod = input_module_list[i];
if (!imod->metadata[0]) {
/* Module has no metadata for matching so will take
* any input. No point in letting it try to match. */
continue;
}
if (!check_required_metadata(imod->metadata, avail_metadata))
/* Cannot satisfy this module's requirements. */
continue;
meta = g_hash_table_new(NULL, NULL);
for (m = 0; m < sizeof(imod->metadata); m++) {
mitem = imod->metadata[m] & ~SR_INPUT_META_REQUIRED;
if (mitem == SR_INPUT_META_FILENAME)
g_hash_table_insert(meta, GINT_TO_POINTER(mitem),
(gpointer)filename);
else if (mitem == SR_INPUT_META_FILESIZE)
g_hash_table_insert(meta, GINT_TO_POINTER(mitem),
GINT_TO_POINTER(st.st_size));
else if (mitem == SR_INPUT_META_HEADER) {
buf = g_string_sized_new(128);
if ((fd = open(filename, O_RDONLY)) < 0) {
sr_err("%s", strerror(errno));
return NULL;
}
size = read(fd, buf->str, 128);
buf->len = size;
close(fd);
if (size <= 0) {
g_string_free(buf, TRUE);
sr_err("%s", strerror(errno));
return NULL;
}
g_hash_table_insert(meta, GINT_TO_POINTER(mitem), buf);
g_string_free(buf, TRUE);
}
}
if (g_hash_table_size(meta) == 0) {
/* No metadata for this module, so there's no way it
* can match. */
g_hash_table_destroy(meta);
continue;
}
if (!imod->format_match(meta))
/* Module didn't recognize this buffer. */
continue;
/* Found a matching module. */
in = sr_input_new(imod, NULL);
in->buf = g_string_new_len(buf->str, buf->len);
break;
}
if (!in && buf)
g_string_free(buf, TRUE);
return in;
}
SR_API struct sr_dev_inst *sr_input_dev_inst_get(const struct sr_input *in)
{
return in->sdi;
}
/**
* Send data to the specified input instance.
*
* When an input module instance is created with sr_input_new(), this
* function is used to feed data to the instance.
*
* @since 0.4.0
*/
SR_API int sr_input_send(const struct sr_input *in, GString *buf)
{
return in->module->receive(in, buf);
}
/**
* Free the specified input instance and all associated resources.
*
* @since 0.4.0
*/
SR_API int sr_input_free(const struct sr_input *in)
{
int ret;
if (!in)
return SR_ERR_ARG;
ret = SR_OK;
if (in->module->cleanup)
ret = in->module->cleanup((struct sr_input *)in);
if (in->buf)
g_string_free(in->buf, TRUE);
g_free((gpointer)in);
return ret;
}
/** @} */

View File

@ -537,7 +537,7 @@ static int loadfile(struct sr_input *in, const char *filename)
SR_PRIV struct sr_input_module input_vcd = {
.id = "vcd",
.description = "Value Change Dump",
.desc = "Value Change Dump",
.format_match = format_match,
.init = init,
.loadfile = loadfile,

View File

@ -30,6 +30,10 @@
/* How many bytes at a time to process and send to the session bus. */
#define CHUNK_SIZE 4096
/* Minimum size of header + 1 8-bit mono PCM sample. */
#define MIN_DATA_CHUNK_OFFSET 45
/* Expect to find the "data" chunk within this offset from the start. */
#define MAX_DATA_CHUNK_OFFSET 256
@ -37,230 +41,278 @@
#define WAVE_FORMAT_IEEE_FLOAT 3
struct context {
int fmt_code;
uint64_t samplerate;
int samplesize;
int num_channels;
int unitsize;
int fmt_code;
gboolean found_data;
};
static int get_wav_header(const char *filename, char *buf)
static int parse_wav_header(GString *buf, struct context *inc)
{
struct stat st;
int fd, l;
uint64_t samplerate;
int fmt_code, samplesize, num_channels, unitsize;
l = strlen(filename);
if (l <= 4 || strcasecmp(filename + l - 4, ".wav"))
if (buf->len < MIN_DATA_CHUNK_OFFSET) {
return SR_ERR;
}
if (stat(filename, &st) == -1)
return SR_ERR;
if (st.st_size <= 45)
/* Minimum size of header + 1 8-bit mono PCM sample. */
return SR_ERR;
fmt_code = GUINT16_FROM_LE(*(uint16_t *)(buf->str + 20));
samplerate = GUINT32_FROM_LE(*(uint32_t *)(buf->str + 24));
samplesize = GUINT16_FROM_LE(*(uint16_t *)(buf->str + 32));
num_channels = GUINT16_FROM_LE(*(uint16_t *)(buf->str + 22));
/* TODO div0 */
unitsize = samplesize / num_channels;
if ((fd = open(filename, O_RDONLY)) == -1)
if (fmt_code == WAVE_FORMAT_PCM) {
if (samplesize != 1 && samplesize != 2 && samplesize != 4) {
sr_err("only 8, 16 or 32 bits per sample supported.");
return SR_ERR;
}
} else if (fmt_code == WAVE_FORMAT_IEEE_FLOAT) {
if (unitsize != 4) {
sr_err("only 32-bit floats supported.");
return SR_ERR;
}
} else {
sr_err("Only PCM and floating point samples are supported.");
return SR_ERR;
}
l = read(fd, buf, 40);
close(fd);
if (l != 40)
return SR_ERR;
if (inc) {
inc->fmt_code = fmt_code;
inc->samplerate = samplerate;
inc->samplesize = samplesize;
inc->num_channels = num_channels;
inc->unitsize = unitsize;
inc->found_data = FALSE;
}
return SR_OK;
}
static int format_match(const char *filename)
static int format_match(GHashTable *metadata)
{
char buf[40];
uint16_t fmt_code;
GString *buf;
if (get_wav_header(filename, buf) != SR_OK)
buf = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_HEADER));
if (strncmp(buf->str, "RIFF", 4))
return FALSE;
if (strncmp(buf, "RIFF", 4))
if (strncmp(buf->str + 8, "WAVE", 4))
return FALSE;
if (strncmp(buf + 8, "WAVE", 4))
if (strncmp(buf->str + 12, "fmt ", 4))
return FALSE;
if (strncmp(buf + 12, "fmt ", 4))
return FALSE;
fmt_code = GUINT16_FROM_LE(*(uint16_t *)(buf + 20));
if (fmt_code != WAVE_FORMAT_PCM
&& fmt_code != WAVE_FORMAT_IEEE_FLOAT)
/*
* Only gets called when we already know this is a WAV file, so
* this parser can log error messages.
*/
if (parse_wav_header(buf, NULL) != SR_OK)
return FALSE;
return TRUE;
}
static int init(struct sr_input *in, const char *filename)
static int init(struct sr_input *in, GHashTable *options)
{
struct sr_channel *ch;
struct context *ctx;
char buf[40], channelname[8];
int i;
(void)options;
if (get_wav_header(filename, buf) != SR_OK)
return SR_ERR;
if (!(ctx = g_try_malloc0(sizeof(struct context))))
return SR_ERR_MALLOC;
/* Create a virtual device. */
in->sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, NULL, NULL, NULL);
in->sdi->priv = ctx;
ctx->fmt_code = GUINT16_FROM_LE(*(uint16_t *)(buf + 20));
ctx->samplerate = GUINT32_FROM_LE(*(uint32_t *)(buf + 24));
ctx->samplesize = GUINT16_FROM_LE(*(uint16_t *)(buf + 32));
ctx->num_channels = GUINT16_FROM_LE(*(uint16_t *)(buf + 22));
ctx->unitsize = ctx->samplesize / ctx->num_channels;
if (ctx->fmt_code == WAVE_FORMAT_PCM) {
if (ctx->samplesize != 1 && ctx->samplesize != 2
&& ctx->samplesize != 4) {
sr_err("only 8, 16 or 32 bits per sample supported.");
return SR_ERR;
}
} else {
/* WAVE_FORMAT_IEEE_FLOAT */
if (ctx->samplesize / ctx->num_channels != 4) {
sr_err("only 32-bit floats supported.");
return SR_ERR;
}
}
for (i = 0; i < ctx->num_channels; i++) {
snprintf(channelname, 8, "CH%d", i + 1);
ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE, channelname);
in->sdi->channels = g_slist_append(in->sdi->channels, ch);
}
return SR_OK;
}
static int find_data_chunk(uint8_t *buf, int initial_offset)
static int find_data_chunk(GString *buf, int initial_offset)
{
int offset, i;
unsigned int offset, i;
offset = initial_offset;
while(offset < MAX_DATA_CHUNK_OFFSET) {
if (!memcmp(buf + offset, "data", 4))
while(offset < MIN(MAX_DATA_CHUNK_OFFSET, buf->len)) {
if (!memcmp(buf->str + offset, "data", 4))
/* Skip into the samples. */
return offset + 8;
for (i = 0; i < 4; i++) {
if (!isalpha(buf[offset + i])
&& !isascii(buf[offset + i])
&& !isblank(buf[offset + i]))
if (!isalpha(buf->str[offset + i])
&& !isascii(buf->str[offset + i])
&& !isblank(buf->str[offset + i]))
/* Doesn't look like a chunk ID. */
return -1;
}
/* Skip past this chunk. */
offset += 8 + GUINT32_FROM_LE(*(uint32_t *)(buf + offset + 4));
offset += 8 + GUINT32_FROM_LE(*(uint32_t *)(buf->str + offset + 4));
}
return offset;
}
static int loadfile(struct sr_input *in, const char *filename)
static int initial_receive(const struct sr_input *in)
{
struct sr_datafeed_packet packet;
struct sr_datafeed_meta meta;
struct sr_datafeed_analog analog;
struct sr_channel *ch;
struct sr_config *src;
struct context *ctx;
float fdata[CHUNK_SIZE];
uint64_t sample;
int offset, chunk_samples, samplenum, fd, l, i;
uint8_t buf[CHUNK_SIZE], *s, *d;
struct context *inc;
int i;
char channelname[8];
ctx = in->sdi->priv;
if (!in->buf)
/* Shouldn't happen. */
return SR_ERR;
inc = in->sdi->priv = g_malloc(sizeof(struct context));
if (parse_wav_header(in->buf, inc) != SR_OK)
return SR_ERR;
for (i = 0; i < inc->num_channels; i++) {
snprintf(channelname, 8, "CH%d", i + 1);
ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE, channelname);
in->sdi->channels = g_slist_append(in->sdi->channels, ch);
}
/* Send header packet to the session bus. */
std_session_send_df_header(in->sdi, LOG_PREFIX);
/* Send the samplerate. */
packet.type = SR_DF_META;
packet.payload = &meta;
src = sr_config_new(SR_CONF_SAMPLERATE,
g_variant_new_uint64(ctx->samplerate));
src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(inc->samplerate));
meta.config = g_slist_append(NULL, src);
sr_session_send(in->sdi, &packet);
sr_config_free(src);
if ((fd = open(filename, O_RDONLY)) == -1)
return SR_ERR;
if (read(fd, buf, MAX_DATA_CHUNK_OFFSET) < MAX_DATA_CHUNK_OFFSET)
return -1;
return SR_OK;
}
/* Skip past size of 'fmt ' chunk. */
i = 20 + GUINT32_FROM_LE(*(uint32_t *)(buf + 16));
offset = find_data_chunk(buf, i);
if (offset < 0) {
sr_err("Couldn't find data chunk.");
return SR_ERR;
}
if (lseek(fd, offset, SEEK_SET) == -1)
return SR_ERR;
static void send_chunk(const struct sr_input *in, int offset, int num_samples)
{
struct sr_datafeed_packet packet;
struct sr_datafeed_analog analog;
struct context *inc;
float fdata[CHUNK_SIZE];
uint64_t sample;
int total_samples, samplenum;
char *s, *d;
inc = in->sdi->priv;
s = in->buf->str + offset;
d = (char *)fdata;
memset(fdata, 0, CHUNK_SIZE);
while (TRUE) {
if ((l = read(fd, buf, CHUNK_SIZE)) < 1)
break;
chunk_samples = l / ctx->num_channels / ctx->unitsize;
s = buf;
d = (uint8_t *)fdata;
for (samplenum = 0; samplenum < chunk_samples * ctx->num_channels; samplenum++) {
if (ctx->fmt_code == WAVE_FORMAT_PCM) {
sample = 0;
memcpy(&sample, s, ctx->unitsize);
switch (ctx->samplesize) {
case 1:
/* 8-bit PCM samples are unsigned. */
fdata[samplenum] = (uint8_t)sample / 255.0;
break;
case 2:
fdata[samplenum] = GINT16_FROM_LE(sample) / 32767.0;
break;
case 4:
fdata[samplenum] = GINT32_FROM_LE(sample) / 65535.0;
break;
}
} else {
/* BINARY32 float */
#ifdef WORDS_BIGENDIAN
for (i = 0; i < ctx->unitsize; i++)
d[i] = s[ctx->unitsize - i];
#else
memcpy(d, s, ctx->unitsize);
#endif
total_samples = num_samples * inc->num_channels;
for (samplenum = 0; samplenum < total_samples; samplenum++) {
if (inc->fmt_code == WAVE_FORMAT_PCM) {
sample = 0;
memcpy(&sample, s, inc->unitsize);
switch (inc->samplesize) {
case 1:
/* 8-bit PCM samples are unsigned. */
fdata[samplenum] = (uint8_t)sample / 255.0;
break;
case 2:
fdata[samplenum] = GINT16_FROM_LE(sample) / 32767.0;
break;
case 4:
fdata[samplenum] = GINT32_FROM_LE(sample) / 65535.0;
break;
}
s += ctx->unitsize;
d += ctx->unitsize;
} else {
/* BINARY32 float */
#ifdef WORDS_BIGENDIAN
for (i = 0; i < inc->unitsize; i++)
d[i] = s[inc->unitsize - i];
#else
memcpy(d, s, inc->unitsize);
#endif
}
packet.type = SR_DF_ANALOG;
packet.payload = &analog;
analog.channels = in->sdi->channels;
analog.num_samples = chunk_samples;
analog.mq = 0;
analog.unit = 0;
analog.data = fdata;
s += inc->unitsize;
d += inc->unitsize;
}
packet.type = SR_DF_ANALOG;
packet.payload = &analog;
analog.channels = in->sdi->channels;
analog.num_samples = num_samples;
analog.mq = 0;
analog.mqflags = 0;
analog.unit = 0;
analog.data = fdata;
sr_session_send(in->sdi, &packet);
}
static int receive(const struct sr_input *in, GString *buf)
{
struct sr_datafeed_packet packet;
struct context *inc;
int offset, chunk_samples, total_samples, processed, max_chunk_samples, num_samples, i;
if (buf->len == 0) {
/* End of stream. */
packet.type = SR_DF_END;
sr_session_send(in->sdi, &packet);
return SR_OK;
}
close(fd);
packet.type = SR_DF_END;
sr_session_send(in->sdi, &packet);
g_string_append_len(in->buf, buf->str, buf->len);
if (!in->sdi->priv) {
if (initial_receive(in) != SR_OK)
return SR_ERR;
if (in->buf->len < MIN_DATA_CHUNK_OFFSET) {
/*
* Don't even get started until there's enough room
* for the data segment to start.
*/
return SR_OK;
}
}
inc = in->sdi->priv;
if (!inc->found_data) {
/* Skip past size of 'fmt ' chunk. */
i = 20 + GUINT32_FROM_LE(*(uint32_t *)(in->buf->str + 16));
offset = find_data_chunk(in->buf, i);
if (offset < 0) {
if (in->buf->len > MAX_DATA_CHUNK_OFFSET) {
sr_err("Couldn't find data chunk.");
return SR_ERR;
}
}
inc->found_data = TRUE;
} else
offset = 0;
/* Round off up to the last channels * unitsize boundary. */
chunk_samples = (in->buf->len - offset) / inc->num_channels / inc->unitsize;
max_chunk_samples = CHUNK_SIZE / inc->num_channels / inc->unitsize;
processed = 0;
total_samples = chunk_samples;
while (processed < total_samples) {
if (chunk_samples > max_chunk_samples)
num_samples = max_chunk_samples;
else
num_samples = chunk_samples;
send_chunk(in, offset, num_samples);
offset += num_samples * inc->unitsize;
chunk_samples -= num_samples;
processed += num_samples;
}
if ((unsigned int)offset < in->buf->len) {
/*
* The incoming buffer wasn't processed completely. Stash
* the leftover data for next time.
*/
g_string_erase(in->buf, 0, offset);
} else
g_string_truncate(in->buf, 0);
return SR_OK;
}
SR_PRIV struct sr_input_module input_wav = {
.id = "wav",
.description = "WAV file",
.name = "WAV",
.desc = "WAV file",
.metadata = { SR_INPUT_META_HEADER | SR_INPUT_META_REQUIRED },
.format_match = format_match,
.init = init,
.loadfile = loadfile,
.receive = receive,
};

View File

@ -172,37 +172,66 @@ struct sr_context {
struct sr_input {
/**
* A pointer to this input module's 'struct sr_input_module'.
* The frontend can use this to call the module's callbacks.
*/
struct sr_input_module *module;
GHashTable *param;
const struct sr_input_module *module;
GString *buf;
struct sr_dev_inst *sdi;
void *internal;
void *priv;
};
/** Input (file) module driver. */
struct sr_input_module {
/** The unique ID for this input module. Must not be NULL. */
char *id;
/**
* A short description of the input module, which can (for example)
* be displayed to the user by frontends. Must not be NULL.
* A unique ID for this output module, suitable for use in command-line
* clients, [a-z0-9-]. Must not be NULL.
*/
char *description;
const char *id;
/**
* Check if this input module can load and parse the specified file.
* A unique name for this output module, suitable for use in GUI
* clients, can contain UTF-8. Must not be NULL.
*/
const char *name;
/**
* A short description of the output module. Must not be NULL.
*
* @param[in] filename The name (and path) of the file to check.
* This can be displayed by frontends, e.g. when selecting the output
* module for saving a file.
*/
const char *desc;
/**
* Zero-terminated list of metadata items the module needs to be able
* to identify an input stream. Can be all-zero, if the module cannot
* identify streams at all, i.e. has to be forced into use.
*
* Each item is one of:
* SR_INPUT_META_FILENAME
* SR_INPUT_META_FILESIZE
* SR_INPUT_META_HEADER
* SR_INPUT_META_MIMETYPE
*
* If the high bit (SR_INPUT META_REQUIRED) is set, the module cannot
* identify a stream without the given metadata.
*/
const uint8_t metadata[8];
/**
* Returns a NULL-terminated list of options this module can take.
* Can be NULL, if the module has no options.
*/
struct sr_option *(*options) (void);
/**
* Check if this input module can load and parse the specified stream.
*
* @param[in] metadata Metadata the module can use to identify the stream.
*
* @retval TRUE This module knows the format.
* @retval FALSE This module does not know the format.
*/
int (*format_match) (const char *filename);
int (*format_match) (GHashTable *metadata);
/**
* Initialize the input module.
@ -215,7 +244,7 @@ struct sr_input_module {
* @retval SR_OK Success
* @retval other Negative error code.
*/
int (*init) (struct sr_input *in, const char *filename);
int (*init) (struct sr_input *in, GHashTable *options);
/**
* Load a file, parsing the input according to the file's format.
@ -232,12 +261,22 @@ struct sr_input_module {
* @param in A pointer to a valid 'struct sr_input' that the caller
* has to allocate and provide to this function. It is also
* the responsibility of the caller to free it later.
* @param filename The name (and path) of the file to use.
* @param f The name (and path) of the file to use.
*
* @retval SR_OK Success
* @retval other Negative error code.
*/
int (*loadfile) (struct sr_input *in, const char *filename);
int (*receive) (const struct sr_input *in, GString *buf);
/**
* This function is called after the caller is finished using
* the input module, and can be used to free any internal
* resources the module may keep.
*
* @retval SR_OK Success
* @retval other Negative error code.
*/
int (*cleanup) (struct sr_input *in);
};
/** Output module instance. */

View File

@ -1,7 +1,7 @@
/*
* This file is part of the libsigrok project.
*
* Copyright (C) 2010-2012 Bert Vermeulen <bert@biot.com>
* Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by