ref: 7de73b76a65b8f4121cea3c1f573e9f0c16f5aa8
parent: b1143c9c6c75d81eced47e0d60fb410e93c1cd26
author: rrt <rrt>
date: Thu Jan 11 15:18:05 EST 2007
Rename skel.c to skelform.c for clarity, and update it (in particular, add a seek function). Add ability to write file formats in Lua: pseudo-file luaform, and format option --lua-script, to supply the script with which to implement the format.
--- a/README
+++ b/README
@@ -126,7 +126,7 @@
SoX includes skeleton format files to assist you in supporting new
formats, sound effect loops, and special-purpose programs. The full
-skeleton format, skel.c, helps you write a driver for a new format
+skeleton format, skelform.c, helps you write a driver for a new format
which has data structures. skeleff.c is a starting point for writing a
sound effect loop. sox.c is a good starting point for new programs.
(Someone finally did this and told me what was wrong...)
--- a/libst.3
+++ b/libst.3
@@ -43,13 +43,7 @@
.P
The \fBst_close\fR function dissociates the named \fIft_t\fR from its underlying file or set of functions. If the format handler was being used for output, any buffered data is written first.
.P
-Sound Tools includes skeleton C
-files to assist you in writing new formats and effects.
-The full skeleton driver, skel.c, helps you write drivers
-for a new format which has data structures.
-The simple skeleton drivers
-help you write a new driver for raw (headerless) formats, or
-for formats which just have a simple header followed by raw data.
+Sound Tools includes skeleton C files to assist you in writing new formats (skelform.c) and effects (skeleff.c).
.SH RETURN VALUE
Upon successful completion \fBst_open_input\fR and \fBst_open_output\fR return a ft_t (which is a pointer). Otherwise, NULL is returned. TODO: Need a what to return reason for failures. Currently, relies on st_warn to print information.
.P
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -23,10 +23,10 @@
effects = avg.c band.c bandpass.c biquad.c biquad.h breject.c btrworth.c \
btrworth.h chorus.c compand.c dcshift.c deemphas.c earwax.c \
echo.c echos.c equalizer.c fade.c FFT.c FFT.h filter.c flanger.c \
- highp.c highpass.c lowp.c lowpass.c luaeff.c lintlib.c mask.c \
- mcompand.c noiseprof.c noisered.c noisered.h pad.c pan.c \
- phaser.c pitch.c polyphas.c rabbit.c rate.c repeat.c resample.c \
- reverb.c reverse.c silence.c speed.c stat.c \
+ highp.c highpass.c lowp.c lowpass.c luaeff.c luaform.c \
+ lintlib.c mask.c mcompand.c noiseprof.c noisered.c noisered.h \
+ pad.c pan.c phaser.c pitch.c polyphas.c rabbit.c rate.c repeat.c \
+ resample.c reverb.c reverse.c silence.c speed.c stat.c stlua.c \
stretch.c swap.c synth.c tone.c trim.c vibro.c vol.c
libst_la_SOURCES = $(formats) $(effects) alsa.c oss.c sunaudio.c handlers.c misc.c \
--- a/src/fade.c
+++ b/src/fade.c
@@ -1,7 +1,6 @@
-/* This code is based in skel.c
+/*
+ * Ari Moisio <armoi@sci.fi> Aug 29 2000, based on skeleton effect
* Written by Chris Bagwell (cbagwell@sprynet.com) - March 16, 1999
- * Non-skel parts written by
- * Ari Moisio <armoi@sci.fi> Aug 29 2000.
*
* Copyright 1999 Chris Bagwell And Sundry Contributors
* This source code is freely redistributable and may be used for
--- a/src/handlers.c
+++ b/src/handlers.c
@@ -36,6 +36,7 @@
st_hcom_format_fn,
st_la_format_fn,
st_lu_format_fn,
+ st_lua_format_fn,
st_maud_format_fn,
#if defined(HAVE_LIBMAD) || defined(HAVE_LIBMP3LAME)
st_mp3_format_fn,
--- /dev/null
+++ b/src/luaeff.c
@@ -1,0 +1,171 @@
+/*
+ * luaeff - write effects in Lua.
+ *
+ * Copyright 2006-2007 Reuben Thomas
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, write to the Free Software
+ * Foundation, Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301,
+ * USA. */
+
+
+/* TODO: If efficiency is a problem, move the call of the Lua script
+ into the flow phase. Instrument the Lua environment so that scripts
+ can still be written naively: reading beyond the end of the input
+ array yields to read more data, and writing output similarly. In
+ order not to need nonetheless to buffer all input and output until
+ finished, need low-water-marks that the script can update to signal
+ that it has finished reading and writing respectively.
+ Alternatively, assume that each location can only be read/written
+ once. */
+
+
+#include "st_i.h"
+
+#include <string.h>
+#include <lua.h>
+#include <lauxlib.h>
+
+/* Private data for effect */
+typedef struct luaeff {
+ char *script; /* Script filename */
+ lua_State *L; /* Lua state */
+ st_size_t nsamp; /* Number of samples in input */
+ st_sample_t *data; /* Input data */
+} *luaeff_t;
+
+assert_static(sizeof(struct luaeff) <= ST_MAX_EFFECT_PRIVSIZE,
+ /* else */ lua_PRIVSIZE_too_big);
+
+/*
+ * Process command-line options
+ */
+static int lua_getopts(eff_t effp, int n, char **argv)
+{
+ luaeff_t lua = (luaeff_t)effp->priv;
+ int i, ret;
+
+ if (n < 1) {
+ st_fail(effp->h->usage);
+ return ST_EOF;
+ }
+
+ lua->L = st_lua_new();
+
+ /* Collect options into global arg table */
+ lua_createtable(lua->L, n - 1, 0);
+ for (i = 1; i < n; i++) {
+ lua_pushstring(lua->L, argv[i]);
+ lua_rawseti(lua->L, -2, i);
+ }
+ lua_setglobal(lua->L, "arg");
+
+ lua->script = xstrdup(argv[0]);
+
+ if ((ret = luaL_loadfile(lua->L, lua->script)) != 0) {
+ st_fail("cannot load Lua script %s: error %d", lua->script, ret);
+ return ST_EOF;
+ }
+ return ST_SUCCESS;
+}
+
+
+/*
+ * Gather samples.
+ */
+static int lua_flow(eff_t effp, const st_sample_t *ibuf, st_sample_t *obuf UNUSED,
+ st_size_t *isamp, st_size_t *osamp)
+{
+ luaeff_t lua = (luaeff_t)effp->priv;
+
+ lua->data = (st_sample_t *)xrealloc(lua->data, (lua->nsamp + *isamp) * sizeof(st_sample_t));
+ memcpy(lua->data + lua->nsamp, ibuf, *isamp * sizeof(st_sample_t));
+ lua->nsamp += *isamp;
+
+ *osamp = 0; /* Signal that we didn't produce any output */
+
+ return ST_SUCCESS;
+}
+
+/*
+ * Send samples to script.
+ */
+static int lua_drain(eff_t effp, st_sample_t *obuf, st_size_t *osamp)
+{
+ luaeff_t lua = (luaeff_t)effp->priv;
+ int ret;
+ st_sample_t_array_t inarr, outarr;
+
+ inarr.size = lua->nsamp;
+ inarr.data = lua->data;
+ outarr.size = *osamp;
+ outarr.data = obuf;
+
+ st_lua_newarr(lua->L, inarr);
+ st_lua_newarr(lua->L, outarr);
+ if ((ret = lua_pcall(lua->L, 2, 0, 0)) != 0)
+ st_fail("error in Lua script: %d", ret);
+
+ *osamp = 0;
+ return ST_EOF;
+}
+
+/*
+ * Free sample data.
+ */
+static int lua_stop(eff_t effp)
+{
+ luaeff_t lua = (luaeff_t)effp->priv;
+
+ free(lua->data);
+ lua->data = NULL;
+
+ return ST_SUCCESS;
+}
+
+/*
+ * Clean up state.
+ */
+static int lua_delete(eff_t effp)
+{
+ luaeff_t lua = (luaeff_t)effp->priv;
+
+ lua_close(lua->L);
+
+ return ST_SUCCESS;
+}
+
+
+/*
+ * Effect descriptor.
+ */
+static st_effect_t st_lua_effect = {
+ "lua",
+ "Usage: lua script [options]",
+ ST_EFF_MCHAN,
+ lua_getopts,
+ st_effect_nothing,
+ lua_flow,
+ lua_drain,
+ lua_stop,
+ lua_delete
+};
+
+/*
+ * Function returning effect descriptor. This should be the only
+ * externally visible object.
+ */
+const st_effect_t *st_lua_effect_fn(void)
+{
+ return &st_lua_effect;
+}
--- /dev/null
+++ b/src/luaform.c
@@ -1,0 +1,163 @@
+/*
+ * luafile - file formats in Lua.
+ *
+ * Copyright 2006-2007 Reuben Thomas
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, write to the Free Software
+ * Foundation, Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301,
+ * USA. */
+
+
+/* TODO: If efficiency is a problem, move the call of the Lua script
+ into the read/write phase. Instrument the Lua environment so that
+ scripts can still be written naively: reading beyond the end of the
+ input array yields to read more data, and writing output similarly.
+ In order not to need nonetheless to buffer all input and output
+ until finished, need low-water-marks that the script can update to
+ signal that it has finished reading and writing respectively.
+ Alternatively, assume that each location can only be read/written
+ once. */
+
+
+#include "st_i.h"
+
+#include <string.h>
+#include <lua.h>
+#include <lauxlib.h>
+
+/* Private data */
+typedef struct luafile {
+ lua_State *L; /* Lua state */
+} *lua_t;
+
+assert_static(sizeof(struct luafile) <= ST_MAX_FILE_PRIVSIZE,
+ /* else */ skel_PRIVSIZE_too_big);
+
+/*
+ * Set up Lua state and read script.
+ */
+static int lua_start(ft_t ft)
+{
+ lua_t lua = (lua_t)ft->priv;
+ int ret;
+
+ lua->L = st_lua_new();
+
+ if ((ret = luaL_loadfile(lua->L, ft->signal.lua_script)) != 0) {
+ st_fail("cannot load Lua script %s: error %d", ft->signal.lua_script, ret);
+ return ST_EOF;
+ }
+
+ return ST_SUCCESS;
+}
+
+/*
+ * Read up to len samples of type st_sample_t from file into buf[].
+ * Return number of samples read.
+ */
+static st_size_t lua_read(ft_t ft, st_sample_t *buf, st_size_t len)
+{
+ lua_t lua = (lua_t)ft->priv;
+ st_size_t done;
+ int ret;
+ st_sample_t_array_t inarr;
+
+ inarr.size = len;
+ inarr.data = buf;
+
+ lua_pushstring(lua->L, "read");
+ st_lua_newarr(lua->L, inarr);
+ if ((ret = lua_pcall(lua->L, 2, 1, 0)) != 0)
+ st_fail("error in Lua script: %d", ret);
+ done = lua_tointeger(lua->L, -1);
+ lua_pop(lua->L, 1);
+
+ return done;
+}
+
+/*
+ * Write len samples of type st_sample_t from buf[] to file.
+ * Return number of samples written.
+ */
+static st_size_t lua_write(ft_t ft, const st_sample_t *buf, st_size_t len)
+{
+ lua_t lua = (lua_t)ft->priv;
+ int ret;
+ st_sample_t_array_t outarr;
+
+ outarr.size = len;
+ outarr.data = (st_sample_t *)buf;
+
+ lua_pushstring(lua->L, "write");
+ st_lua_newarr(lua->L, outarr);
+ if ((ret = lua_pcall(lua->L, 2, 1, 0)) != 0)
+ st_fail("error in Lua script: %d", ret);
+
+ return len;
+}
+
+/*
+ * Clean up state.
+ */
+static int lua_stop(ft_t ft)
+{
+ lua_t lua = (lua_t)ft->priv;
+
+ lua_close(lua->L);
+
+ return ST_SUCCESS;
+}
+
+static int lua_seek(ft_t ft, st_size_t offset)
+{
+ lua_t lua = (lua_t)ft->priv;
+ int ret;
+
+ lua_pushstring(lua->L, "seek");
+ lua_pushinteger(lua->L, offset);
+ if ((ret = lua_pcall(lua->L, 2, 1, 0)) != 0)
+ st_fail("error in Lua script: %d", ret);
+ /* Seek relative to current position. */
+
+ return ret;
+}
+
+/* Format file suffixes */
+static const char *lua_names[] = {
+ "lua",
+ NULL
+};
+
+/* Format descriptor */
+static st_format_t st_lua_format = {
+ lua_names,
+ NULL,
+ 0,
+ lua_start,
+ lua_read,
+ lua_stop,
+ lua_start,
+ lua_write,
+ lua_stop,
+ lua_seek
+};
+
+/*
+ * Function returning effect descriptor. This should be the only
+ * externally visible object.
+ */
+const st_format_t *st_lua_format_fn(void)
+{
+ return &st_lua_format;
+}
--- a/src/skel.c
+++ /dev/null
@@ -1,201 +1,0 @@
-/*
- * Sound Tools skeleton file format driver.
- *
- * Copyright 1999 Chris Bagwell And Sundry Contributors
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library. If not, write to the Free Software
- * Foundation, Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301,
- * USA. */
-
-#include "st_i.h"
-
-/* Private data for SKEL file */
-typedef struct skel
-{
- st_size_t samples_remaining;
-} *skel_t;
-
-/* Note that if any of your methods doesn't need to do anything, you
- can instead use the relevant st_*_nothing* method */
-
-/*
- * Do anything required before you start reading samples.
- * Read file header.
- * Find out sampling rate,
- * size and encoding of samples,
- * mono/stereo/quad.
- */
-static int skel_startread(ft_t ft)
-{
- skel_t sk = (skel_t)ft->priv;
-
- /* If you need to seek around the input file. */
- if (!ft->seekable) {
- st_fail_errno(ft,ST_EVALUE,"SKEL input file must be a file, not a pipe");
- return (ST_EOF);
- }
-
- /*
- * If your format is headerless and has fixed values for
- * the following items, you can hard code them here (see cdr.c).
- * If your format contains a header with format information
- * then you should set it here.
- */
- ft->signal.rate = 44100L;
- ft->signal.size = ST_SIZE_BYTE or WORD ...;
- ft->signal.encoding = ST_ENCODING_UNSIGNED or SIGN2 ...;
- ft->signal.channels = 1 or 2 or 4;
- ft->comment = xmalloc(size_of_comment);
- strcpy(ft->comment, "any comment in file header.");
-
- /* If your format doesn't have a header then samples_in_file
- * can be determined by the file size.
- */
- samples_in_file = st_filelength(ft)/ft->signal.size;
-
- /* If you can detect the length of your file, record it here. */
- ft->length = samples_in_file;
- sk->remaining_samples = samples_in_file;
-
- return (ST_SUCCESS);
-}
-
-/*
- * Read up to len samples from file.
- * Convert to st_sample_t.
- * Place in buf[].
- * Return number of samples read.
- */
-static st_size_t skel_read(ft_t ft, st_sample_t *buf, st_size_t len)
-{
- skel_t sk = (skel_t)ft->priv;
- st_size_t done;
- st_sample_t l;
-
- /* Always return a full frame of audio data */
- if (len % ft->signal.size)
- len -= (len % ft->signal.size);
-
- for (done = 0; done < len; done++) {
- if no more samples
- break
- get a sample
- switch (ft->signal.size) {
- case ST_SIZE_BYTE:
- switch (ft->signal.encoding) {
- case ST_ENCODING_UNSIGNED;
- *buf++ = ST_UNSIGNED_BYTE_TO_SAMPLE(sample);
- break;
- }
- break;
- }
- }
-
- return done;
-}
-
-/*
- * Do anything required when you stop reading samples.
- * Don't close input file!
- */
-static int skel_stopread(ft_t ft)
-{
- return ST_SUCCESS;
-}
-
-static int skel_startwrite(ft_t ft)
-{
- skel_t sk = (skel_t)ft->priv;
-
- /* If you have to seek around the output file. */
- /* If header contains a length value then seeking will be
- * required. Instead of failing, it's sometimes nice to
- * just set the length to max value and not fail.
- */
- if (!ft->seekable) {
- st_fail_errno(ft, ST_EVALUE, "Output .skel file must be a file, not a pipe");
- return ST_EOF;
- }
-
- if (ft->signal.rate != 44100L)
- st_fail_errno(ft, ST_EVALUE, "Output .skel file must have a sample rate of 44100");
-
- if (ft->signal.size == -1) {
- st_fail_errno(ft, ST_EVALUE, "Did not specify a size for .skel output file");
- return ST_EOF;
- }
-
- error check ft->signal.encoding;
- error check ft->signal.channels;
-
- /* Write file header, if any */
- /* Write comment field, if any */
-
- return ST_SUCCESS;
-
-}
-
-static st_size_t skel_write(ft_t ft, const st_sample_t *buf, st_size_t len)
-{
- skel_t sk = (skel_t)ft->priv;
- st_size_t len = 0;
-
- switch (ft->signal.size) {
- case ST_SIZE_BYTE:
- switch (ft->signal.encoding) {
- case ST_ENCODING_UNSIGNED:
- while (len--) {
- len = st_writeb(ft, ST_SAMPLE_TO_UNSIGNED_BYTE(*buff++, ft->clippedCount));
- if (len == ST_EOF)
- break;
- }
- break;
- }
- break;
- }
-
- return len;
-}
-
-static int skel_stopwrite(ft_t ft)
-{
- /* All samples are already written out. */
- /* If file header needs fixing up, for example it needs the */
- /* the number of samples in a field, seek back and write them here. */
- return ST_SUCCESS;
-}
-
-/* Format file suffixes */
-static const char *skel_names[] = {
- "skel",
- NULL
-};
-
-static st_format_t st_skel_format = {
- skel_names,
- NULL,
- ST_FILE_STEREO | ST_FILE_SEEK,
- skel_startread,
- skel_read,
- skel_stopread,
- skel_startwrite,
- skel_write,
- skel_stopwrite,
- skel_seek
-};
-
-const st_format_t *st_skel_format_fn()
-{
- return &st_skel_format;
-}
--- /dev/null
+++ b/src/skelform.c
@@ -1,0 +1,213 @@
+/*
+ * Sound Tools skeleton file format driver.
+ *
+ * Copyright 1999 Chris Bagwell And Sundry Contributors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, write to the Free Software
+ * Foundation, Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301,
+ * USA. */
+
+#include "st_i.h"
+
+/* Private data for SKEL file */
+typedef struct skelform
+{
+ st_size_t samples_remaining;
+} *skelform_t;
+
+assert_static(sizeof(struct skelform) <= ST_MAX_FILE_PRIVSIZE,
+ /* else */ skel_PRIVSIZE_too_big);
+
+/* Note that if any of your methods doesn't need to do anything, you
+ can instead use the relevant st_*_nothing* method */
+
+/*
+ * Do anything required before you start reading samples.
+ * Read file header.
+ * Find out sampling rate,
+ * size and encoding of samples,
+ * mono/stereo/quad.
+ */
+static int skel_startread(ft_t ft)
+{
+ skelform_t sk = (skelform_t)ft->priv;
+
+ /* If you need to seek around the input file. */
+ if (!ft->seekable) {
+ st_fail_errno(ft,ST_EVALUE,"SKEL input file must be a file, not a pipe");
+ return (ST_EOF);
+ }
+
+ /*
+ * If your format is headerless and has fixed values for
+ * the following items, you can hard code them here (see cdr.c).
+ * If your format contains a header with format information
+ * then you should set it here.
+ */
+ ft->signal.rate = 44100L;
+ ft->signal.size = ST_SIZE_BYTE or WORD ...;
+ ft->signal.encoding = ST_ENCODING_UNSIGNED or SIGN2 ...;
+ ft->signal.channels = 1 or 2 or 4;
+ ft->comment = xmalloc(size_of_comment);
+ strcpy(ft->comment, "any comment in file header.");
+
+ /* If your format doesn't have a header then samples_in_file
+ * can be determined by the file size.
+ */
+ samples_in_file = st_filelength(ft) / ft->signal.size;
+
+ /* If you can detect the length of your file, record it here. */
+ ft->length = samples_in_file;
+ sk->remaining_samples = samples_in_file;
+
+ return (ST_SUCCESS);
+}
+
+/*
+ * Read up to len samples of type st_sample_t from file into buf[].
+ * Return number of samples read.
+ */
+static st_size_t skel_read(ft_t ft, st_sample_t *buf, st_size_t len)
+{
+ skelform_t sk = (skelform_t)ft->priv;
+ st_size_t done;
+ st_sample_t l;
+
+ /* Always return a full frame of audio data */
+ if (len % ft->signal.size)
+ len -= (len % ft->signal.size);
+
+ for (done = 0; done < len; done++) {
+ if no more samples
+ break
+ get a sample
+ switch (ft->signal.size) {
+ case ST_SIZE_BYTE:
+ switch (ft->signal.encoding) {
+ case ST_ENCODING_UNSIGNED;
+ *buf++ = ST_UNSIGNED_BYTE_TO_SAMPLE(sample);
+ break;
+ }
+ break;
+ }
+ }
+
+ return done;
+}
+
+/*
+ * Do anything required when you stop reading samples.
+ * Don't close input file!
+ */
+static int skel_stopread(ft_t ft)
+{
+ return ST_SUCCESS;
+}
+
+static int skel_startwrite(ft_t ft)
+{
+ skelform_t sk = (skelform_t)ft->priv;
+
+ /* If you have to seek around the output file. */
+ /* If header contains a length value then seeking will be
+ * required. Instead of failing, it's sometimes nice to
+ * just set the length to max value and not fail.
+ */
+ if (!ft->seekable) {
+ st_fail_errno(ft, ST_EVALUE, "Output .skel file must be a file, not a pipe");
+ return ST_EOF;
+ }
+
+ if (ft->signal.rate != 44100L)
+ st_fail_errno(ft, ST_EVALUE, "Output .skel file must have a sample rate of 44100");
+
+ if (ft->signal.size == -1) {
+ st_fail_errno(ft, ST_EVALUE, "Did not specify a size for .skel output file");
+ return ST_EOF;
+ }
+
+ error check ft->signal.encoding;
+ error check ft->signal.channels;
+
+ /* Write file header, if any */
+ /* Write comment field, if any */
+
+ return ST_SUCCESS;
+
+}
+
+/*
+ * Write len samples of type st_sample_t from buf[] to file.
+ * Return number of samples written.
+ */
+static st_size_t skel_write(ft_t ft, const st_sample_t *buf, st_size_t len)
+{
+ skelform_t sk = (skelform_t)ft->priv;
+ st_size_t len = 0;
+
+ switch (ft->signal.size) {
+ case ST_SIZE_BYTE:
+ switch (ft->signal.encoding) {
+ case ST_ENCODING_UNSIGNED:
+ while (len--) {
+ len = st_writeb(ft, ST_SAMPLE_TO_UNSIGNED_BYTE(*buff++, ft->clippedCount));
+ if (len == ST_EOF)
+ break;
+ }
+ break;
+ }
+ break;
+ }
+
+ return len;
+}
+
+static int skel_stopwrite(ft_t ft)
+{
+ /* All samples are already written out. */
+ /* If file header needs fixing up, for example it needs the number
+ of samples in a field, seek back and write them here. */
+ return ST_SUCCESS;
+}
+
+static int skel_seek(ft_t ft, st_size_t offset)
+{
+ /* Seek relative to current position. */
+ return ST_SUCCESS;
+}
+
+/* Format file suffixes */
+static const char *skel_names[] = {
+ "skel",
+ NULL
+};
+
+/* Format descriptor */
+static st_format_t st_skel_format = {
+ skel_names,
+ NULL,
+ ST_FILE_STEREO | ST_FILE_SEEK,
+ skel_startread,
+ skel_read,
+ skel_stopread,
+ skel_startwrite,
+ skel_write,
+ skel_stopwrite,
+ skel_seek
+};
+
+const st_format_t *st_skel_format_fn()
+{
+ return &st_skel_format;
+}
--- a/src/sox.c
+++ b/src/sox.c
@@ -208,33 +208,33 @@
static file_info_t make_file_info(void)
{
- file_info_t fo = xcalloc(sizeof(*fo), 1);
+ file_info_t fi = xcalloc(sizeof(*fi), 1);
- fo->signal.size = -1;
- fo->signal.encoding = ST_ENCODING_UNKNOWN;
- fo->signal.channels = 0;
- fo->signal.reverse_bytes = ST_REVERSE_DEFAULT;
- fo->signal.reverse_nibbles = ST_REVERSE_DEFAULT;
- fo->signal.reverse_bits = ST_REVERSE_DEFAULT;
- fo->signal.compression = HUGE_VAL;
- fo->volume = HUGE_VAL;
- fo->volume_clips = 0;
+ fi->signal.size = -1;
+ fi->signal.encoding = ST_ENCODING_UNKNOWN;
+ fi->signal.channels = 0;
+ fi->signal.reverse_bytes = ST_REVERSE_DEFAULT;
+ fi->signal.reverse_nibbles = ST_REVERSE_DEFAULT;
+ fi->signal.reverse_bits = ST_REVERSE_DEFAULT;
+ fi->signal.compression = HUGE_VAL;
+ fi->volume = HUGE_VAL;
+ fi->volume_clips = 0;
- return fo;
+ return fi;
}
-static void set_device(file_info_t fo)
+static void set_device(file_info_t fi)
{
#if defined(HAVE_ALSA)
- fo->filetype = "alsa";
- fo->filename = xstrdup("default");
+ fi->filetype = "alsa";
+ fi->filename = xstrdup("default");
#elif defined(HAVE_OSS)
- fo->filetype = "ossdsp";
- fo->filename = xstrdup("/dev/dsp");
+ fi->filetype = "ossdsp";
+ fi->filename = xstrdup("/dev/dsp");
#elif defined (HAVE_SUN_AUDIO)
char *device = getenv("AUDIODEV");
- fo->filetype = "sunau";
- fo->filename = xstrdup(device ? device : "/dev/audio");
+ fi->filetype = "sunau";
+ fi->filename = xstrdup(device ? device : "/dev/audio");
#endif
}
@@ -259,8 +259,8 @@
/* Loop over arguments and filenames, stop when an effect name is
* found. */
while (optind < argc && !is_effect_name(argv[optind])) {
- file_info_t fo = make_file_info();
- struct file_info fo_none;
+ file_info_t fi = make_file_info();
+ struct file_info fi_none;
if (file_count >= MAX_FILES) {
st_fail("Too many filenames; maximum is %d input files and 1 output file", MAX_INPUT_FILES);
@@ -267,23 +267,23 @@
exit(1);
}
- fo_none = *fo;
+ fi_none = *fi;
- if (doopts(fo, argc, argv)) { /* is null file? */
- if (fo->filetype != NULL && strcmp(fo->filetype, "null") != 0)
- st_warn("Ignoring \"-t %s\".", fo->filetype);
- fo->filetype = "null";
- fo->filename = xstrdup(fo->filetype);
+ if (doopts(fi, argc, argv)) { /* is null file? */
+ if (fi->filetype != NULL && strcmp(fi->filetype, "null") != 0)
+ st_warn("Ignoring \"-t %s\".", fi->filetype);
+ fi->filetype = "null";
+ fi->filename = xstrdup(fi->filetype);
} else {
if (optind >= argc || is_effect_name(argv[optind])) {
- if (memcmp(fo, &fo_none, sizeof(fo_none)) != 0) /* fopts but no file */
+ if (memcmp(fi, &fi_none, sizeof(fi_none)) != 0) /* fopts but no file */
usage("missing filename"); /* No return */
- free(fo); /* No file opts and no filename, so that's okay */
+ free(fi); /* No file opts and no filename, so that's okay */
continue;
}
- fo->filename = xstrdup(argv[optind++]);
+ fi->filename = xstrdup(argv[optind++]);
}
- file_opts[file_count++] = fo;
+ file_opts[file_count++] = fi;
}
if (rec) {
@@ -425,6 +425,7 @@
{"endian" , required_argument, NULL, 0},
{"interactive" , no_argument, NULL, 0},
{"help-effect" , required_argument, NULL, 0},
+ {"lua-script" , required_argument, NULL, 0},
{"octave" , no_argument, NULL, 0},
{"version" , no_argument, NULL, 0},
@@ -444,7 +445,7 @@
{NULL, 0, NULL, 0}
};
-static bool doopts(file_info_t fo, int argc, char **argv)
+static bool doopts(file_info_t fi, int argc, char **argv)
{
bool isnull = false;
int option_index, c;
@@ -457,20 +458,20 @@
case 0: /* Long options with no short equivalent. */
switch (option_index) {
case 0:
- fo->comment = read_comment_file(optarg);
+ fi->comment = read_comment_file(optarg);
break;
case 1:
- fo->comment = xstrdup(optarg);
+ fi->comment = xstrdup(optarg);
break;
case 2:
if (!strcmp(optarg, "little"))
- fo->signal.reverse_bytes = ST_IS_BIGENDIAN;
+ fi->signal.reverse_bytes = ST_IS_BIGENDIAN;
else if (!strcmp(optarg, "big"))
- fo->signal.reverse_bytes = ST_IS_LITTLEENDIAN;
+ fi->signal.reverse_bytes = ST_IS_LITTLEENDIAN;
else if (!strcmp(optarg, "swap"))
- fo->signal.reverse_bytes = true;
+ fi->signal.reverse_bytes = true;
break;
case 3:
@@ -482,10 +483,14 @@
break;
case 5:
+ fi->signal.lua_script = xstrdup(optarg);
+ break;
+
+ case 6:
globalinfo.octave_plot_effect = true;
break;
- case 8:
+ case 7:
printf("%s: v%s\n", myname, st_version());
exit(0);
break;
@@ -513,9 +518,9 @@
break;
case 't':
- fo->filetype = optarg;
- if (fo->filetype[0] == '.')
- fo->filetype++;
+ fi->filetype = optarg;
+ if (fi->filetype[0] == '.')
+ fi->filetype++;
break;
case 'r':
@@ -523,16 +528,16 @@
st_fail("Rate value '%s' is not a positive integer", optarg);
exit(1);
}
- fo->signal.rate = i;
+ fi->signal.rate = i;
break;
case 'v':
- if (sscanf(optarg, "%lf %c", &fo->volume, &dummy) != 1) {
+ if (sscanf(optarg, "%lf %c", &fi->volume, &dummy) != 1) {
st_fail("Volume value '%s' is not a number", optarg);
exit(1);
}
uservolume = 1;
- if (fo->volume < 0.0)
+ if (fi->volume < 0.0)
st_report("Volume adjustment is negative; "
"this will result in a phase change");
break;
@@ -542,59 +547,59 @@
st_fail("Channels value '%s' is not a positive integer", optarg);
exit(1);
}
- fo->signal.channels = i;
+ fi->signal.channels = i;
break;
case 'C':
- if (sscanf(optarg, "%lf %c", &fo->signal.compression, &dummy) != 1) {
+ if (sscanf(optarg, "%lf %c", &fi->signal.compression, &dummy) != 1) {
st_fail("Compression value '%s' is not a number", optarg);
exit(1);
}
break;
- case '1': case 'b': fo->signal.size = ST_SIZE_BYTE; break;
- case '2': case 'w': fo->signal.size = ST_SIZE_WORD; break;
- case '3': fo->signal.size = ST_SIZE_24BIT; break;
- case '4': case 'l': fo->signal.size = ST_SIZE_DWORD; break;
- case '8': case 'd': fo->signal.size = ST_SIZE_DDWORD; break;
+ case '1': case 'b': fi->signal.size = ST_SIZE_BYTE; break;
+ case '2': case 'w': fi->signal.size = ST_SIZE_WORD; break;
+ case '3': fi->signal.size = ST_SIZE_24BIT; break;
+ case '4': case 'l': fi->signal.size = ST_SIZE_DWORD; break;
+ case '8': case 'd': fi->signal.size = ST_SIZE_DDWORD; break;
- case 's': fo->signal.encoding = ST_ENCODING_SIGN2; break;
- case 'u': fo->signal.encoding = ST_ENCODING_UNSIGNED; break;
- case 'f': fo->signal.encoding = ST_ENCODING_FLOAT; break;
- case 'a': fo->signal.encoding = ST_ENCODING_ADPCM; break;
- case 'D': fo->signal.encoding = ST_ENCODING_MS_ADPCM; break; /* WIP */
- case 'i': fo->signal.encoding = ST_ENCODING_IMA_ADPCM; break;
- case 'o': fo->signal.encoding = ST_ENCODING_OKI_ADPCM; break; /* WIP */
- case 'g': fo->signal.encoding = ST_ENCODING_GSM; break;
+ case 's': fi->signal.encoding = ST_ENCODING_SIGN2; break;
+ case 'u': fi->signal.encoding = ST_ENCODING_UNSIGNED; break;
+ case 'f': fi->signal.encoding = ST_ENCODING_FLOAT; break;
+ case 'a': fi->signal.encoding = ST_ENCODING_ADPCM; break;
+ case 'D': fi->signal.encoding = ST_ENCODING_MS_ADPCM; break; /* WIP */
+ case 'i': fi->signal.encoding = ST_ENCODING_IMA_ADPCM; break;
+ case 'o': fi->signal.encoding = ST_ENCODING_OKI_ADPCM; break; /* WIP */
+ case 'g': fi->signal.encoding = ST_ENCODING_GSM; break;
- case 'U': fo->signal.encoding = ST_ENCODING_ULAW;
- if (fo->signal.size == -1)
- fo->signal.size = ST_SIZE_BYTE;
+ case 'U': fi->signal.encoding = ST_ENCODING_ULAW;
+ if (fi->signal.size == -1)
+ fi->signal.size = ST_SIZE_BYTE;
break;
- case 'A': fo->signal.encoding = ST_ENCODING_ALAW;
- if (fo->signal.size == -1)
- fo->signal.size = ST_SIZE_BYTE;
+ case 'A': fi->signal.encoding = ST_ENCODING_ALAW;
+ if (fi->signal.size == -1)
+ fi->signal.size = ST_SIZE_BYTE;
break;
case 'L':
- fo->signal.reverse_bytes = ST_IS_BIGENDIAN;
+ fi->signal.reverse_bytes = ST_IS_BIGENDIAN;
break;
case 'B':
- fo->signal.reverse_bytes = ST_IS_LITTLEENDIAN;
+ fi->signal.reverse_bytes = ST_IS_LITTLEENDIAN;
break;
case 'x':
- fo->signal.reverse_bytes = ST_REVERSE_YES;
+ fi->signal.reverse_bytes = ST_REVERSE_YES;
break;
case 'X':
- fo->signal.reverse_bits = ST_REVERSE_YES;
+ fi->signal.reverse_bits = ST_REVERSE_YES;
break;
case 'N':
- fo->signal.reverse_nibbles = ST_REVERSE_YES;
+ fi->signal.reverse_nibbles = ST_REVERSE_YES;
break;
case 'V':
@@ -1599,12 +1604,12 @@
}
/* Adjust volume based on value specified by the -v option for this file. */
-static void volumechange(st_sample_t * buf, st_ssize_t len, file_info_t fo)
+static void volumechange(st_sample_t * buf, st_ssize_t len, file_info_t fi)
{
- if (fo->volume != HUGE_VAL && fo->volume != 1)
+ if (fi->volume != HUGE_VAL && fi->volume != 1)
while (len--) {
- double d = fo->volume * *buf;
- *buf++ = ST_ROUND_CLIP_COUNT(d, fo->volume_clips);
+ double d = fi->volume * *buf;
+ *buf++ = ST_ROUND_CLIP_COUNT(d, fi->volume_clips);
}
}
@@ -1657,8 +1662,8 @@
"-c channels number of channels in audio data\n"
"-C compression compression factor for variably compressing output formats\n"
"--comment text Specify comment text for the output file\n"
- "--comment-file filename\n"
- " Specify file containing comment text for the output file\n"
+ "--comment-file filename file containing comment text for the output file\n"
+ "--lua-script filename file containing script for Lua pseudo-file\n"
"-r rate sample rate of audio\n"
"-t filetype file type of audio\n"
"-x/-N/-X invert auto-detected endianness/nibble-order/bit-order of data\n"
@@ -1667,7 +1672,6 @@
" -a/-i/-g/-f ADPCM/IMA_ADPCM/GSM/floating point\n"
"-1/-2/-3/-4/-8 sample size in bytes\n"
"-b/-w/-l/-d aliases for -1/-2/-4/-8 (byte, word, long, double-long)\n"
- "\n"
"-v volume input file volume adjustment factor (real number)\n"
"-R use default random numbers (same on each run of SoX)\n"
"\n");
--- a/src/st.h
+++ b/src/st.h
@@ -204,7 +204,7 @@
/* Signal parameters */
-typedef struct st_signalinfo
+typedef struct st_signalinfo
{
st_rate_t rate; /* sampling rate */
signed char size; /* word length of data */
@@ -214,6 +214,7 @@
st_reverse_t reverse_nibbles;
st_reverse_t reverse_bits;
double compression; /* compression factor (where applicable) */
+ char *lua_script; /* Lua script to use for Lua pseudo-file */
} st_signalinfo_t;
/* Loop parameters */
--- a/src/st_i.h
+++ b/src/st_i.h
@@ -16,6 +16,8 @@
#include "stconfig.h"
#include "st.h"
+#include <lua.h>
+
#include "xmalloc.h"
#ifdef HAVE_BYTESWAP_H
@@ -202,6 +204,7 @@
extern const st_format_t *st_al_format_fn(void);
extern const st_format_t *st_la_format_fn(void);
extern const st_format_t *st_lu_format_fn(void);
+extern const st_format_t *st_lua_format_fn(void);
extern const st_format_t *st_s3_format_fn(void);
extern const st_format_t *st_sb_format_fn(void);
extern const st_format_t *st_sl_format_fn(void);
@@ -255,6 +258,14 @@
#define st_rawstartread(ft) st_rawstart(ft, false, false, ST_ENCODING_UNKNOWN, -1, ST_REVERSE_DEFAULT)
#define st_rawstartwrite st_rawstartread
#define st_rawstopread st_format_nothing
+
+/* Lua functions and types in stlua.c */
+typedef struct {
+ st_size_t size;
+ st_sample_t *data;
+} st_sample_t_array_t;
+int st_lua_newarr(lua_State *L, st_sample_t_array_t arr);
+void *st_lua_new(void);
/*=============================================================================
--- /dev/null
+++ b/src/stlua.c
@@ -1,0 +1,129 @@
+/*
+ * luast - miscellaneous Lua support functions.
+ *
+ * Copyright 2006-2007 Reuben Thomas
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, write to the Free Software
+ * Foundation, Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301,
+ * USA. */
+
+#include "st_i.h"
+
+#include <assert.h>
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+/* st_sample_t arrays */
+
+static const char *handle = "st_sample_t array";
+
+int st_lua_newarr(lua_State *L, st_sample_t_array_t arr)
+{
+ lua_newuserdata(L, sizeof(st_sample_t_array_t));
+ *(st_sample_t_array_t *)lua_touserdata(L, -1) = arr;
+ luaL_getmetatable(L, handle);
+ lua_setmetatable(L, -2);
+ return 1;
+}
+
+static int arr_index(lua_State *L)
+ /* array, key -> value */
+{
+ st_sample_t_array_t *p = luaL_checkudata(L, 1, handle);
+ lua_Integer k = luaL_checkinteger(L, 2);
+
+ if ((st_size_t)k >= p->size)
+ lua_pushnil(L);
+ else
+ lua_pushinteger(L, (lua_Integer)p->data[k]);
+
+ return 1;
+}
+
+static int arr_newindex(lua_State *L)
+ /* array, key, value -> */
+{
+ st_sample_t_array_t *p = luaL_checkudata(L, 1, handle);
+ lua_Integer k = luaL_checkinteger(L, 2);
+ lua_Integer v = luaL_checkinteger(L, 3);
+
+ /* FIXME: Have some indication for out of range */
+ if ((st_size_t)k < p->size)
+ p->data[k] = v;
+
+ return 0;
+}
+
+static int arr_len(lua_State *L)
+ /* array -> #array */
+{
+ st_sample_t_array_t *p;
+ p = luaL_checkudata(L, 1, handle);
+ lua_pushinteger(L, (lua_Integer)p->size);
+ return 1;
+}
+
+static int arr_tostring(lua_State *L)
+{
+ char buf[256];
+ void *udata = luaL_checkudata(L, 1, handle);
+ if(udata) {
+ sprintf(buf, "%s (%p)", handle, udata);
+ lua_pushstring(L, buf);
+ }
+ else {
+ sprintf(buf, "must be userdata of type '%s'", handle);
+ luaL_argerror(L, 1, buf);
+ }
+ return 1;
+}
+
+/* Metatable */
+static const luaL_reg meta[] = {
+ {"__index", arr_index},
+ {"__newindex", arr_newindex},
+ {"__len", arr_len},
+ {"__tostring", arr_tostring},
+ {NULL, NULL}
+};
+
+/* Allocator function for use by Lua */
+static void *lua_alloc(void *ud UNUSED, void *ptr, size_t osize UNUSED, size_t nsize)
+{
+ if (nsize == 0) {
+ free(ptr);
+ return NULL;
+ } else
+ return xrealloc(ptr, nsize);
+}
+
+void *st_lua_new(void)
+{
+ lua_State *L;
+
+ /* Since the allocator quits if it fails, this should always
+ succeed if it returns. */
+ assert((L = lua_newstate(lua_alloc, NULL)));
+
+ /* TODO: If concerned about security, lock down here: in particular,
+ don't open the io library. */
+ luaL_openlibs(L);
+
+ luaopen_int(L);
+
+ /* Create st_sample_t array userdata type */
+ createmeta(L, handle);
+ luaL_register(L, NULL, meta);
+}