ref: 2776fb46d9d949230e22ce5099da1db8e0e206c4
parent: fe1c1e06d0c343967b578b81f1bc1fe9f80946ba
author: robs <robs>
date: Sat Sep 27 07:33:00 EDT 2008
embryonic bend effect
--- a/ChangeLog
+++ b/ChangeLog
@@ -52,6 +52,7 @@
o New `riaa' effect: RIAA vinyl playback EQ. (robs)
o New `loudness' effect: gain control with ISO 226 loudness
compensation. (robs)
+ o New `bend' effect; pitch bending; W.I.P., subject to change. (robs)
o New -b option for the norm effect; can be used to fix stereo
imbalance. (robs)
o New --effects-file option to read effects and arguments from
--- a/sox.1
+++ b/sox.1
@@ -1119,6 +1119,14 @@
.SP
See also \fBequalizer\fR for a peaking equalisation effect.
.TP
+\fBbend\fR [\fB\-f \fIframe-rate\fR(25)] [\fB\-o \fIoversample\fR(16)] { \fIdelay\fB,\fIcents\fB,\fIduration\fR }
+Pitch bending. Work in progress\*msubject to change.
+.SP
+For example:
+.EX
+ play -n synth 2.5 sin 667 bend .35,180,.25 .15,740,.53 0,-520,.3
+.EE
+.TP
\fBchorus \fIgain-in gain-out\fR <\fIdelay decay speed depth \fB\-s\fR\^|\^\fB\-t\fR>
Add a chorus effect to the audio. This can make a single vocal sound
like a chorus, but can also be applied to instrumentation.
@@ -1586,7 +1594,7 @@
and one audio output port can be used. If found, the environment varible
LADSPA_PATH will be used as search path for plugins.
.TP
-\fBloudness [\fIgain\fR [\fIreference\fR]]
+\fBloudness\fR [\fIgain\fR [\fIreference\fR]]
Loudness control\*msimilar to the
.B gain
effect, but provides equalisation for the human auditory system. See
@@ -1598,6 +1606,9 @@
.I reference
level may be given if the original audio has been equalised for some
other optimal level.
+A default gain of \-10dB is used if a
+.I gain
+value is not given.
.SP
See also the
.B gain
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -28,16 +28,16 @@
# Format with: !xargs echo|tr ' ' '\n'|sort|column|expand|sed 's/^/ /'
set(effects_srcs
- biquad echo noiseprof remix swap
- biquads echos noisered repeat synth
- chorus fade normalise resample tempo
- compand fft4g output reverb tremolo
- compandt filter pad reverse trim
- contrast flanger pan silence vol
- dcshift input phaser skeleff
- delay loudness pitch speed
- dither mcompand polyphas splice
- earwax mixer rate stat
+ bend earwax mcompand polyphas splice
+ biquad echo mixer rate stat
+ biquads echos noiseprof remix swap
+ chorus fade noisered repeat synth
+ compand fft4g normalise resample tempo
+ compandt output reverb tremolo
+ contrast filter pad reverse trim
+ dcshift flanger pan silence vol
+ delay input phaser skeleff
+ dither loudness pitch speed
)
set(formats_srcs
8svx dat ima-fmt s3-fmt u4-fmt
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -249,7 +249,7 @@
############################
libsfx_la_SOURCES = \
- band.h biquad.c biquad.h biquads.c chorus.c compand.c compandt.c \
+ band.h bend.c biquad.c biquad.h biquads.c chorus.c compand.c compandt.c \
compandt.h contrast.c dcshift.c delay.c dither.c earwax.c echo.c \
echos.c effects.c effects.h effects_i.c fade.c fft4g.c fft4g.h \
fifo.h filter.c flanger.c input.c ladspa.c loudness.c mcompand.c \
--- /dev/null
+++ b/src/bend.c
@@ -1,0 +1,362 @@
+/* libSoX effect: Pitch Bend (c) 2008 robs@users.sourceforge.net
+ *
+ * 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.1 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,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* Portions based on http://www.dspdimension.com/download smbPitchShift.cpp:
+ *
+ * COPYRIGHT 1999-2006 Stephan M. Bernsee <smb [AT] dspdimension [DOT] com>
+ *
+ * The Wide Open License (WOL)
+ *
+ * Permission to use, copy, modify, distribute and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice and this license appear in all source copies.
+ * THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY OF
+ * ANY KIND. See http://www.dspguru.com/wol.htm for more information.
+ */
+
+#include "sox_i.h"
+#include "getopt.h"
+
+static void smbFft(float *fftBuffer, long fftFrameSize, long sign)
+/*
+ * FFT routine, (C)1996 S.M.Bernsee. Sign = -1 is FFT, 1 is iFFT (inverse)
+ * Fills fftBuffer[0...2*fftFrameSize-1] with the Fourier transform of the
+ * time domain data in fftBuffer[0...2*fftFrameSize-1]. The FFT array takes
+ * and returns the cosine and sine parts in an interleaved manner, ie.
+ * fftBuffer[0] = cosPart[0], fftBuffer[1] = sinPart[0], asf. fftFrameSize
+ * must be a power of 2. It expects a complex input signal (see footnote 2),
+ * ie. when working with 'common' audio signals our input signal has to be
+ * passed as {in[0],0.,in[1],0.,in[2],0.,...} asf. In that case, the transform
+ * of the frequencies of interest is in fftBuffer[0...fftFrameSize].
+ */
+{
+ float wr, wi, arg, *p1, *p2, temp;
+ float tr, ti, ur, ui, *p1r, *p1i, *p2r, *p2i;
+ long i, bitm, j, le, le2, k;
+
+ for (i = 2; i < 2 * fftFrameSize - 2; i += 2) {
+ for (bitm = 2, j = 0; bitm < 2 * fftFrameSize; bitm <<= 1) {
+ if (i & bitm)
+ j++;
+ j <<= 1;
+ }
+ if (i < j) {
+ p1 = fftBuffer + i;
+ p2 = fftBuffer + j;
+ temp = *p1;
+ *(p1++) = *p2;
+ *(p2++) = temp;
+ temp = *p1;
+ *p1 = *p2;
+ *p2 = temp;
+ }
+ }
+ for (k = 0, le = 2; k < (long) (log((double) fftFrameSize) / log(2.) + .5);
+ k++) {
+ le <<= 1;
+ le2 = le >> 1;
+ ur = 1.0;
+ ui = 0.0;
+ arg = M_PI / (le2 >> 1);
+ wr = cos(arg);
+ wi = sign * sin(arg);
+ for (j = 0; j < le2; j += 2) {
+ p1r = fftBuffer + j;
+ p1i = p1r + 1;
+ p2r = p1r + le2;
+ p2i = p2r + 1;
+ for (i = j; i < 2 * fftFrameSize; i += le) {
+ tr = *p2r * ur - *p2i * ui;
+ ti = *p2r * ui + *p2i * ur;
+ *p2r = *p1r - tr;
+ *p2i = *p1i - ti;
+ *p1r += tr;
+ *p1i += ti;
+ p1r += le;
+ p1i += le;
+ p2r += le;
+ p2i += le;
+ }
+ tr = ur * wr - ui * wi;
+ ui = ur * wi + ui * wr;
+ ur = tr;
+ }
+ }
+}
+
+#define MAX_FRAME_LENGTH 8192
+
+typedef struct {
+ unsigned nbends; /* Number of bends requested */
+ struct {
+ char *str; /* Command-line argument to parse for this bend */
+ size_t start; /* Start bending when in_pos equals this */
+ double cents;
+ size_t duration; /* Number of samples to bend */
+ } *bends;
+
+ unsigned frame_rate;
+ size_t in_pos; /* Number of samples read from the input stream */
+ unsigned bends_pos; /* Number of bends completed so far */
+
+ double shift;
+
+ float gInFIFO[MAX_FRAME_LENGTH];
+ float gOutFIFO[MAX_FRAME_LENGTH];
+ float gFFTworksp[2 * MAX_FRAME_LENGTH];
+ float gLastPhase[MAX_FRAME_LENGTH / 2 + 1];
+ float gSumPhase[MAX_FRAME_LENGTH / 2 + 1];
+ float gOutputAccum[2 * MAX_FRAME_LENGTH];
+ float gAnaFreq[MAX_FRAME_LENGTH];
+ float gAnaMagn[MAX_FRAME_LENGTH];
+ float gSynFreq[MAX_FRAME_LENGTH];
+ float gSynMagn[MAX_FRAME_LENGTH];
+ long gRover;
+ int fftFrameSize, ovsamp;
+} priv_t;
+
+static int parse(sox_effect_t * effp, char **argv, sox_rate_t rate)
+{
+ priv_t *p = (priv_t *) effp->priv;
+ size_t i, time = 0, delay;
+ char const *next;
+
+ for (i = 0; i < p->nbends; ++i) {
+ if (argv) /* 1st parse only */
+ p->bends[i].str = lsx_strdup(argv[i]);
+ next = lsx_parsesamples(rate, p->bends[i].str, &delay, 't');
+ if (next == NULL || *next != ',')
+ break;
+ p->bends[i].start = time += delay;
+ p->bends[i].cents = strtod(next + 1, (char **)&next);
+ if (p->bends[i].cents == 0 || *next != ',')
+ break;
+ next = lsx_parsesamples(rate, next + 1, &p->bends[i].duration, 't');
+ if (next == NULL || *next != '\0')
+ break;
+ time += p->bends[i].duration;
+ }
+ if (i < p->nbends)
+ return lsx_usage(effp);
+ return SOX_SUCCESS;
+}
+
+static int create(sox_effect_t * effp, int argc, char **argv)
+{
+ priv_t *p = (priv_t *) effp->priv;
+ char const * opts = "f:o:";
+ int c;
+
+ p->frame_rate = 25;
+ p->ovsamp = 16;
+ while ((c = getopt(argc, argv, opts)) != -1) switch (c) {
+ GETOPT_NUMERIC('f', frame_rate, 10 , 80)
+ GETOPT_NUMERIC('o', ovsamp, 4 , 32)
+ default: sox_fail("unknown option `-%c'", optopt); return lsx_usage(effp);
+ }
+ argc -= optind, argv += optind;
+
+ p->bends = lsx_calloc(p->nbends = argc, sizeof(*p->bends));
+ return parse(effp, argv, 1e6); /* No rate yet; parse with dummy */
+}
+
+static int start(sox_effect_t * effp)
+{
+ priv_t *p = (priv_t *) effp->priv;
+ unsigned i;
+
+ int n = effp->in_signal.rate / p->frame_rate + .5;
+ for (p->fftFrameSize = 2; n > 2; p->fftFrameSize <<= 1, n >>= 1);
+ p->shift = 1;
+ parse(effp, 0, effp->in_signal.rate); /* Re-parse now rate is known */
+ p->in_pos = p->bends_pos = 0;
+ for (i = 0; i < p->nbends; ++i)
+ if (p->bends[i].duration)
+ return SOX_SUCCESS;
+ return SOX_EFF_NULL;
+}
+
+static int flow(sox_effect_t * effp, const sox_sample_t * ibuf,
+ sox_sample_t * obuf, size_t * isamp, size_t * osamp)
+{
+ priv_t *p = (priv_t *) effp->priv;
+ size_t i, len = *isamp = *osamp = min(*isamp, *osamp);
+ double magn, phase, tmp, window, real, imag;
+ double freqPerBin, expct;
+ long k, qpd, index, inFifoLatency, stepSize, fftFrameSize2;
+ float pitchShift = p->shift;
+
+ /* set up some handy variables */
+ fftFrameSize2 = p->fftFrameSize / 2;
+ stepSize = p->fftFrameSize / p->ovsamp;
+ freqPerBin = effp->in_signal.rate / p->fftFrameSize;
+ expct = 2. * M_PI * (double) stepSize / (double) p->fftFrameSize;
+ inFifoLatency = p->fftFrameSize - stepSize;
+ if (!p->gRover)
+ p->gRover = inFifoLatency;
+
+ /* main processing loop */
+ for (i = 0; i < len; i++) {
+ ++p->in_pos;
+
+ /* As long as we have not yet collected enough data just read in */
+ p->gInFIFO[p->gRover] = SOX_SAMPLE_TO_FLOAT_32BIT(ibuf[i], effp->clips);
+ obuf[i] = SOX_FLOAT_32BIT_TO_SAMPLE(
+ p->gOutFIFO[p->gRover - inFifoLatency], effp->clips);
+ p->gRover++;
+
+ /* now we have enough data for processing */
+ if (p->gRover >= p->fftFrameSize) {
+ if (p->bends_pos != p->nbends && p->in_pos >=
+ p->bends[p->bends_pos].start + p->bends[p->bends_pos].duration) {
+ pitchShift = p->shift *= pow(2., p->bends[p->bends_pos].cents / 1200);
+ ++p->bends_pos;
+ }
+ if (p->bends_pos != p->nbends && p->in_pos >= p->bends[p->bends_pos].start) {
+ double progress = (double)(p->in_pos - p->bends[p->bends_pos].start) /
+ p->bends[p->bends_pos].duration;
+ progress = 1 - cos(M_PI * progress);
+ progress *= p->bends[p->bends_pos].cents * (.5 / 1200);
+ pitchShift = p->shift * pow(2., progress);
+ }
+
+ p->gRover = inFifoLatency;
+
+ /* do windowing and re,im interleave */
+ for (k = 0; k < p->fftFrameSize; k++) {
+ window = -.5 * cos(2 * M_PI * k / (double) p->fftFrameSize) + .5;
+ p->gFFTworksp[2 * k] = p->gInFIFO[k] * window;
+ p->gFFTworksp[2 * k + 1] = 0.;
+ }
+
+ /* ***************** ANALYSIS ******************* */
+ smbFft(p->gFFTworksp, p->fftFrameSize, -1);
+
+ /* this is the analysis step */
+ for (k = 0; k <= fftFrameSize2; k++) {
+ /* de-interlace FFT buffer */
+ real = p->gFFTworksp[2 * k];
+ imag = p->gFFTworksp[2 * k + 1];
+
+ /* compute magnitude and phase */
+ magn = 2. * sqrt(real * real + imag * imag);
+ phase = atan2(imag, real);
+
+ /* compute phase difference */
+ tmp = phase - p->gLastPhase[k];
+ p->gLastPhase[k] = phase;
+
+ tmp -= (double) k *expct; /* subtract expected phase difference */
+
+ /* map delta phase into +/- Pi interval */
+ qpd = tmp / M_PI;
+ if (qpd >= 0)
+ qpd += qpd & 1;
+ else qpd -= qpd & 1;
+ tmp -= M_PI * (double) qpd;
+
+ /* get deviation from bin frequency from the +/- Pi interval */
+ tmp = p->ovsamp * tmp / (2. * M_PI);
+
+ /* compute the k-th partials' true frequency */
+ tmp = (double) k *freqPerBin + tmp * freqPerBin;
+
+ /* store magnitude and true frequency in analysis arrays */
+ p->gAnaMagn[k] = magn;
+ p->gAnaFreq[k] = tmp;
+
+ }
+
+ /* this does the actual pitch shifting */
+ memset(p->gSynMagn, 0, p->fftFrameSize * sizeof(float));
+ memset(p->gSynFreq, 0, p->fftFrameSize * sizeof(float));
+ for (k = 0; k <= fftFrameSize2; k++) {
+ index = k * pitchShift;
+ if (index <= fftFrameSize2) {
+ p->gSynMagn[index] += p->gAnaMagn[k];
+ p->gSynFreq[index] = p->gAnaFreq[k] * pitchShift;
+ }
+ }
+
+ for (k = 0; k <= fftFrameSize2; k++) { /* SYNTHESIS */
+ /* get magnitude and true frequency from synthesis arrays */
+ magn = p->gSynMagn[k], tmp = p->gSynFreq[k];
+ tmp -= (double) k *freqPerBin; /* subtract bin mid frequency */
+ tmp /= freqPerBin; /* get bin deviation from freq deviation */
+ tmp = 2. * M_PI * tmp / p->ovsamp; /* take p->ovsamp into account */
+ tmp += (double) k *expct; /* add the overlap phase advance back in */
+ p->gSumPhase[k] += tmp; /* accumulate delta phase to get bin phase */
+ phase = p->gSumPhase[k];
+ /* get real and imag part and re-interleave */
+ p->gFFTworksp[2 * k] = magn * cos(phase);
+ p->gFFTworksp[2 * k + 1] = magn * sin(phase);
+ }
+
+ for (k = p->fftFrameSize + 2; k < 2 * p->fftFrameSize; k++)
+ p->gFFTworksp[k] = 0.; /* zero negative frequencies */
+
+ smbFft(p->gFFTworksp, p->fftFrameSize, 1); /* do inverse transform */
+
+ /* do windowing and add to output accumulator */
+ for (k = 0; k < p->fftFrameSize; k++) {
+ window =
+ -.5 * cos(2. * M_PI * (double) k / (double) p->fftFrameSize) + .5;
+ p->gOutputAccum[k] +=
+ 2. * window * p->gFFTworksp[2 * k] / (fftFrameSize2 * p->ovsamp);
+ }
+ for (k = 0; k < stepSize; k++)
+ p->gOutFIFO[k] = p->gOutputAccum[k];
+
+ memmove(p->gOutputAccum, /* shift accumulator */
+ p->gOutputAccum + stepSize, p->fftFrameSize * sizeof(float));
+
+ for (k = 0; k < inFifoLatency; k++) /* move input FIFO */
+ p->gInFIFO[k] = p->gInFIFO[k + stepSize];
+ }
+ }
+ return SOX_SUCCESS;
+}
+
+static int stop(sox_effect_t * effp)
+{
+ priv_t *p = (priv_t *) effp->priv;
+
+ if (p->bends_pos != p->nbends)
+ sox_warn("Input audio too short; bends not applied: %u",
+ p->nbends - p->bends_pos);
+ return SOX_SUCCESS;
+}
+
+static int kill(sox_effect_t * effp)
+{
+ priv_t *p = (priv_t *) effp->priv;
+ unsigned i;
+
+ for (i = 0; i < p->nbends; ++i)
+ free(p->bends[i].str);
+ free(p->bends);
+ return SOX_SUCCESS;
+}
+
+sox_effect_handler_t const *sox_bend_effect_fn(void)
+{
+ static sox_effect_handler_t handler = {
+ "bend", "{delay,cents,duration}", SOX_EFF_GETOPT,
+ create, start, flow, 0, stop, kill, sizeof(priv_t)
+ };
+ return &handler;
+}
--- a/src/effects.h
+++ b/src/effects.h
@@ -20,6 +20,7 @@
EFFECT(bandpass)
EFFECT(bandreject)
EFFECT(bass)
+ EFFECT(bend)
EFFECT(chorus)
EFFECT(compand)
EFFECT(contrast)
--- a/src/effects_i.c
+++ b/src/effects_i.c
@@ -211,71 +211,129 @@
* # of samples.
* Returns NULL on error, pointer to next char to parse otherwise.
*/
-char const * lsx_parsesamples(sox_rate_t rate, const char *str, size_t *samples, int def)
+char const * lsx_parsesamples(sox_rate_t rate, const char *str0, size_t *samples, int def)
{
- int found_samples = 0, found_time = 0;
- int time = 0;
- long long_samples;
- float frac = 0;
- char const * end;
- char const * pos;
- sox_bool found_colon, found_dot;
+ int i, found_samples = 0, found_time = 0;
+ char const * end;
+ char const * pos;
+ sox_bool found_colon, found_dot;
+ char * str = (char *)str0;
- for (end = str; *end && strchr("0123456789:.ts", *end); ++end);
- if (end == str)
+ for (;*str == ' '; ++str);
+ for (end = str; *end && strchr("0123456789:.ets", *end); ++end);
+ if (end == str)
+ return NULL;
+
+ pos = strchr(str, ':');
+ found_colon = pos && pos < end;
+
+ pos = strchr(str, '.');
+ found_dot = pos && pos < end;
+
+ if (found_colon || found_dot || *(end-1) == 't')
+ found_time = 1;
+ else if (*(end-1) == 's')
+ found_samples = 1;
+
+ if (found_time || (def == 't' && !found_samples)) {
+ for (*samples = 0, i = 0; *str != '.' && i < 3; ++i) {
+ char * last_str = str;
+ long part = strtol(str, &str, 10);
+ if (!i && str == last_str)
+ return NULL;
+ *samples += rate * part;
+ if (i < 2) {
+ if (*str != ':')
+ break;
+ ++str;
+ *samples *= 60;
+ }
+ }
+ if (*str == '.') {
+ char * last_str = str;
+ double part = strtod(str, &str);
+ if (str == last_str)
+ return NULL;
+ *samples += rate * part + .5;
+ }
+ return *str == 't'? str + 1 : str;
+ }
+ {
+ char * last_str = str;
+ double part = strtod(str, &str);
+ if (str == last_str)
return NULL;
+ *samples = part + .5;
+ return *str == 's'? str + 1 : str;
+ }
+}
- pos = strchr(str, ':');
- found_colon = pos && pos < end;
+#if 0
- pos = strchr(str, '.');
- found_dot = pos && pos < end;
+#define TEST(st, samp, len) \
+ str = st; \
+ next = lsx_parsesamples(10000, str, &samples, 't'); \
+ assert(samples == samp && next == str + len);
- if (found_colon || found_dot || *(end-1) == 't')
- found_time = 1;
- else if (*(end-1) == 's')
- found_samples = 1;
+int main(int argc, char * * argv)
+{
+ char const * str, * next;
+ size_t samples;
- if (found_time || (def == 't' && !found_samples))
- {
- *samples = 0;
+ TEST("0" , 0, 1)
+ TEST("1" , 10000, 1)
- while(1)
- {
- if (str[0] != '.' && sscanf(str, "%d", &time) != 1)
- return NULL;
- *samples += time;
+ TEST("0s" , 0, 2)
+ TEST("0s,", 0, 2)
+ TEST("0s/", 0, 2)
+ TEST("0s@", 0, 2)
- while (*str != ':' && *str != '.' && *str != 0)
- str++;
+ TEST("0t" , 0, 2)
+ TEST("0t,", 0, 2)
+ TEST("0t/", 0, 2)
+ TEST("0t@", 0, 2)
- if (*str == '.' || *str == 0)
- break;
+ TEST("1s" , 1, 2)
+ TEST("1s,", 1, 2)
+ TEST("1s/", 1, 2)
+ TEST("1s@", 1, 2)
+ TEST(" 01s" , 1, 4)
+ TEST("1e6s" , 1000000, 4)
- /* Skip past ':' */
- str++;
- *samples *= 60;
- }
+ TEST("1t" , 10000, 2)
+ TEST("1t,", 10000, 2)
+ TEST("1t/", 10000, 2)
+ TEST("1t@", 10000, 2)
+ TEST("1.1t" , 11000, 4)
+ TEST("1.1t,", 11000, 4)
+ TEST("1.1t/", 11000, 4)
+ TEST("1.1t@", 11000, 4)
+ TEST("1e6t" , 10000, 1)
- if (*str == '.')
- {
- if (sscanf(str, "%f", &frac) != 1)
- return NULL;
- }
+ TEST(".0", 0, 2)
+ TEST("0.0", 0, 3)
+ TEST("0:0.0", 0, 5)
+ TEST("0:0:0.0", 0, 7)
- *samples *= rate;
- *samples += (rate * frac) + 0.5;
- return end;
- }
- if (found_samples || (def == 's' && !found_time))
- {
- if (sscanf(str, "%ld", &long_samples) != 1)
- return NULL;
- *samples = long_samples;
- return end;
- }
- return NULL;
+ TEST(".1", 1000, 2)
+ TEST(".10", 1000, 3)
+ TEST("0.1", 1000, 3)
+ TEST("1.1", 11000, 3)
+ TEST("1:1.1", 611000, 5)
+ TEST("1:1:1.1", 36611000, 7)
+ TEST("1:1", 610000, 3)
+ TEST("1:01", 610000, 4)
+ TEST("1:1:1", 36610000, 5)
+ TEST("1:", 600000, 2)
+ TEST("1::", 36000000, 3)
+
+ TEST("0.444444", 4444, 8)
+ TEST("0.555555", 5556, 8)
+
+ assert(!lsx_parsesamples(10000, "x", &samples, 't'));
+ return 0;
}
+#endif
/* a note is given as an int,
* 0 => 440 Hz = A