ref: 32736bb3b3a40125b6985844dc1efe280f02dd45
parent: 09be3ef60edd86c77c913b6214ac20f9527de16f
author: cbagwell <cbagwell>
date: Sun Sep 2 19:27:37 EDT 2001
Finishing up first debugged version of silence effect.
--- a/src/silence.c
+++ b/src/silence.c
@@ -1,13 +1,14 @@
-/* Silence effect for SoX
- * by Heikki Leinonen (heilei@iki.fi) 25.03.2001
- * Major Modifications by Chris Bagwell 06.08.2001
+/* Silence effect for SoX
+ * by Heikki Leinonen (heilei@iki.fi) 25.03.2001
+ * Major Modifications by Chris Bagwell 06.08.2001
*
- * This effect deletes samples from the start of the sound
- * file until a sample exceeds a given threshold (either
- * left or right channel in stereo files). This can be used
- * to filter out unwanted silence or low noise in the beginning
- * of a sound file. The threshold can be given either as a
- * percentage or in decibels.
+ * This effect can delete samples from the start of a sound file
+ * until it sees a specified count of samples exceed a given threshold
+ * (any of the channels).
+ * This effect can also delete samples from the end of a sound file
+ * when it sees a specified count of samples below a given threshold
+ * (all channels).
+ * Theshold's can be given as either a percentage or in decibels.
*/
@@ -29,96 +30,219 @@
/* Private data for silence effect. */
-#define SILENCE_START 0
-#define SILENCE_TRIM 1
-#define SILENCE_COPY 2
-#define SILENCE_FLUSH 3
-#define SILENCE_STOP 4
+#define SILENCE_TRIM 0
+#define SILENCE_TRIM_FLUSH 1
+#define SILENCE_COPY 2
+#define SILENCE_COPY_FLUSH 3
+#define SILENCE_STOP 4
typedef struct silencestuff
{
- char trim;
- double trim_threshold;
- char trim_unit; /* "d" for decibels or "%" for percent. */
+ char start;
+ int start_periods;
+ char *start_duration_str;
+ ULONG start_duration;
+ double start_threshold;
+ char start_unit; /* "d" for decibels or "%" for percent. */
+
+ LONG *start_holdoff;
+ ULONG start_holdoff_offset;
+ ULONG start_holdoff_end;
+ int start_found_periods;
+
char stop;
- int stop_count;
+ int stop_periods;
+ char *stop_duration_str;
ULONG stop_duration;
double stop_threshold;
char stop_unit;
- LONG *holdoff;
- ULONG holdoff_count;
- ULONG holdoff_offset;
+
+ LONG *stop_holdoff;
+ ULONG stop_holdoff_offset;
+ ULONG stop_holdoff_end;
+ int stop_found_periods;
+
+ /* State Machine */
char mode;
- char crossings;
} *silence_t;
-/*#define SILENCE_USAGE "Usage: silence count duration thershold [d | %% | s] [ -notrim ] [ count duration threshold [ d | %% | s ]]" */
+#define SILENCE_USAGE "Usage: silence above_periods [ duration thershold[d | %% | s] ] [ below_periods duration threshold[ d | %% | s ]]"
-#define SILENCE_USAGE "Usage: silence count duration threshold [d | %%]"
-
int st_silence_getopts(eff_t effp, int n, char **argv)
{
- silence_t silence = (silence_t) effp->priv;
- ULONG duration;
+ silence_t silence = (silence_t) effp->priv;
+ int parse_count;
- if (n < 3)
+ if (n < 1)
+ {
+ st_fail(SILENCE_USAGE);
+ return (ST_EOF);
+ }
+
+ /* Parse data related to trimming front side */
+ silence->start = FALSE;
+ if (sscanf(argv[0], "%d", &silence->start_periods) != 1)
+ {
+ st_fail(SILENCE_USAGE);
+ return(ST_EOF);
+ }
+ if (silence->start_periods < 0)
+ {
+ st_fail("Periods must not be negative");
+ return(ST_EOF);
+ }
+ argv++;
+ n--;
+
+ if (silence->start_periods > 0)
+ {
+ silence->start = TRUE;
+ if (n < 2)
{
st_fail(SILENCE_USAGE);
- return (ST_EOF);
+ return ST_EOF;
}
- silence->trim = FALSE;
- silence->trim_threshold = 1.0;
- silence->trim_unit = '%';
+ /* We do not know the sample rate so we can not fully
+ * parse the duration info yet. So save argument off
+ * for future processing.
+ */
+ silence->start_duration_str = malloc(strlen(argv[0])+1);
+ if (!silence->start_duration_str)
+ {
+ st_fail("Could not allocate memory");
+ return(ST_EOF);
+ }
+ strcpy(silence->start_duration_str,argv[0]);
+ /* Perform a fake parse to do error checking */
+ if (st_parsesamples(0,silence->start_duration_str,
+ &silence->start_duration,'s') !=
+ ST_SUCCESS)
+ {
+ st_fail(SILENCE_USAGE);
+ return(ST_EOF);
+ }
- silence->stop = TRUE;
- if (sscanf(argv[0], "%d", &silence->stop_count) != 1)
+ parse_count = sscanf(argv[1], "%lf%c", &silence->start_threshold,
+ &silence->start_unit);
+ if (parse_count < 1)
{
st_fail(SILENCE_USAGE);
return ST_EOF;
}
- if (sscanf(argv[1], "%ld", &duration) != 1)
+ else if (parse_count < 2)
+ silence->start_unit = 's';
+
+ argv++; argv++;
+ n--; n--;
+ }
+ else
+ {
+ st_fail(SILENCE_USAGE);
+ return ST_EOF;
+ }
+
+ silence->stop = FALSE;
+ /* Parse data needed for trimming of backside */
+ if (n > 0)
+ {
+ if (n < 3)
{
st_fail(SILENCE_USAGE);
return ST_EOF;
}
- silence->stop_duration = duration;
- if (sscanf(argv[2], "%lf", &silence->stop_threshold) != 1)
+ if (sscanf(argv[0], "%d", &silence->stop_periods) != 1)
{
st_fail(SILENCE_USAGE);
return ST_EOF;
}
+ if (silence->stop_periods < 0)
+ {
+ st_fail("Periods must not be greater then zero");
+ return(ST_EOF);
+ }
+ silence->stop = TRUE;
+ argv++;
+ n--;
- if (n > 3 && strlen(argv[3]) == 1)
+ /* We do not know the sample rate so we can not fully
+ * parse the duration info yet. So save argument off
+ * for future processing.
+ */
+ silence->stop_duration_str = malloc(strlen(argv[0])+1);
+ if (!silence->stop_duration_str)
{
- silence->stop_unit = argv[3][0];
+ st_fail("Could not allocate memory");
+ return(ST_EOF);
}
+ strcpy(silence->stop_duration_str,argv[0]);
+ /* Perform a fake parse to do error checking */
+ if (st_parsesamples(0,silence->stop_duration_str,
+ &silence->stop_duration,'s') !=
+ ST_SUCCESS)
+ {
+ st_fail(SILENCE_USAGE);
+ return(ST_EOF);
+ }
- if (silence->stop_count != 1)
- {
- st_warn("Only support a stop count of 1 currently");
+ parse_count = sscanf(argv[1], "%lf%c", &silence->stop_threshold,
+ &silence->stop_unit);
+ if (parse_count < 1)
+ {
st_fail(SILENCE_USAGE);
+ return ST_EOF;
+ }
+ else if (parse_count < 2)
+ silence->stop_unit = 's';
+
+ argv++; argv++;
+ n--; n--;
+ }
+
+ /* Error checking */
+ if (silence->start)
+ {
+ if ((silence->start_unit != '%') && (silence->start_unit != 'd') &&
+ (silence->start_unit != 's'))
+ {
+ st_fail("Invalid unit specified");
+ st_fail(SILENCE_USAGE);
return(ST_EOF);
}
+ if ((silence->start_unit == '%') && ((silence->start_threshold < 0.0)
+ || (silence->start_threshold > 100.0)))
+ {
+ st_fail("silence threshold should be between 0.0 and 100.0 %%");
+ return (ST_EOF);
+ }
+ if ((silence->start_unit == 'd') && (silence->start_threshold >= 0.0))
+ {
+ st_fail("silence threshold should be less than 0.0 dB");
+ return(ST_EOF);
+ }
+ }
+ if (silence->stop)
+ {
if ((silence->stop_unit != '%') && (silence->stop_unit != 'd') &&
(silence->stop_unit != 's'))
{
- st_fail(SILENCE_USAGE);
- return(ST_EOF);
+ st_fail("Invalid unit specified");
+ return(ST_EOF);
}
if ((silence->stop_unit == '%') && ((silence->stop_threshold < 0.0) ||
- (silence->stop_threshold > 100.0)))
+ (silence->stop_threshold > 100.0)))
{
- st_fail("silence threshold should be between 0.0 and 100.0 %%");
- return (ST_EOF);
+ st_fail("silence threshold should be between 0.0 and 100.0 %%");
+ return (ST_EOF);
}
if ((silence->stop_unit == 'd') && (silence->stop_threshold >= 0.0))
{
- st_fail("silence threshold should be less than 0.0 dB");
- return(ST_EOF);
+ st_fail("silence threshold should be less than 0.0 dB");
+ return(ST_EOF);
}
- return(ST_SUCCESS);
+ }
+ return(ST_SUCCESS);
}
int st_silence_start(eff_t effp)
@@ -125,14 +249,37 @@
{
silence_t silence = (silence_t) effp->priv;
- silence->mode = SILENCE_START;
+ /* Now that we now sample rate, reparse duration. */
+ if (st_parsesamples(effp->ininfo.rate, silence->stop_duration_str,
+ &silence->stop_duration,'s') !=
+ ST_SUCCESS)
+ {
+ st_fail(SILENCE_USAGE);
+ return(ST_EOF);
+ }
+ if (st_parsesamples(effp->ininfo.rate,silence->stop_duration_str,
+ &silence->stop_duration,'s') !=
+ ST_SUCCESS)
+ {
+ st_fail(SILENCE_USAGE);
+ return(ST_EOF);
+ }
- silence->holdoff = malloc(sizeof(LONG)*silence->stop_duration);
- silence->holdoff_count = 0;
- silence->holdoff_offset = 0;
+ if (silence->start)
+ silence->mode = SILENCE_TRIM;
+ else
+ silence->mode = SILENCE_COPY;
- silence->crossings = 0;
+ silence->start_holdoff = malloc(sizeof(LONG)*silence->start_duration);
+ silence->start_holdoff_offset = 0;
+ silence->start_holdoff_end = 0;
+ silence->start_found_periods = 0;
+ silence->stop_holdoff = malloc(sizeof(LONG)*silence->stop_duration);
+ silence->stop_holdoff_offset = 0;
+ silence->stop_holdoff_end = 0;
+ silence->stop_found_periods = 0;
+
return(ST_SUCCESS);
}
@@ -161,11 +308,12 @@
/* Process signed long samples from ibuf to obuf. */
/* Return number of samples processed in isamp and osamp. */
-int st_silence_flow(eff_t effp, LONG *ibuf, LONG *obuf, LONG *isamp, LONG *osamp)
+int st_silence_flow(eff_t effp, LONG *ibuf, LONG *obuf,
+ LONG *isamp, LONG *osamp)
{
silence_t silence = (silence_t) effp->priv;
int threshold, i, j;
- LONG nrOfTicks, nrOfInSamplesRead, nrOfOutSamplesWritten;
+ ULONG nrOfTicks, nrOfInSamplesRead, nrOfOutSamplesWritten;
nrOfInSamplesRead = 0;
nrOfOutSamplesWritten = 0;
@@ -172,40 +320,77 @@
switch (silence->mode)
{
- case SILENCE_START:
- if (!silence->trim)
- {
- /* If no trimming then copy over starting from here */
- silence->mode = SILENCE_COPY;
- goto silence_copy;
- }
- else
- /* Fall through and trim audio */
- silence->mode = SILENCE_TRIM;
-
- /* Reads and discards all input data until it detects a
- * sample that is above the specified threshold. Turns on
- * copy mode when detected.
- */
case SILENCE_TRIM:
+ /* Reads and discards all input data until it detects a
+ * sample that is above the specified threshold. Turns on
+ * copy mode when detected.
+ */
nrOfTicks = min((*isamp), (*osamp)) / effp->ininfo.channels;
for(i = 0; i < nrOfTicks; i++)
{
- threshold = 1;
+ threshold = 0;
for (j = 0; j < effp->ininfo.channels; j++)
{
- threshold &= aboveThreshold(ibuf[j], silence->trim_threshold,
- silence->trim_unit);
+ threshold |= aboveThreshold(ibuf[j],
+ silence->start_threshold,
+ silence->start_unit);
}
if (threshold)
{
- silence->mode = SILENCE_COPY;
- goto silence_copy;
+ /* Add to holdoff buffer */
+ for (j = 0; j < effp->ininfo.channels; j++)
+ {
+ silence->start_holdoff[
+ silence->start_holdoff_end++] = *ibuf++;
+ nrOfInSamplesRead++;
+ }
+
+ if (silence->start_holdoff_end >=
+ silence->start_duration)
+ {
+ if (++silence->start_found_periods >=
+ silence->start_periods)
+ {
+ silence->mode = SILENCE_TRIM_FLUSH;
+ goto silence_trim_flush;
+ }
+ /* Trash holdoff buffer since its not
+ * needed. Start looking again.
+ */
+ silence->start_holdoff_offset = 0;
+ silence->start_holdoff_end = 0;
+ }
}
- ibuf += effp->ininfo.channels;
- nrOfInSamplesRead += effp->ininfo.channels;
+ else /* !above Threshold */
+ {
+ silence->start_holdoff_end = 0;
+ ibuf += effp->ininfo.channels;
+ nrOfInSamplesRead += effp->ininfo.channels;
+ }
+ } /* for nrOfTicks */
+ break;
+
+ case SILENCE_TRIM_FLUSH:
+silence_trim_flush:
+ nrOfTicks = min((silence->start_holdoff_end -
+ silence->start_holdoff_offset),
+ (*osamp-nrOfOutSamplesWritten)) /
+ effp->ininfo.channels;
+ for(i = 0; i < nrOfTicks; i++)
+ {
+ *obuf++ = silence->start_holdoff[silence->start_holdoff_offset++];
+ nrOfOutSamplesWritten++;
}
+
+ if (silence->start_holdoff_offset == silence->start_holdoff_end)
+ {
+ silence->start_holdoff_offset = 0;
+ silence->start_holdoff_end = 0;
+ silence->mode = SILENCE_COPY;
+ goto silence_copy;
+ }
break;
+
case SILENCE_COPY:
/* Attempts to copy samples into output buffer. If not
* looking for silence to terminate copy then blindly
@@ -225,22 +410,28 @@
*/
silence_copy:
nrOfTicks = min((*isamp-nrOfInSamplesRead),
- (*osamp-nrOfOutSamplesWritten)) / effp->ininfo.channels;
+ (*osamp-nrOfOutSamplesWritten)) /
+ effp->ininfo.channels;
if (silence->stop)
{
for(i = 0; i < nrOfTicks; i++)
{
- threshold = 0;
+ threshold = 1;
for (j = 0; j < effp->ininfo.channels; j++)
{
- threshold |= aboveThreshold(ibuf[j],
+ threshold &= aboveThreshold(ibuf[j],
silence->stop_threshold,
silence->stop_unit);
}
- if (threshold && silence->holdoff_count)
+ /* If above threshold, check to see if we where holding
+ * off previously. If so then flush this buffer.
+ * We haven't incremented any pionters yet so nothing
+ * is lost.
+ */
+ if (threshold && silence->stop_holdoff_end)
{
- silence->mode = SILENCE_FLUSH;
- goto silence_flush;
+ silence->mode = SILENCE_COPY_FLUSH;
+ goto silence_copy_flush;
}
else if (threshold)
{
@@ -255,28 +446,43 @@
/* Add to holdoff buffer */
for (j = 0; j < effp->ininfo.channels; j++)
{
- silence->holdoff[silence->holdoff_count++] =
- *ibuf++;
+ silence->stop_holdoff[
+ silence->stop_holdoff_end++] = *ibuf++;
nrOfInSamplesRead++;
}
- /* Check if holdoff buffer is greater than duration,
- * if so then stop processing.
+ /* Check if holdoff buffer is greater than duration
*/
- if (silence->holdoff_count >=
+ if (silence->stop_holdoff_end >=
silence->stop_duration)
{
- silence->mode = SILENCE_STOP;
- silence->holdoff_count = 0;
- *isamp = nrOfInSamplesRead;
- *osamp = nrOfOutSamplesWritten;
- /* Return ST_EOF to indicate no more processing */
- return (ST_EOF);
+ /* Increment found counter and see if this
+ * is the last period. If so then exit.
+ */
+ if (++silence->stop_found_periods >=
+ silence->stop_periods)
+ {
+ silence->mode = SILENCE_STOP;
+ silence->stop_holdoff_offset = 0;
+ silence->stop_holdoff_end = 0;
+ *isamp = nrOfInSamplesRead;
+ *osamp = nrOfOutSamplesWritten;
+ /* Return ST_EOF since no more processing */
+ return (ST_EOF);
+ }
+ else
+ {
+ /* Flush this buffer and start
+ * looking again.
+ */
+ silence->mode = SILENCE_COPY_FLUSH;
+ goto silence_copy_flush;
+ }
break;
}
}
}
}
- else
+ else /* !(silence->stop) */
{
memcpy(obuf, ibuf, sizeof(LONG)*nrOfTicks);
nrOfInSamplesRead += nrOfTicks;
@@ -284,27 +490,29 @@
}
break;
- case SILENCE_FLUSH:
-silence_flush:
- nrOfTicks = min((silence->holdoff_count - silence->holdoff_offset),
+ case SILENCE_COPY_FLUSH:
+silence_copy_flush:
+ nrOfTicks = min((silence->stop_holdoff_end -
+ silence->stop_holdoff_offset),
(*osamp-nrOfOutSamplesWritten)) / effp->ininfo.channels;
for(i = 0; i < nrOfTicks; i++)
{
- *obuf++ = silence->holdoff[silence->holdoff_offset++];
+ *obuf++ = silence->stop_holdoff[silence->stop_holdoff_offset++];
nrOfOutSamplesWritten++;
}
- if (silence->holdoff_offset == silence->holdoff_count)
+ if (silence->stop_holdoff_offset == silence->stop_holdoff_end)
{
- silence->holdoff_offset = 0;
- silence->holdoff_count = 0;
+ silence->stop_holdoff_offset = 0;
+ silence->stop_holdoff_end = 0;
silence->mode = SILENCE_COPY;
- /* Return to copy mode incase there are is more room in output buffer
- * to copy some more data from input buffer.
+ /* Return to copy mode incase there are is more room in
+ * output buffer to copy some more data from input buffer.
*/
goto silence_copy;
}
break;
+
case SILENCE_STOP:
nrOfInSamplesRead = *isamp;
break;
@@ -325,20 +533,21 @@
/* Only if in flush mode will there be possible samples to write
* out during drain() call.
*/
- if (silence->mode == SILENCE_FLUSH)
+ if (silence->mode == SILENCE_COPY_FLUSH)
{
- nrOfTicks = min((silence->holdoff_count - silence->holdoff_offset),
+ nrOfTicks = min((silence->stop_holdoff_end -
+ silence->stop_holdoff_offset),
*osamp) / effp->ininfo.channels;
for(i = 0; i < nrOfTicks; i++)
{
- *obuf++ = silence->holdoff[silence->holdoff_offset++];
+ *obuf++ = silence->stop_holdoff[silence->stop_holdoff_offset++];
nrOfOutSamplesWritten++;
}
- if (silence->holdoff_offset == silence->holdoff_count)
+ if (silence->stop_holdoff_offset == silence->stop_holdoff_end)
{
- silence->holdoff_offset = 0;
- silence->holdoff_count = 0;
+ silence->stop_holdoff_offset = 0;
+ silence->stop_holdoff_end = 0;
silence->mode = SILENCE_STOP;
}
}
@@ -351,6 +560,6 @@
{
silence_t silence = (silence_t) effp->priv;
- free(silence->holdoff);
+ free(silence->stop_holdoff);
return(ST_SUCCESS);
}