shithub: sox

Download patch

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);
 }