ref: b9cb8289a553aa616c8ecaa450b5c07b0e76d6c7
parent: e1bb8ea335d3517f9b3153bdc56b237847e2c7cd
author: cbagwell <cbagwell>
date: Wed Feb 28 16:05:38 EST 2001
Added new synth effect and nul file handler. Allows sounds to be created. Added new mix program that will mix two seperate audio files into one.
--- a/Changelog
+++ b/Changelog
@@ -34,6 +34,14 @@
o With help from David Blythe, updated OSS drivers to use newer format
interface. OSS driver will now attempt to detect a valid endian type
to use with sound card.
+ o Carsten Borchardt pointed out a bug in lowp filter. Added new
+ nul file handler that reads and writes from/to nothing.
+ Also added new synth effect that creates sounds using a simple
+ synthesizer. Created a testcd.sh that uses two new features
+ to create a test sound CD for testing audio equipment.
+ o Ben Last added a new program that uses libst and will merge two
+ seperate audio files into a single file with multiple channels.
+ Written for sox 12.16 so doesn't work with 12.17.2 yet.
sox-12.17.1
-----------
--- a/Makefile.dos
+++ b/Makefile.dos
@@ -12,7 +12,7 @@
FOBJ = 8svx.obj adpcm.obj aiff.obj alsa.obj au.obj auto.obj avr.obj cdr.obj \
cvsd.obj dat.obj g721.obj g723_24.obj g723_40.obj g72x.obj gsm.obj \
- hcom.obj ima_rw.obj maud.obj oss.obj raw.obj sf.obj smp.obj \
+ hcom.obj ima_rw.obj maud.obj nul.obj oss.obj raw.obj sf.obj smp.obj \
sndrtool.obj sphere.obj sunaudio.obj tx16w.obj voc.obj \
wav.obj wve.obj
@@ -22,7 +22,7 @@
lowp.obj lowpass.obj map.obj mask.obj phaser.obj pick.obj \
pitch.obj pan.obj polyphase.obj rate.obj resample.obj reverb.obj \
reverse.obj speed.obj split.obj stat.obj stretch.obj \
- swap.obj trim.obj vibro.obj vol.obj
+ swap.obj synth.obj trim.obj vibro.obj vol.obj
LIBOBJS = $(FOBJ) $(EOBJ) handlers.obj libst.obj misc.obj getopt.obj util.obj
@@ -66,14 +66,20 @@
# $(CC) $(CFLAGS) $*.c
# $(LDD) libst -+$*,,
-all: sox.exe
+all: sox.exe mix.exe
sox.exe: sox.obj libst.lib
$(CC) $(LFLAGS) -L$(LIBDIR) sox.obj libst.lib
+mix.exe: mix.obj libst.lib
+ $(CC) $(LFLAGS) -L$(LIBDIR) mix.obj libst.lib
+
libst.lib: $(LIBOBJS)
sox.obj: sox.c st.h
+ $(CC) $(CFLAGS) -I$(INCDIR) -L$(LIBDIR) $*.c
+
+mix.obj: mix.c st.h
$(CC) $(CFLAGS) -I$(INCDIR) -L$(LIBDIR) $*.c
clean:
--- a/Makefile.gcc
+++ b/Makefile.gcc
@@ -30,14 +30,14 @@
FOBJ = 8svx.o adpcm.o aiff.o alsa.o au.o auto.o avr.o cdr.o cvsd.o dat.o \
g721.o g723_24.o g723_40.o g72x.o gsm.o hcom.o ima_rw.o maud.o \
- oss.o raw.o sf.o smp.o sndrtool.o sphere.o sunaudio.o tx16w.o \
- voc.o wav.o wve.o
+ nul.o oss.o raw.o sf.o smp.o sndrtool.o sphere.o sunaudio.o \
+ tx16w.o voc.o wav.o wve.o
EOBJ = avg.o band.o bandpass.o breject.o btrworth.o chorus.o compand.o \
copy.o cut.o deemphas.o earwax.o echo.o echos.o fade.o filter.o \
flanger.o highp.o highpass.o lowp.o lowpass.o map.o mask.o pan.o \
phaser.o pick.o pitch.o polyphas.o rate.o resample.o reverb.o \
- reverse.o speed.o split.o stat.o stretch.o swap.o \
+ reverse.o speed.o split.o stat.o stretch.o swap.o synth.o \
trimo vibro.o vol.o
SOUNDLIB = libst.a
@@ -188,10 +188,13 @@
CFLAGS = $O $(SOX_DEFINES) $(SOX_INCLUDES)
-all: sox
+all: sox mix
sox: sox.o $(SOUNDLIB)
$(CC) $(CFLAGS) -o sox sox.o $(SOUNDLIB) $(SOX_PRE_LIBS) $(SOX_POST_LIBS)
+
+mix: mix.o $(SOUNDLIB)
+ $(CC) $(CFLAGS) -o mix mix.o $(SOUNDLIB) $(SOX_PRE_LIBS) $(SOX_POST_LIBS)
$(SOUNDLIB): $(LIBOBJS)
$(RM) $(SOUNDLIB)
--- a/README
+++ b/README
@@ -37,6 +37,7 @@
o Psion (palmtop) A-law WVE files
o Pseudo-file fomats that allow direct playing/recording
from some audio devices under unix.
+ o Pseudo-nul file that reads and writes from/to nowhere
The sound effects include:
@@ -65,10 +66,11 @@
o Reverse the sound samples (to search for Satanic messages ;-)
o Change the speed of samples being played (like speeding up the motor
on a tape recorder)
- o Stretch/shorten the duration of a sound file.
o Convert from mono to stereo
- o Swap stereo channels
o Display general stats on a sound sample
+ o Stretch/shorten the duration of a sound file.
+ o Swap stereo channels
+ o Create sounds with a simple synthesizer.
o Trim audio data from beginning and end of file.
o Add the world-famous Fender Vibro-Champ effect
o Adjust volume of samples
--- /dev/null
+++ b/mix.c
@@ -1,0 +1,565 @@
+/*
+ * Mix - the audiofile mixing program of SOX
+ *
+ * This is the main function for the command line sox program.
+ *
+ * January 26, 2001
+ * Copyright 2001 ben last, Lance Norskog And Sundry Contributors
+ * This source code is freely redistributable and may be used for
+ * any purpose. This copyright notice must be maintained.
+ * ben last, Lance Norskog And Sundry Contributors are not responsible
+ * for the consequences of using this software.
+ *
+ * Change History:
+ *
+ * January 26, 2001 - ben last (ben@benlast.com)
+ * Derived mix.c from sox.c and then rewrote it for
+ * mix functionality.
+ */
+
+/* FIXME: Quickly ported from 12.16 to 12.17.2... Make sure all st_*
+ * functions are checking return values!
+ */
+
+#include "st.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h> /* for malloc() */
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#include <errno.h>
+#include <sys/types.h> /* for fstat() */
+#include <sys/stat.h> /* for fstat() */
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for unlink() */
+#endif
+
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+#ifndef HAVE_GETOPT
+int getopt(int,char **,char *);
+extern char *optarg;
+extern int optind;
+#endif
+#endif
+
+#ifdef VMS
+#include <perror.h>
+#define LASTCHAR ']'
+#else
+#define LASTCHAR '/'
+#endif
+
+/*
+ * SOX mix main program.
+ */
+
+static int dovolume = 0; /* User wants volume change */
+static double volume = 1.0; /* Linear volume change */
+static int clipped = 0; /* Volume change clipping errors */
+static int writing = 0; /* are we writing to a file? */
+
+void init();
+void doopts(int, char **);
+void usage(char *);
+int filetype(int);
+void process();
+void statistics();
+LONG volumechange();
+void checkeffect(eff_t);
+int flow_effect(int);
+int drain_effect(int);
+
+struct st_soundstream informat, mixformat, outformat;
+
+static ft_t ft;
+
+char *ifile, *mfile, *ofile, *itype, *mtype, *otype;
+extern char *optarg;
+extern int optind;
+
+int main(argc, argv)
+int argc;
+char **argv;
+{
+ myname = argv[0];
+ init();
+
+ ifile = mfile = ofile = NULL;
+
+ /* Get input format options */
+ ft = &informat;
+ doopts(argc, argv);
+ /* Get input file */
+ if (optind >= argc)
+ usage("No input file?");
+
+ ifile = argv[optind];
+ if (! strcmp(ifile, "-"))
+ ft->fp = stdin;
+ else if ((ft->fp = fopen(ifile, READBINARY)) == NULL)
+ st_fail("Can't open input file '%s': %s",
+ ifile, strerror(errno));
+ ft->filename = ifile;
+ optind++;
+
+ /* Get mix format options */
+ ft = &mixformat;
+ doopts(argc, argv);
+ /* Get mix file */
+ if (optind >= argc)
+ usage("No mix file?");
+
+ mfile = argv[optind];
+ if (! strcmp(mfile, "-"))
+ ft->fp = stdin;
+ else if ((ft->fp = fopen(mfile, READBINARY)) == NULL)
+ st_fail("Can't open mix file '%s': %s",
+ mfile, strerror(errno));
+ ft->filename = mfile;
+ optind++;
+
+ /* Get output format options */
+ ft = &outformat;
+ doopts(argc, argv);
+ writing = 1;
+ if (writing) {
+ /* Get output file */
+ if (optind >= argc)
+ usage("No output file?");
+ ofile = argv[optind];
+ ft->filename = ofile;
+ /*
+ * There are two choices here:
+ * 1) stomp the old file - normal shell "> file" behavior
+ * 2) fail if the old file already exists - csh mode
+ */
+ if (! strcmp(ofile, "-"))
+ {
+ ft->fp = stdout;
+
+ /* stdout tends to be line-buffered. Override this */
+ /* to be Full Buffering. */
+ if (setvbuf (ft->fp,NULL,_IOFBF,sizeof(char)*BUFSIZ))
+ st_fail("Can't set write buffer");
+ }
+ else {
+
+ ft->fp = fopen(ofile, WRITEBINARY);
+
+ if (ft->fp == NULL)
+ st_fail("Can't open output file '%s': %s",
+ ofile, strerror(errno));
+
+ /* stdout tends to be line-buffered. Override this */
+ /* to be Full Buffering. */
+ if (setvbuf (ft->fp,NULL,_IOFBF,sizeof(char)*BUFSIZ))
+ st_fail("Can't set write buffer");
+
+ } /* end of else != stdout */
+
+ /* Move past filename */
+ optind++;
+ } /* end if writing */
+
+ /* Check global arguments */
+ if (volume <= 0.0)
+ st_fail("Volume must be greater than 0.0");
+
+#if defined(DUMB_FILESYSETM)
+ informat.seekable = 0;
+ mixformat.seekable = 0;
+ outformat.seekable = 0;
+#else
+ informat.seekable = (filetype(fileno(informat.fp)) == S_IFREG);
+ mixformat.seekable = (filetype(fileno(mixformat.fp)) == S_IFREG);
+ outformat.seekable = (filetype(fileno(outformat.fp)) == S_IFREG);
+#endif
+
+ /* If file types have not been set with -t, set from file names. */
+ if (! informat.filetype) {
+ if ((informat.filetype = strrchr(ifile, LASTCHAR)) != NULL)
+ informat.filetype++;
+ else
+ informat.filetype = ifile;
+ if ((informat.filetype = strrchr(informat.filetype, '.')) != NULL)
+ informat.filetype++;
+ else /* Default to "auto" */
+ informat.filetype = "auto";
+ }
+ if (! mixformat.filetype) {
+ if ((mixformat.filetype = strrchr(mfile, LASTCHAR)) != NULL)
+ mixformat.filetype++;
+ else
+ mixformat.filetype = mfile;
+ if ((mixformat.filetype = strrchr(mixformat.filetype, '.')) != NULL)
+ mixformat.filetype++;
+ else /* Default to "auto" */
+ mixformat.filetype = "auto";
+ }
+ if (writing && ! outformat.filetype) {
+ if ((outformat.filetype = strrchr(ofile, LASTCHAR)) != NULL)
+ outformat.filetype++;
+ else
+ outformat.filetype = ofile;
+ if ((outformat.filetype = strrchr(outformat.filetype, '.')) != NULL)
+ outformat.filetype++;
+ }
+ /* Default the input and mix comments to filenames.
+ * The output comment will be assigned when the informat
+ * structure is copied to the outformat.
+ */
+ informat.comment = informat.filename;
+ mixformat.comment = mixformat.filename;
+
+ process();
+ statistics();
+ return(0);
+}
+
+#ifdef HAVE_GETOPT_H
+char *getoptstr = "+r:v:t:c:phsuUAagbwlfdDxV";
+#else
+char *getoptstr = "r:v:t:c:phsuUAagbwlfdDxV";
+#endif
+
+void doopts(int argc, char **argv)
+{
+ int c;
+ char *str;
+
+ while ((c = getopt(argc, argv, getoptstr)) != -1) {
+ switch(c) {
+ case 'h':
+ usage((char *)0);
+ /* no return from above */
+
+ case 't':
+ if (! ft) usage("-t");
+ ft->filetype = optarg;
+ if (ft->filetype[0] == '.')
+ ft->filetype++;
+ break;
+
+ case 'r':
+ if (! ft) usage("-r");
+ str = optarg;
+#ifdef __alpha__
+ if ((! sscanf(str, "%u", &ft->info.rate)) ||
+ (ft->info.rate <= 0))
+#else
+ if ((! sscanf(str, "%lu", &ft->info.rate)) ||
+ (ft->info.rate <= 0))
+#endif
+ st_fail("-r must be given a positive integer");
+ break;
+ case 'v':
+ if (! ft) usage("-v");
+ str = optarg;
+ if ((! sscanf(str, "%lf", &volume)) ||
+ (volume <= 0))
+ st_fail("Volume value '%s' is not a number",
+ optarg);
+ dovolume = 1;
+ break;
+
+ case 'c':
+ if (! ft) usage("-c");
+ str = optarg;
+ if (! sscanf(str, "%d", &ft->info.channels))
+ st_fail("-c must be given a number");
+ break;
+ case 'b':
+ if (! ft) usage("-b");
+ ft->info.size = ST_SIZE_BYTE;
+ break;
+ case 'w':
+ if (! ft) usage("-w");
+ ft->info.size = ST_SIZE_WORD;
+ break;
+ case 'l':
+ if (! ft) usage("-l");
+ ft->info.size = ST_SIZE_DWORD;
+ break;
+ case 'f':
+ if (! ft) usage("-f");
+ ft->info.size = ST_SIZE_FLOAT;
+ break;
+ case 'd':
+ if (! ft) usage("-d");
+ ft->info.size = ST_SIZE_DOUBLE;
+ break;
+ case 'D':
+ if (! ft) usage("-D");
+ ft->info.size = ST_SIZE_IEEE;
+ break;
+
+ case 's':
+ if (! ft) usage("-s");
+ ft->info.encoding = ST_ENCODING_SIGN2;
+ break;
+ case 'u':
+ if (! ft) usage("-u");
+ ft->info.encoding = ST_ENCODING_UNSIGNED;
+ break;
+ case 'U':
+ if (! ft) usage("-U");
+ ft->info.encoding = ST_ENCODING_ULAW;
+ break;
+ case 'A':
+ if (! ft) usage("-A");
+ ft->info.encoding = ST_ENCODING_ALAW;
+ break;
+ case 'a':
+ if (! ft) usage("-a");
+ ft->info.encoding = ST_ENCODING_ADPCM;
+ break;
+ case 'g':
+ if (! ft) usage("-g");
+ ft->info.encoding = ST_ENCODING_GSM;
+ break;
+
+ case 'x':
+ if (! ft) usage("-x");
+ ft->swap = 1;
+ break;
+
+ case 'V':
+ verbose = 1;
+ break;
+ }
+ }
+}
+
+void init() {
+
+ /* init files */
+ informat.info.rate = mixformat.info.rate = outformat.info.rate = 0;
+ informat.info.size = mixformat.info.size = outformat.info.size = -1;
+ informat.info.encoding = mixformat.info.encoding = outformat.info.encoding = -1;
+ informat.info.channels = mixformat.info.channels = outformat.info.channels = -1;
+ informat.comment = mixformat.comment = outformat.comment = NULL;
+ informat.swap = mixformat.swap = 0;
+ informat.filetype = mixformat.filetype = outformat.filetype = (char *) 0;
+ informat.fp = stdin;
+ mixformat.fp = NULL;
+ outformat.fp = stdout;
+ informat.filename = "input";
+ mixformat.filename = "mix";
+ outformat.filename = "output";
+}
+
+/*
+ * Process input file -> mix with mixfile -> output file
+ * one buffer at a time
+ */
+
+void process() {
+ LONG result, i, *ibuf, *mbuf, *obuf, ilen=0, mlen=0, olen=0;
+
+ st_gettype(&informat);
+ st_gettype(&mixformat);
+ if (writing)
+ st_gettype(&outformat);
+
+ /* Read and write starters can change their formats. */
+ (* informat.h->startread)(&informat);
+ st_checkformat(&informat);
+
+ (* mixformat.h->startread)(&mixformat);
+ st_checkformat(&mixformat);
+
+ if (dovolume)
+ st_report("Volume factor: %f\n", volume);
+
+ st_report("Input file: using sample rate %lu\n\tsize %s, encoding %s, %d %s",
+ informat.info.rate, st_sizes_str[informat.info.size],
+ st_encodings_str[informat.info.encoding], informat.info.channels,
+ (informat.info.channels > 1) ? "channels" : "channel");
+ if (informat.comment)
+ st_report("Input file: comment \"%s\"\n", informat.comment);
+
+ st_report("Mix file: using sample rate %lu\n\tsize %s, encoding %s, %d %s",
+ mixformat.info.rate, st_sizes_str[mixformat.info.size],
+ st_encodings_str[mixformat.info.encoding], mixformat.info.channels,
+ (mixformat.info.channels > 1) ? "channels" : "channel");
+ if (mixformat.comment)
+ st_report("Mix file: comment \"%s\"\n", mixformat.comment);
+
+/*
+ Expect the formats of the input and mix files to be compatible.
+ Although it's true that I could fix it up on the fly, it's easier
+ for me to tell the user to use sox to fix it first and it's also
+ more reliable; better to use the code that Chris, Lance & Co have
+ written well than for me to rewrite the same stuff badly!
+
+ Sample rates are a cause for failure.
+*/
+ if(informat.info.rate != mixformat.info.rate)
+ st_fail("fail: Input and mix files have different sample rates.\nUse sox to resample one of them.\n");
+
+ /* need to check EFF_REPORT */
+ if (writing) {
+ st_copyformat(&informat, &outformat);
+ (* outformat.h->startwrite)(&outformat);
+ st_checkformat(&outformat);
+ st_report("Output file: using sample rate %lu\n\tsize %s, encoding %s, %d %s",
+ outformat.info.rate, st_sizes_str[outformat.info.size],
+ st_encodings_str[outformat.info.encoding], outformat.info.channels,
+ (outformat.info.channels > 1) ? "channels" : "channel");
+ if (outformat.comment)
+ st_report("Output file: comment \"%s\"\n", outformat.comment);
+ }
+
+ /* Allocate buffers */
+ ibuf = (LONG *) malloc(BUFSIZ * sizeof(LONG));
+ mbuf = (LONG *) malloc(BUFSIZ * sizeof(LONG));
+ obuf = (LONG *) malloc(BUFSIZ * sizeof(LONG));
+ if((!ibuf) || (!mbuf) || (!obuf))
+ {
+ fprintf(stderr, "Can't allocate memory for buffer (%s)\n",
+ strerror(errno));
+ return;
+ }
+
+ /* Read initial chunks of input data. */
+ /* Do the input file first */
+ ilen = (*informat.h->read)(&informat, ibuf, (LONG) BUFSIZ);
+ /* Change the volume of this data if needed. */
+ if(dovolume && ilen)
+ for (i = 0; i < ilen; i++)
+ ibuf[i] = volumechange(ibuf[i]);
+
+ /* Now do the mixfile */
+ mlen = (*mixformat.h->read)(&mixformat, mbuf, (LONG) BUFSIZ);
+ /* Change the volume of this data if needed. */
+ if(dovolume && mlen)
+ for (i = 0; i < mlen; i++)
+ mbuf[i] = volumechange(mbuf[i]);
+
+ /* mix until both input files are done */
+ while (ilen || mlen) {
+ /* Do the mixing from ibuf and mbuf into obuf. */
+ olen = (ilen > mlen) ? ilen : mlen;
+ for (i=0; i<olen; i++)
+ {
+ /* initial crude mix that might lose a bit of accuracy */
+ result = (i<ilen) ? (ibuf[i]/2) : 0;
+ if (i<mlen) result += mbuf[i]/2;
+ obuf[i] = result;
+ }
+
+ if (writing && olen)
+ (* outformat.h->write)(&outformat, obuf, (LONG) olen);
+
+ /* Read another chunk of input data. */
+ ilen = (*informat.h->read)(&informat,
+ ibuf, (LONG) BUFSIZ);
+
+ /* Change volume of these samples if needed. */
+ if(dovolume && ilen)
+ for (i = 0; i < ilen; i++)
+ ibuf[i] = volumechange(ibuf[i]);
+
+ /* Read another chunk of mix data. */
+ mlen = (*mixformat.h->read)(&mixformat,
+ mbuf, (LONG) BUFSIZ);
+
+ /* Change volume of these samples if needed. */
+ if(dovolume && mlen)
+ for (i = 0; i < mlen; i++)
+ mbuf[i] = volumechange(mbuf[i]);
+ }
+
+ (* informat.h->stopread)(&informat);
+ fclose(informat.fp);
+
+ (* mixformat.h->stopread)(&mixformat);
+ fclose(mixformat.fp);
+
+ if (writing)
+ (* outformat.h->stopwrite)(&outformat);
+ if (writing)
+ fclose(outformat.fp);
+}
+
+/* Guido Van Rossum fix */
+void statistics() {
+ if (dovolume && clipped > 0)
+ st_report("Volume change clipped %d samples", clipped);
+}
+
+LONG volumechange(y)
+LONG y;
+{
+ double y1;
+
+ y1 = y * volume;
+ if (y1 < -2147483647.0) {
+ y1 = -2147483647.0;
+ clipped++;
+ }
+ else if (y1 > 2147483647.0) {
+ y1 = 2147483647.0;
+ clipped++;
+ }
+
+ return y1;
+}
+
+int filetype(fd)
+int fd;
+{
+ struct stat st;
+
+ fstat(fd, &st);
+
+ return st.st_mode & S_IFMT;
+}
+
+char *usagestr =
+"[ gopts ] [ fopts ] ifile [ fopts ] mixfile [ fopts ] ofile";
+
+void usage(opt)
+char *opt;
+{
+ int i;
+
+ fprintf(stderr, "%s: ", myname);
+ if (verbose || !opt)
+ fprintf(stderr, "%s\n\n", st_version());
+ fprintf(stderr, "Usage: %s\n\n", usagestr);
+ if (opt)
+ fprintf(stderr, "Failed at: %s\n", opt);
+ else {
+ fprintf(stderr,"gopts: -e -h -p -v volume -V\n\n");
+ fprintf(stderr,"fopts: -r rate -c channels -s/-u/-U/-A/-a/-g -b/-w/-l/-f/-d/-D -x\n\n");
+ fprintf(stderr, "Supported file s: ");
+ for (i = 0; st_formats[i].names != NULL; i++) {
+ /* only print the first name */
+ fprintf(stderr, "%s ", st_formats[i].names[0]);
+ }
+ fputc('\n', stderr);
+ }
+ exit(1);
+}
+
+
+/* called from util.c:fail */
+void cleanup() {
+ /* Close the input file and outputfile before exiting*/
+ if (informat.fp)
+ fclose(informat.fp);
+ if (mixformat.fp)
+ fclose(mixformat.fp);
+ if (outformat.fp) {
+ fclose(outformat.fp);
+ /* remove the output file because we failed, if it's ours. */
+ /* Don't if its not a regular file. */
+ if (filetype(fileno(outformat.fp)) == S_IFREG)
+ REMOVE(outformat.filename);
+ }
+}
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -43,14 +43,14 @@
# Objects.
FOBJ = 8svx.o adpcm.o aiff.o au.o auto.o avr.o cdr.o cvsd.o dat.o g721.o \
- g723_24.o g723_40.o g72x.o gsm.o hcom.o ima_rw.o maud.o raw.o \
- sf.o smp.o sndrtool.o sphere.o tx16w.o voc.o wav.o wve.o
+ g723_24.o g723_40.o g72x.o gsm.o hcom.o ima_rw.o maud.o nul.o \
+ raw.o sf.o smp.o sndrtool.o sphere.o tx16w.o voc.o wav.o wve.o
EOBJ = avg.o band.o bandpass.o breject.o btrworth.o chorus.o compand.o \
copy.o cut.o deemphas.o earwax.o echo.o echos.o fade.o filter.o \
flanger.o highp.o highpass.o lowp.o lowpass.o map.o mask.o pan.o \
phaser.o pick.o pitch.o polyphas.o rate.o resample.o reverb.o \
- reverse.o speed.o split.o stat.o stretch.o swap.o trim.o \
+ reverse.o speed.o split.o stat.o stretch.o swap.o synth.o trim.o \
vibro.o vol.o
OSSOBJ_0 =
@@ -68,10 +68,13 @@
PLAY_0 =
PLAY_1 = play
-all: sox $(PLAY_$(PLAY_SUPPORT))
+all: sox mix $(PLAY_$(PLAY_SUPPORT))
sox: libst.a sox.o
$(CC) $(LDFLAGS) -o sox sox.o $(LIBS)
+
+mix: libst.a mix.o
+ $(CC) $(LDFLAGS) -o mix mix.o $(LIBS)
play: play.in
$(SED) -e 's|@PREFIX@|$(BINDIR)|g' < play.in > play
--- a/src/handlers.c
+++ b/src/handlers.c
@@ -161,6 +161,18 @@
extern int st_maudstartwrite();
extern int st_maudstopwrite();
+static char *nulnames[] = {
+ "nul",
+ (char *) 0,
+};
+
+extern int st_nulstartread();
+extern LONG st_nulread();
+extern int st_nulstopread();
+extern LONG st_nulwrite();
+extern int st_nulstartwrite();
+extern int st_nulstopwrite();
+
#if defined(OSS_PLAYER)
static char *ossdspnames[] = {
"ossdsp",
@@ -394,6 +406,9 @@
{maudnames, ST_FILE_STEREO, /* Amiga MAUD */
st_maudstartread, st_maudread, st_maudstopread,
st_maudstartwrite, st_maudwrite, st_maudstopwrite, st_nothing},
+ {nulnames, ST_FILE_STEREO, /* NUL */
+ st_nulstartread, st_nulread, st_nulstopread,
+ st_nulstartwrite, st_nulwrite, st_nulstopwrite},
#if defined(OSS_PLAYER)
{ossdspnames, ST_FILE_STEREO, /* OSS /dev/dsp player */
st_ossdspstartread, st_rawread, st_rawstopread,
@@ -641,6 +656,12 @@
extern int st_swap_drain();
extern int st_swap_stop();
+extern int st_synth_getopts();
+extern int st_synth_start();
+extern int st_synth_flow();
+extern int st_synth_drain();
+extern int st_synth_stop();
+
extern int st_vibro_getopts();
extern int st_vibro_start();
extern int st_vibro_flow();
@@ -772,6 +793,9 @@
{"swap", ST_EFF_MCHAN,
st_swap_getopts, st_swap_start, st_swap_flow,
st_swap_drain, st_swap_stop},
+ {"synth", ST_EFF_MCHAN,
+ st_synth_getopts, st_synth_start, st_synth_flow,
+ st_synth_drain, st_synth_stop},
{"vibro", 0,
st_vibro_getopts, st_vibro_start, st_vibro_flow,
st_null_drain, st_nothing},
--- a/src/lowp.c
+++ b/src/lowp.c
@@ -98,7 +98,7 @@
d = -2147483647L;
else if (d > 2147483647L)
d = 2147483647L;
- lowp->outm1 = l;
+ lowp->outm1 = d;
*obuf++ = d;
}
*isamp = len;
--- /dev/null
+++ b/src/synth.c
@@ -1,0 +1,788 @@
+/*
+ * synth - Synthesizer Effect.
+ *
+ * Written by Carsten Borchardt Jan 2001
+ * Version 0.1
+ *
+ * This source code is freely redistributable and may be used for
+ * any purpose. This copyright notice must be maintained.
+ * The authors are not responsible for
+ * the consequences of using this software.
+ */
+
+#include <signal.h>
+#include <string.h>
+#include <limits.h>
+#include <math.h>
+#include <ctype.h>
+#include "st.h"
+
+#define USSTR ""\
+"Usage:synth [length] type mix [freq[-freq2]] [off] [ph] [p1] [p2] [p3]\n"\
+" <length> length in sec or hh:mm:ss.frac, 0=inputlength, default=0\n"\
+" <type> is sine, square, triangle, sawtooth, trapetz, exp,\n"\
+" whitenoise, pinknoise, brownnoise, default=sine\n"\
+" <mix> is create, mix, amod, default=create\n"\
+" <freq> frequency at beginning in Hz, not used for noise..\n"\
+" <freq2> frequency at end in Hz, not used for noise..\n"\
+" <freq/2> can be given as %%n, where 'n' is the number of\n"\
+" half notes in respect to A (440Hz)\n"\
+" <off> Bias (DC-offset) of signal in percent, default=0\n"\
+" <ph> phase shift 0..100 shift phase 0..2*Pi, not used for noise..\n"\
+" <p1> square: Ton/Toff, triangle+trapetz: rising slope time (0..100)\n"\
+" <p2> trapetz: ON time (0..100)\n"\
+" <p3> trapetz: falling slope position (0..100)"
+
+#define PCOUNT 5
+
+#define SYNTH_SINE 0
+#define SYNTH_SQUARE 1
+#define SYNTH_SAWTOOTH 2
+#define SYNTH_TRIANGLE 3
+#define SYNTH_TRAPETZ 4
+#define SYNTH_WHITENOISE 5
+#define SYNTH_PINKNOISE 6
+#define SYNTH_BROWNNOISE 7
+#define SYNTH_VOICENOISE 8
+#define SYNTH_EXP 9
+
+#define SYNTH_CREATE 0x000
+#define SYNTH_MIX 0x100
+#define SYNTH_AMOD 0x200
+#define SYNTH_FMOD 0x400
+/* do not ask me for the colored noise, i copied the
+ * algorithm somewhere...
+ */
+#define BROWNNOISE_FAC (500.0/32768.0)
+#define PINKNOISE_FAC (5000.0/32768.0)
+#define LOG_10_20 0.1151292546497022842009e0
+
+/*#define TIMERES 1000*/
+#define MAXCHAN 4
+
+
+/******************************************************************************
+ * start of pink noise generator stuff
+ * algorithm stolen from:
+ * Author: Phil Burk, http://www.softsynth.com
+ */
+
+
+/* Calculate pseudo-random 32 bit number based on linear congruential method. */
+static unsigned long GenerateRandomNumber( void )
+{
+ static unsigned long randSeed = 22222; /* Change this for different random sequences. */
+ randSeed = (randSeed * 196314165) + 907633515;
+ return randSeed;
+}
+
+#define PINK_MAX_RANDOM_ROWS (30)
+#define PINK_RANDOM_BITS (24)
+#define PINK_RANDOM_SHIFT ((sizeof(long)*8)-PINK_RANDOM_BITS)
+
+typedef struct{
+ long pink_Rows[PINK_MAX_RANDOM_ROWS];
+ long pink_RunningSum; /* Used to optimize summing of generators. */
+ int pink_Index; /* Incremented each sample. */
+ int pink_IndexMask; /* Index wrapped by ANDing with this mask. */
+ float pink_Scalar; /* Used to scale within range of -1.0 to +1.0 */
+} PinkNoise;
+
+/* Setup PinkNoise structure for N rows of generators. */
+void InitializePinkNoise( PinkNoise *pink, int numRows )
+{
+ int i;
+ long pmax;
+ pink->pink_Index = 0;
+ pink->pink_IndexMask = (1<<numRows) - 1;
+/* Calculate maximum possible signed random value. Extra 1 for white noise always added. */
+ pmax = (numRows + 1) * (1<<(PINK_RANDOM_BITS-1));
+ pink->pink_Scalar = 1.0f / pmax;
+/* Initialize rows. */
+ for( i=0; i<numRows; i++ ) pink->pink_Rows[i] = 0;
+ pink->pink_RunningSum = 0;
+}
+
+/* Generate Pink noise values between -1.0 and +1.0 */
+float GeneratePinkNoise( PinkNoise *pink )
+{
+ long newRandom;
+ long sum;
+ float output;
+
+/* Increment and mask index. */
+ pink->pink_Index = (pink->pink_Index + 1) & pink->pink_IndexMask;
+
+/* If index is zero, don't update any random values. */
+ if( pink->pink_Index != 0 )
+ {
+ /* Determine how many trailing zeros in PinkIndex. */
+ /* This algorithm will hang if n==0 so test first. */
+ int numZeros = 0;
+ int n = pink->pink_Index;
+ while( (n & 1) == 0 )
+ {
+ n = n >> 1;
+ numZeros++;
+ }
+
+ /* Replace the indexed ROWS random value.
+ * Subtract and add back to RunningSum instead of adding all the random
+ * values together. Only one changes each time.
+ */
+ pink->pink_RunningSum -= pink->pink_Rows[numZeros];
+ newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT;
+ pink->pink_RunningSum += newRandom;
+ pink->pink_Rows[numZeros] = newRandom;
+ }
+
+/* Add extra white noise value. */
+ newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT;
+ sum = pink->pink_RunningSum + newRandom;
+
+/* Scale to range of -1.0 to 0.9999. */
+ output = pink->pink_Scalar * sum;
+
+ return output;
+}
+
+/**************** end of pink noise stuff */
+
+
+
+/* Private data for the synthesizer */
+typedef struct synthstuff {
+ /* options */
+ double time; /* length in sec */
+ int type[MAXCHAN];
+ int mix[MAXCHAN];
+ double freq[MAXCHAN];
+ double freq2[MAXCHAN];
+ double par[MAXCHAN][5];
+
+ /* internal stuff */
+ LONG max;
+ LONG samples_done;
+ int rate;
+ LONG length; /* length in number of samples */
+ double h[MAXCHAN]; /* store values necessary for creation */
+ PinkNoise pinkn[MAXCHAN];
+} *synth_t;
+
+
+/* a note is given as an int,
+ * 0 => 440 Hz = A
+ * >0 => number of half notes 'up',
+ * <0 => number of half notes down,
+ * example 12 => A of next octave, 880Hz
+ *
+ * calculated by freq = 440Hz * 2**(note/12)
+ */
+static double calc_note_freq(double note){
+ return (440.0 * pow(2,note/12.0));
+}
+
+
+/* read string 's' and convert to frequency
+ * 's' can be a positive number which is the frequency in Hz
+ * if 's' starts with a hash '%' and a following number the corresponding
+ * note is calculated
+ * return -1 on error
+ */
+static double StringToFreq(char *s, char **h){
+ double f;
+
+ if(*s=='%'){
+ f = strtod(s+1,h);
+ if ( *h == s+1 ){
+ /* error*/
+ return -1.0;
+ }
+ f=calc_note_freq(f);
+ }else{
+ f=strtod(s,h);
+ if(*h==s){
+ return -1.0;
+ }
+ }
+ if( f < 0.0 )
+ return -1.0;
+ return f;
+}
+
+
+
+static void parmcopy(synth_t sy, int s, int d){
+ int i;
+ sy->freq[d]=sy->freq[s];
+ sy->freq2[d]=sy->freq2[s];
+ sy->type[d]=sy->type[s];
+ sy->mix[d]=sy->mix[s];
+ for(i=0;i<PCOUNT;i++){
+ sy->par[d][i]=sy->par[s][i];
+ }
+}
+
+
+/*
+ * Process options
+ *
+ * Don't do initialization now.
+ * The 'info' fields are not yet filled in.
+ */
+int st_synth_getopts(effp, n, argv)
+eff_t effp;
+int n;
+char **argv;
+{
+ int argn;
+ char *usstr=USSTR;
+ char *hlp;
+ int i;
+ int c;
+ synth_t synth = (synth_t) effp->priv;
+
+
+ /* set default parameters */
+ synth->length = 0; /* use length of input file */
+ synth->time = 0.0;
+ synth->max = LONG_MAX;
+ for(c=0;c<MAXCHAN;c++){
+ synth->freq[c] = 440.0;
+ synth->freq2[c] = 440.0;
+ synth->type[c]=SYNTH_SINE;
+ synth->mix[c] = SYNTH_CREATE;
+
+ for(i=0;i<PCOUNT;i++)
+ synth->par[c][i]= -1.0;
+
+ synth->par[c][0]= 0.0; /* offset */
+ synth->par[c][1]= 0.0; /* phase */;
+ }
+
+ argn=0;
+ if ( n<0){
+ st_fail(usstr);
+ return(ST_EOF);
+ }
+ if(n==0){
+ /* no arg, use default*/
+ return(ST_SUCCESS);
+ }
+
+
+ /* read length if given ( if first par starts with digit )*/
+ if( isdigit((int)argv[argn][0])) {
+ synth->time = st_parsetime(argv[argn]);
+ if (synth->time < 0.0){
+ st_warn("synth: illegal time");
+ st_fail(usstr);
+ return(ST_EOF);
+ }
+ argn++;
+ }
+ /* for one or more channel */
+ /* type [mix] [f1[-f2]] [p0] [p1] [p2] [p3] [p4] */
+ for(c=0;c<MAXCHAN;c++){
+ if(n > argn){
+ /* next par must be type */
+ if( strcasecmp(argv[argn],"sine")==0){
+ synth->type[c]=SYNTH_SINE;
+ argn++; /* 1 */
+ }else if( strcasecmp(argv[argn],"square")==0){
+ synth->type[c]=SYNTH_SQUARE;
+ argn++; /* 1 */
+ }else if( strcasecmp(argv[argn],"sawtooth")==0){
+ synth->type[c]=SYNTH_SAWTOOTH;
+ argn++; /* 1 */
+ }else if( strcasecmp(argv[argn],"triangle")==0){
+ synth->type[c]=SYNTH_TRIANGLE;
+ argn++; /* 1 */
+ }else if( strcasecmp(argv[argn],"exp")==0){
+ synth->type[c]=SYNTH_EXP;
+ argn++; /* 1 */
+ }else if( strcasecmp(argv[argn],"trapetz")==0){
+ synth->type[c]=SYNTH_TRAPETZ;
+ argn++;
+ }else if( strcasecmp(argv[argn],"whitenoise")==0){
+ synth->type[c]=SYNTH_WHITENOISE;
+ argn++; /* 1 */
+ }else if( strcasecmp(argv[argn],"noise")==0){
+ synth->type[c]=SYNTH_WHITENOISE;
+ argn++; /* 1 */
+ }else if( strcasecmp(argv[argn],"pinknoise")==0){
+ synth->type[c]=SYNTH_PINKNOISE;
+ argn++; /* 1 */
+ }else if( strcasecmp(argv[argn],"brownnoise")==0){
+ synth->type[c]=SYNTH_BROWNNOISE;
+ argn++; /* 1 */
+ }else if( strcasecmp(argv[argn],"voicenoise")==0){
+ synth->type[c]=SYNTH_VOICENOISE;
+ argn++; /* 1 */
+ }else{
+ /* type not given, error */
+ st_warn("synth: no type given");
+ st_fail(usstr);
+ return(ST_EOF);
+ }
+ if(n > argn){
+ /* maybe there is a mix-type in next arg */
+ if(strcasecmp(argv[argn],"create")==0){
+ synth->mix[c]=SYNTH_CREATE;
+ argn++;
+ }else if(strcasecmp(argv[argn],"mix")==0){
+ synth->mix[c]=SYNTH_MIX;
+ argn++;
+ }else if(strcasecmp(argv[argn],"amod")==0){
+ synth->mix[c]=SYNTH_AMOD;
+ argn++;
+ }else if(strcasecmp(argv[argn],"fmod")==0){
+ synth->mix[c]=SYNTH_FMOD;
+ argn++;
+ }
+ if(n > argn){
+ /* read frequency's if given */
+ synth->freq[c]= StringToFreq(argv[argn],&hlp);
+ synth->freq2[c] = synth->freq[c];
+ if(synth->freq[c] < 0.0){
+ st_warn("synth: illegal freq");
+ st_fail(usstr);
+ return(ST_EOF);
+ }
+ if(*hlp=='-') {
+ /* freq2 given ! */
+ char *hlp2;
+ synth->freq2[c]=StringToFreq(hlp+1,&hlp2);
+ if(synth->freq2[c] < 0.0){
+ st_warn("synth: illegal freq2");
+ st_fail(usstr);
+ return(ST_EOF);
+ }
+ }
+ argn++;
+ i=0;
+ /* read rest of parameters */
+ while(n > argn){
+ if( ! isdigit((int)argv[argn][0]) ){
+ /* not a digit, must be type of next channel */
+ break;
+ }
+ if( i >= PCOUNT) {
+ st_warn("synth: too many parameters");
+ st_fail(usstr);
+ return(ST_EOF);
+
+ }
+ synth->par[c][i]=strtod(argv[argn],&hlp);
+ if(hlp==argv[argn]){
+ /* error in number */
+ st_warn("synth: parameter error");
+ st_fail(usstr);
+ return(ST_EOF);
+ }
+ i++;
+ argn++;
+ }/* .. while */
+ if(n > argn){
+ /* got here by 'break', scan parms for next chan */
+ }else{
+
+ break;
+ }
+ }
+ }
+ }
+ }/* for .. */
+
+ /* make some intelligent parameter initialization for channels
+ * where no parameters were given
+ *
+ * - of only parms for one channel were given, copy to ther channels
+ * - if parm for 2 channels were given, copy to channel 1->3, 2->4
+ * - if parm for 3 channels were given, copy 2->4
+ */
+ if(c == 0){
+ for(c=1;c<MAXCHAN;c++)
+ parmcopy(synth,0,c);
+ }else if(c == 1){
+ parmcopy(synth,0,2);
+ parmcopy(synth,1,3);
+ }else if(c == 2){
+ parmcopy(synth,1,3);
+ }
+
+
+
+
+ return (ST_SUCCESS);
+}
+
+
+/*
+ * Prepare processing.
+ * Do all initializations.
+ */
+int st_synth_start(effp)
+eff_t effp;
+{
+ int i;
+ int c;
+ synth_t synth = (synth_t) effp->priv;
+
+ /* calc length in number of samples... */
+ synth->length = (double)effp->ininfo.rate * synth->time;
+
+ if (synth->length < 0){
+ st_fail("synth: start must be positive");
+ return(ST_EOF);
+ }
+
+ synth->samples_done=0;
+ synth->rate = effp->ininfo.rate;
+
+ for(i=0;i< MAXCHAN; i++){
+ synth->h[i]=0.0;
+ }
+
+
+
+
+ /* parameter adjustment for all channels */
+ for(c=0;c<MAXCHAN;c++){
+ /* adjust parameter 0 - 100% to 0..1 */
+ for(i=0;i<PCOUNT;i++){
+ synth->par[c][i] /= 100.0;
+ }
+
+
+
+ /* give parameters nice defaults for the different 'type' */
+
+ switch(synth->type[c]){
+ case SYNTH_SINE:
+ break;
+ case SYNTH_SQUARE:
+ /* p2 is pulse width */
+ if(synth->par[c][2] < 0.0){
+ synth->par[c][2] = 0.5; /* default to 50% duty cycle */
+ }
+ break;
+ case SYNTH_TRIANGLE:
+ /* p2 is position of maximum*/
+ if(synth->par[c][2] < 0.0){
+ /* default : 0 */
+ synth->par[c][2]=0.5;
+ }
+ break;
+ case SYNTH_SAWTOOTH:
+ /* no parameters, use TRIANGLE to create no-default-sawtooth */
+ break;
+ case SYNTH_TRAPETZ:
+ /* p2 is length of rising slope,
+ * p3 position where falling slope begins
+ * p4 position of end of falling slope
+ */
+ if(synth->par[c][2] < 0.0 ){
+ synth->par[c][2]= 0.1;
+ synth->par[c][3]= 0.5;
+ synth->par[c][4]= 0.6;
+ }else if(synth->par[c][3] < 0.0){
+ /* try a symetric waveform
+ */
+ if(synth->par[c][2] <= 0.5){
+ synth->par[c][3] = (1.0-2.0*synth->par[c][2])/2.0;
+ synth->par[c][4] = synth->par[c][3] + synth->par[c][2];
+ }else{
+ /* symetric is not possible, fall back to asymetrical
+ * triangle
+ */
+ synth->par[c][3]=synth->par[c][2];
+ synth->par[c][4]=1.0;
+ }
+ }else if(synth->par[c][4] < 0.0){
+ /* simple falling slope to the end */
+ synth->par[c][4]=1.0;
+ }
+ break;
+ case SYNTH_PINKNOISE:
+ /* Initialize pink noise signals with different numbers of rows. */
+ InitializePinkNoise( &(synth->pinkn[c]),10+2*c);
+ break;
+ default:
+ break;
+ }
+
+
+#if 0
+ st_warn("synth: type=%d, mix=%d, time=%lf, f1=%lf, f2=%lf",
+ synth->type[c], synth->mix[c],
+ synth->time, synth->freq[c], synth->freq2[c]);
+ st_warn("synth: p0=%f, p1=%f, p2=%f, p3=%f, p4=%f",synth->par[c][0],
+ synth->par[c][1],synth->par[c][2],synth->par[c][3],synth->par[c][4]);
+ st_warn("synth: inchan =%d, rate=%d",
+ (int)effp->ininfo.channels,synth->rate
+ );
+#endif
+ }
+ return (ST_SUCCESS);
+}
+
+
+
+static LONG do_synth(LONG iv, synth_t synth, int c){
+ LONG ov=iv;
+ double r=0.0; /* -1 .. +1 */
+ double f;
+ double om;
+ double sd;
+ double move;
+ double t,dt ;
+
+ if(synth->length<=0){
+ /* there is no way to change the freq. without knowing the length
+ * use startfreq all the time ...
+ */
+ f = synth->freq[c];
+ }else{
+ f = synth->freq[c] *
+ exp( (log(synth->freq2[c])-log(synth->freq[c]))*
+ synth->samples_done/synth->length );
+ }
+ om = 1.0 / f; /* periodendauer inn sec */
+ t = synth->samples_done / (double)synth->rate; /* zeit seit start in sec */
+ dt = t - synth->h[c]; /* seit seitdem letzte periode um war. */
+ if( dt < om){
+ /* wir sind noch in der periode.. */
+ }else{
+ /* schon in naechste periode */
+ synth->h[c]+=om;
+ dt=t-synth->h[c];
+ }
+ sd= dt/om; /* position in der aktuellen periode; 0<= sd < 1*/
+ sd = fmod(sd+synth->par[c][1],1.0); /* phase einbauen */
+
+
+// sd = fmod( (synth->samples_done - synth->h[c])/om + synth->par[c][1],1.0);
+ switch(synth->type[c]){
+ case SYNTH_SINE:
+ r = sin(2.0 * M_PI * sd);
+ break;
+ case SYNTH_SQUARE:
+ /* |_______ | +1
+ * | | |
+ * |_______|__________| 0
+ * | | |
+ * | |__________| -1
+ * | |
+ * 0 p2 1
+ */
+ if(sd < synth->par[c][2]){
+ r = -1.0;
+ }else{
+ r = +1.0;
+ }
+ break;
+ case SYNTH_SAWTOOTH:
+ /* | __| +1
+ * | __/ |
+ * |_______/_____| 0
+ * | __/ |
+ * |_/ | -1
+ * | |
+ * 0 1
+ */
+ r = -1.0 + 2.0 * sd;
+ break;
+ case SYNTH_TRIANGLE:
+ /* | _ | +1
+ * | / \ |
+ * |__/___\__| 0
+ * | / \ |
+ * |/ \| -1
+ * | |
+ * 0 p2 1
+ */
+
+ if( sd < synth->par[c][2]){ /* in rising Part of period */
+ r = -1.0 + 2.0 * sd / synth->par[c][2];
+ }else{ /* falling part */
+ r = 1.0 - 2.0 *
+ (sd-synth->par[c][2])/(1-synth->par[c][2]);
+ }
+ break;
+ case SYNTH_TRAPETZ:
+ /* | ______ |+1
+ * | / \ |
+ * |__/________\___________| 0
+ * | / \ |
+ * |/ \_________|-1
+ * | |
+ * 0 p2 p3 p4 1
+ */
+ if( sd < synth->par[c][2]){ /* in rising part of period */
+ r = -1.0 + 2.0 * sd / synth->par[c][2];
+ }else if( sd < synth->par[c][3]){ /* in constant Part of period */
+ r=1.0;
+ }else if( sd < synth->par[c][4] ){ /* falling part */
+ r = 1.0 - 2.0 *
+ (sd - synth->par[c][3])/(synth->par[c][4]-synth->par[c][3]);
+ }else{
+ r = -1.0;
+ }
+ break;
+
+ case SYNTH_EXP:
+ /* | | | +1
+ * | | | |
+ * | _| |_ | 0
+ * | __- -__ |
+ * |____--- ---____ | f(p3)
+ * | |
+ * 0 p2 1
+ */
+ move=exp( - synth->par[c][3] * LOG_10_20 * 100.0 ); /* 0 .. 1 */
+ if ( sd < synth->par[c][2] ) {
+ r = move * exp(sd * log(1.0/move)/synth->par[c][2]);
+ }else{
+ r = move *
+ exp( (1-sd)*log(1.0/move)/
+ (1.0-synth->par[c][2]));
+ }
+
+ /* r in 0 .. 1 */
+ r = r * 2.0 - 1.0; /* -1 .. +1 */
+ break;
+ case SYNTH_WHITENOISE:
+ r= 2.0* rand()/(double)RAND_MAX - 1.0;
+ break;
+ case SYNTH_PINKNOISE:
+ r = GeneratePinkNoise( &(synth->pinkn[c]) );
+ break;
+ case SYNTH_BROWNNOISE:
+ /* no idea if this algorithm is good enough.. */
+ move = 2.0* rand()/(double)RAND_MAX - 1.0;
+ move *= BROWNNOISE_FAC;
+ synth->h[c] += move;
+ if( fabs(synth->h[c]) > 1.0 ){
+ synth->h[c] -= 2.0*move;
+ }
+ r=synth->h[c];
+ break;
+ default:
+ st_warn("synth: internal error 1");
+ break;
+ }
+
+ /* add offset, but prevent clipping */
+ om = fabs(synth->par[c][0]);
+ if( om <= 1.0 ){
+ r *= 1.0 - om; /* reduce amp, prevent clipping */
+ r += om;
+ }
+
+
+ switch(synth->mix[c]){
+ case SYNTH_CREATE:
+ ov = synth->max * r;
+ break;
+ case SYNTH_MIX:
+ ov = iv/2 + r*synth->max/2;
+ break;
+ case SYNTH_AMOD:
+ ov = (LONG)(0.5*(r+1.0)*(double)iv);
+ break;
+ case SYNTH_FMOD:
+ ov = iv * r ;
+ break;
+ default:
+ st_fail("synth: internel error 2");
+ break;
+ }
+
+ return ov;
+}
+
+
+
+/*
+ * Processed signed long samples from ibuf to obuf.
+ */
+
+int st_synth_flow(effp, ibuf, obuf, isamp, osamp)
+eff_t effp;
+LONG *ibuf, *obuf;
+LONG *isamp, *osamp;
+{
+ synth_t synth = (synth_t) effp->priv;
+ int len; /* number of input samples */
+ int done;
+ int c;
+ int chan=effp->ininfo.channels;
+
+ if(chan > MAXCHAN ){
+ st_fail("synth: can not operate with more than %d channels",MAXCHAN);
+ return(ST_EOF);
+ }
+
+ len = ((*isamp > *osamp) ? *osamp : *isamp) / chan;
+
+ for(done = 0; done < len ; done++){
+ for(c=0;c<chan;c++){
+ /* each channel is independent, but the algorithm is the same */
+
+ obuf[c] = do_synth(ibuf[c],synth,c);
+ }
+ ibuf+=chan;
+ obuf+=chan;
+ synth->samples_done++;
+ if(synth->length > 0 ){
+ if( synth->samples_done > synth->length){
+ /* break 'nul' file fileter when enough samples
+ * are produced. the actual number of samples
+ * will be a little bigger, depends on when the
+ * signal gets to the plugin
+ */
+ raise(SIGINT); /* only once */
+ *osamp = done*chan;
+ break;
+
+ }
+ }
+
+ }
+ return (ST_SUCCESS);
+}
+
+/*
+ * Drain out remaining samples if the effect generates any.
+ */
+
+int st_synth_drain(effp, obuf, osamp)
+LONG *obuf;
+LONG *osamp;
+{
+ *osamp = 0;
+ return (ST_SUCCESS);
+}
+
+/*
+ * Do anything required when you stop reading samples.
+ * (free allocated memory, etc.)
+ */
+int st_synth_stop(effp)
+eff_t effp;
+{
+ /* nothing to do */
+ return (ST_SUCCESS);
+}
+/*-------------------------------------------------------------- end of file */
+
+
+
+
+
+
+
+
+
+
--- /dev/null
+++ b/src/testcd.sh
@@ -1,0 +1,237 @@
+#!/bin/sh
+# create WAV-Files that can be used for an audio Test CD
+# all files are created in the current directory
+#
+#
+# length of sample file in seconds
+
+if [ "$2" = "" ] ; then
+ LENGTH="30"
+else
+ LENGTH=$2
+fi
+# use 'fade' effect for smooth start and end of tone
+FT="0.05"
+
+# a different default volume
+VOL=""
+
+#output file type
+OFT=".wav"
+
+#our binary
+SOX=./sox
+
+# filenameprefix
+if [ "$1" = "" ] ; then
+ PRE="testcd"
+else
+ PRE=$1;
+fi
+# 2 channel 16 bit signed linear int with CD sampling rate
+SOXOPT="-t nul -c 2 -r 44100 -s -w - "
+
+# file with list of filenames
+LST="${PRE}.lst"
+
+
+#summarise seconds
+TC="0"
+#summarize file numbers
+FC="0"
+
+
+newname()
+{
+ FC="$(( $FC + 1 ))"
+ LEN="$2"
+ FADE=" fade $FT $LEN $FT"
+
+ TC="$(( $TC + $LEN ))"
+
+ if [ $FC -lt 10 ] ; then
+ NAME="${PRE}_0${FC}_${1}${OFT}"
+ else
+ NAME="${PRE}_${FC}_${1}${OFT}"
+ fi
+ echo -n -e " \t$1"
+ echo "$NAME" >>$LST
+}
+
+#empty / delete list file
+echo "" >$LST
+
+
+#
+# ok, lets start with the actual createion of the files
+#
+
+#
+# fixed frequencies,
+FREQ=" 19.4 23.1 27.5 32.7 38.9 46.2"
+FREQ="$FREQ 55.0 64.4 77.8 92.5 110.0 130.8 155.6 185.0"
+FREQ="$FREQ 220.0 261.6 311.1 367.0 440.0 523.3 622.3 740.0"
+FREQ="$FREQ 880.0 1046 1245 1480 1760 2093 2489 2960"
+FREQ="$FREQ 3520 4186 4978 5920 7040 8372 9956 11840"
+FREQ="$FREQ 14080 16744 19912"
+#FREQ="5 10 20 50 100 200 500 1000 2000 5000 10000 20000"
+echo -e "\n--- different frequencies"
+for f in $FREQ; do
+ newname "${f}hz" $LENGTH
+$SOX $SOXOPT $NAME synth $LEN sine $f $FADE $VOL
+done
+
+
+
+#
+# frequency sweep
+# need some mark every octave
+#
+FREQ="220-3520" #4
+FREQ="55-14080" # 8 oct
+FREQ="13.75-28160" # 10 oct
+OCT=10
+TOCT=10
+TGES="$(( $OCT * $TOCT ))"
+MARKFREQ=622
+echo -e "\n--- frequency sweep range $FREQ"
+newname ${FREQ}hz $TGES
+$SOX $SOXOPT $NAME synth $LEN sine $MARKFREQ synth square amod 0.1 0 97 94 vol -3 db synth $LEN sine mix $FREQ $VOL
+
+FREQ="3520-220"
+FREQ="28160-13.75" # 9 oct
+newname ${FREQ}hz $TGES
+$SOX $SOXOPT $NAME synth $LEN sine $MARKFREQ synth square amod 0.1 0 97 94 vol -3 db synth $LEN sine mix $FREQ $VOL
+
+# CD frequencies
+FREQ="22050 11025 5512.5 "
+echo -e "\n--- different frequencies $FREQ"
+for f in $FREQ; do
+ newname "cd${f}hz" $LENGTH
+$SOX $SOXOPT $NAME synth $LEN sine $f $FADE $VOL
+done
+
+
+#
+# similar frequencies
+#
+FREQ1="9000"
+FREQ2="10000"
+echo -e "\n--- similar frequencies"
+newname ${FREQ1}_${FREQ2} $LENGTH
+$SOX $SOXOPT $NAME synth $LEN sine $FREQ1 synth sine mix $FREQ2 $FADE $VOL
+FREQ1="440"
+FREQ2="445"
+newname ${FREQ1}_${FREQ2} $LENGTH
+$SOX $SOXOPT $NAME synth $LEN sine $FREQ1 synth sine mix $FREQ2 $FADE $VOL
+
+#
+#noise
+#
+echo -e "\n--- noise"
+newname whitenoise $LENGTH
+$SOX $SOXOPT $NAME synth $LEN whitenoise $FADE $VOL
+newname pinknoise $LENGTH
+$SOX $SOXOPT $NAME synth $LEN pinknoise $FADE $VOL
+newname brownnoise $LENGTH
+$SOX $SOXOPT $NAME synth $LEN brownnoise $FADE $VOL
+
+
+
+#
+# square waves
+#
+FREQ="100 1000 10000"
+echo -e "\n--- square waves at $FREQ"
+for f in $FREQ; do
+ newname ${f}_square $LENGTH
+$SOX $SOXOPT $NAME synth $LEN square $f vol -12 db $FADE
+done
+
+
+#
+# different volumes at a few frequencies
+#
+FREQ="100 1000 10000"
+DB="0 12 24 36 48 60 72 84 96"
+echo -e "\n--- different volumes $DB db at Frequencies $FREQ"
+ for f in $FREQ; do
+ for d in $DB; do
+ newname ${f}_${d}db $LENGTH
+ $SOX $SOXOPT $NAME synth $LEN sine $f vol -$d db $FADE
+ done
+done
+
+# silence
+echo -e "\n-- silence"
+newname silence $LENGTH
+$SOX $SOXOPT $NAME synth $LEN sine 1000 vol 0
+
+
+
+
+#
+# volume sweep at different frequencies
+#
+
+FREQ="100 1000 10000"
+MARKFREQ=662
+DB=100 # 10sec for 10db
+echo -e "\n--- volume sweep 0..100% at Frequencies $FREQ"
+for f in $FREQ; do
+ newname ${f}_dbsweep 200
+$SOX $SOXOPT $NAME synth $LEN sine $f synth exp amod 0.005 0 0 50 $DB
+done
+
+
+#
+# offset test - a 1K sine with 1Hz square offset of 10%
+#
+echo -e "\n--- offset test, 1KHz tone with 1HZ 10% offset"
+newname offset $LENGTH
+$SOX $SOXOPT $NAME synth $LEN square 1 vol 0.1 synth sine mix 1000 $FADE $VOL
+newname offset1 $LENGTH
+$SOX $SOXOPT $NAME synth $LEN square 1 0 0 square 1 vol 0.1 0 25 $FADE
+
+
+
+# effects for different channels
+#
+# silence on one channel, full power on the other - different frequencies
+FREQ="100 1000 10000"
+echo -e "\n--- single channel"
+for f in $FREQ; do
+ newname ${f}leftchan $LENGTH
+ $SOX $SOXOPT $NAME synth $LEN sine $f synth square amod 0 100 square amod 0 0 $Fade $VOL
+ newname ${f}rightchan $LENGTH
+ $SOX $SOXOPT $NAME synth $LEN sine $f synth square amod 0 0 square amod 0 100 $Fade $VOL
+done
+
+# phase error between channels
+FREQ="100 1000 10000"
+# equal phase/ 24 degrees / 90 degrees / 180 degrees
+PHASE="25"
+echo -e "\n--- phase error test between channels at $FREQ "
+for f in $FREQ; do
+ for p in $PHASE; do
+ newname ${f}hz_phase${p} $LENGTH
+ $SOX $SOXOPT $NAME synth $LEN sine $f 0 0 sine $f 0 $p $FADE $VOL
+ done
+done
+
+#
+#
+# end - show statistics
+#
+
+echo -e "\n------------------\ncreated $FC files with prefix $PRE type $OFT"
+MIN="$(( $TC / 60 ))"
+echo "total length is $TC sec = $MIN min"
+#---------
+
+
+
+
+
+
+
--- a/src/wav.c
+++ b/src/wav.c
@@ -1367,8 +1367,8 @@
#ifdef HAVE_LIBGSM
if (wChannels!=1)
{
- st_fail_errno(ft,ST_EOF,"Channels(%d) must be == 1\n",wChannels);
- return ST_EOF;
+ st_warn("Overriding GSM audio from %d channel to 1\n",wChannels);
+ wChannels = ft->info.channels = 1;
}
wFormatTag = WAVE_FORMAT_GSM610;
/* wAvgBytesPerSec = 1625*(wSamplesPerSecond/8000.)+0.5; */