Silence Marker Plus (a Silence Finder modification)

Silence Marker Plus
Marks silence in more ways than default Silence Finder

;nyquist plug-in
;version 1
;type analyze
;categories ""
;name "Silence Marker Plus..."
;action "Marking silence in various ways..."
;info "Adds labels in various points of silence according to the specified level and duration of silence.\nIf too many silences are detected, increase the silence level and/or duration;\nif too few are detected, reduce the level and/or duration.\nThe more samples per second checked, the more precisely the program can place the silence labels, but the longer it takes."
;author "uvhwpevx"
;copyright "Released under terms of the GNU General Public License version 2"

;; by uvhwpevx
;; Based on Silence Finder by Alex S. Brown, PMP (
;; Released under terms of the GNU General Public License version 2:

;control sil-lev "Treat audio below this level as silence, -dB" real "" 26 0 100
;control sil-dur "Minimum duration of silence, seconds" real "" 0.25 0.01 60
;control per-sec "Analyze samples per second" int "" 1000 1 16000
;control labeltype "Label placement" choice "Silence midpoint,At % of silence time,All silence (T1...T2),At T1 seconds after silence begins,At T2 seconds before silence ends" 0
;control labelatdur "Label at % of silence time (if selected)" real "" 33.333 0.0 100.0
;control labelafterdur "Padding T1 after silence begins, seconds" real "" 0.0 0.0 60.0
;control labelbeforedur "Padding T2 before silence ends, seconds" real "" 0.0 0.0 60.0
;control labeltext "Label text" string "" "" ""

;Create a function to make the sum the two channels if they are stereo
(defun mono-s (s-in) (if (arrayp s-in) (snd-add (aref s-in 0) (aref s-in 1)) s-in))

;Create a function to reduce the sample rate and prepare the signal for
;analysis. RMS is good to monitor volume the way humans hear it, but is not
;available in Audacity. Used a peak-calculating function instead.
;NOTE: this is the place to add any processing to improve the quality of the
;signal. Noise filters could improve the quality of matches for noisy signals.
;Reducing the samples per second should improve the performance and decrease
;the accuracy of the labels. Increasing the samples per second will do the
;opposite. The more samples checked, the longer it takes. The more samples
;checked, the more precisely the program can place the silence labels.
;my-srate-ratio determines the number of samples in my-s.

(defun my-s (s-in)
 (setq my-srate-ratio (truncate (/ (snd-srate (mono-s s-in)) per-sec)))
 (snd-avg (mono-s s-in) my-srate-ratio my-srate-ratio OP-PEAK)

;Set the silence threshold level (convert it to a linear form)
(setq thres (db-to-linear (* -1 sil-lev)))
;Store the sample rate of the sound
(setq s1-srate (snd-srate (my-s s)))
;Initialize the variable that will hold the length of the sound.
;Do not calculate it now with snd-length, because it would waste memory.
;We will calculate it later.
(setq s1-length 0)
;Initialize the silence counter and the labels variable
(setq sil-c 0)
(setq l NIL)
;Convert the silence duration in seconds to a length in samples
(setq sil-length (* sil-dur s1-srate))

;Define a function to add new items to the list of labels
(defun add-label (l-time l-text)
 (setq l (cons (list l-time l-text) l))

(defun add-range-label (l-starttime l-endtime l-text)
 (setq l (cons (list l-starttime l-endtime l-text) l))

;The main working part of the program, it counts
;the number of sequential samples with volume under
;the threshold. It adds to a list of markers ever time
;there is a longer period of silence than the silence
;duration amount.

;It runs through a loop, adding to the list of markers (l) each time it finds silence.
(let (s1) ;Define s1 as a local variable to allow efficient memory use
 ; Get the sample into s1, then free s to save memory
 (setq s1 (my-s s))
 (setq s nil)
 ;Capture the result of this "do" loop, because we need the sountd's legnth in samples.
 (setq s1-length
  ;Keep repeating, incrementing the counter and getting another sample each time through the loop.
  (do ((n 1 (+ n 1)) (v (snd-fetch s1) (setq v (snd-fetch s1))))
   ;Exit when we run out of samples (v is nil) and return the number of samples processed (n)
   ((not v) n)
   ;Start the execution part of the do loop
   ;if found silence, increment the silence counter
   (if (< v thres) (setq sil-c (+ sil-c 1)))

   ;If this sample is NOT silent and the previous samples were silent then mark the passage.
   (if (and (> v thres) (> sil-c sil-length))
    (case labeltype
     (0 (add-label (/ (- n (/ sil-c 2)) s1-srate) labeltext)) ;At midpoint
     (1 (add-label (/ (- n (* sil-c (- 1.0 (/ labelatdur 100.0)))) s1-srate) labeltext)) ; At % of silence time
     (2 (if (>= (/ sil-c s1-srate) (+ labelafterdur labelbeforedur)) ;All silence (T1...T2)
       (add-range-label (+ (/ (- n sil-c) s1-srate) labelafterdur) (- (/ n s1-srate) labelbeforedur) labeltext)))
     (3 (if (>= (/ sil-c s1-srate) labelafterdur) ;At T1 seconds after silence begins
         (add-label (+ (/ (- n sil-c) s1-srate) labelafterdur) labeltext)))
     (4 (if (>= (/ sil-c s1-srate) labelbeforedur) ;At T2 seconds before silence ends
         (add-label (- (/ n s1-srate) labelbeforedur) labeltext)))
   ;If this sample is NOT silent, then reset the silence counter
   (if (> v thres)
    (setq sil-c 0)

;Check for a long period of silence at the end of the sample. If so, then mark it.
(if (> sil-c sil-length)
 ;If found, add a label
 ;Label time is the time the silence began plus the silence duration target
 ;amount. We calculate the time the silence began as the end-time minus the
 ;final value of the silence counter
 (add-label (+ (/ (- s1-length sil-c) s1-srate) sil-dur) labeltext)

;If no silence markers were found, return a message
(if (null l)
 (setq l "No silences found.\nTry reducing the silence level\nand/or minimum silence duration.")

Unfortunately, moderators still are suspecting me of something hideous and disabled BBCode and all stuff in my messages…

Please understand that keeping this forum free of spam, pornography, trolls and flame wars … is a lot of work. The small team of moderators and forum friends that keep this forum as a helpful and safe little corner in cyberspace, separate the ham from the spam for hundreds of thousands of posts.

Your account got a black mark against it when you posted a direct link to a file, which according to 32 virus checkers, was infected by a virus.
This is the virus total report for the file that you posted to the forum:

I shall review your account settings when I get time. Meanwhile, your posts will be published to the forum subject to approval by a forum moderator.

As I said then, all of those are false positive and I ain’t gonna fight those windmills nor prove here my goodness. Anyone can get my source code of it on Delphi there on GitHub and check it all.

I’m sure that some Audacity users will find these additional options useful. Perhaps you could provide a little documentation to explain what the new options are and how to use them? (the wiki has examples of concise plug-in documentation Missing features - Audacity Support)

One problem that I noticed is that if “Analyze samples per second” is set too low, then “Minimum duration of silence, seconds” does not work correctly.

You may also find this brief guide useful: Indenting Common Lisp

Quite possibly, but my job here is to keep the forum running as a safe and useful resource for Audacity users, so I’m not going to ignore 30+ virus checkers. I suggest that we put that behind us and move on.

Basically, the problem came from the Silence Finder code. There “samples per second” was fixed 100 and “silence duration” shorter than 0.15s didn’t work too. I can’t find the reason and solution.

I can’t do much. I can’t even edit my own messages. This is frustrating and kills any desire to improve and contribute.

The “SND-AVG” function performs a kind of resampling (

The selected audio is resampled to a fairly low sample rate before being analyzed, so that there are less samples to analyze (the actual analysis loops through one sample at a time, which is very inefficient at normal audio sample rates because there are so many samples).

At a sample rate of 100 Hz, each sample represents 1/100th of a second (0.01 seconds), so silences in the original audio must be bigger than this or they will be completely “invisible” at the lower sample rate.

This shows what happens to a 0.11 second silence when resampled (with snd-avg / op-peak) to 10 Hz:
Just spotted a bug:

(if (and (> v thres) (> sil-c sil-length))

‘sil-length’ is the number of low sample rate samples equivalent to the ‘minimum silence duration’
If the sample rate is say 100 Hz, then each sample period is 0.01 seconds
To find silences that are at least 0.01 seconds, we need to find at least 1 sample below the threshold, but the above code says “more than”, so we will not actually be able to detect silences unless they are at least 0.02 seconds duration. Try changing this to:

(if (and (> v thres) (>= sil-c sil-length))

In practice, if you want the effect to have a minimum silence duration capability of 0.1 seconds, then the sample period must be less than 0.05 seconds, so that’s a sample rate of 20 Hz or more.
So something like:

;control sil-dur "Minimum duration of silence, seconds" real "" 0.25 0.1 60
;control per-sec "Analyze samples per second" int "" 1000 20 16000

Generally it’s better not to edit messages after they have been replied to, otherwise the topic threads rapidly become impossible to follow. If you need to correct something, better to make a new post with the correction.

By the way, there has just been a glitch on the web server. The system admin is on the case, so it should be fixed soon, but you may notice intermittent connection problems for a short while.

Another bug (which also exists in the original code):

;Create a function to make the sum the two channels if they are stereo
(defun mono-s (s-in) (if (arrayp s-in) (snd-add (aref s-in 0) (aref s-in 1)) s-in))

Why would we want the sum of the two channels? Wouldn’t we want the absolute maximum (ignoring sign) of the two channels?

Just checked: an increasing from 20Hz up to 100Hz and even 1000Hz gave me up to +0.07s or +0.09s in the marking precision. I think, the 0.09s in a period of the 0.10s - it is a very significant increase of the precision!

I really didn’ t get it. Should I better make a NEW TOPIC every time instead of fixing the code in the TOP MESSAGE or adding some explains there? :slight_smile:

Just post a new reply, then anyone reading the topic will be able to follow the progression.