mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-16 08:09:32 +02:00
Bug 2352: Rewrite of Silence Finder
Silence Finder now throws an error if selection too long. UI updated to use negative dB consistent with all other shipped effects. Use max of channels for stereo tracks rather than sum of channels. Avoid resampling track twice. Update to version 4 syntax. Remove Debug button, consistent with other shipped effects.
This commit is contained in:
parent
ac37d78388
commit
1f8574c144
@ -1,124 +1,95 @@
|
|||||||
$nyquist plug-in
|
$nyquist plug-in
|
||||||
$version 1
|
$version 4
|
||||||
$type analyze
|
$type analyze
|
||||||
|
$debugbutton false
|
||||||
|
$debugflags trace
|
||||||
$name (_ "Silence Finder")
|
$name (_ "Silence Finder")
|
||||||
$manpage "Silence_Finder"
|
$manpage "Silence_Finder"
|
||||||
$action (_ "Finding silence...")
|
$author (_ "Steve Daulton")
|
||||||
$author (_ "Alex S. Brown")
|
$release 2.4.0
|
||||||
$release 2.3.0
|
|
||||||
$copyright (_ "Released under terms of the GNU General Public License version 2")
|
$copyright (_ "Released under terms of the GNU General Public License version 2")
|
||||||
|
|
||||||
;; by Alex S. Brown, PMP (http://www.alexsbrown.com)
|
;; Original version by Alex S. Brown, PMP (http://www.alexsbrown.com)
|
||||||
;;
|
;;
|
||||||
;; Adds point labels in areas of silence according to the specified
|
|
||||||
;; level and duration of silence. If too many silences are detected,
|
|
||||||
;; increase the silence level and duration; if too few are detected,
|
|
||||||
;; reduce the level and duration."
|
|
||||||
|
|
||||||
;; Released under terms of the GNU General Public License version 2:
|
;; Released under terms of the GNU General Public License version 2:
|
||||||
;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||||
;;
|
;;
|
||||||
;; For information about writing and modifying Nyquist plug-ins:
|
;; For information about writing and modifying Nyquist plug-ins:
|
||||||
;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference
|
;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference
|
||||||
|
|
||||||
$control sil-lev (_ "Treat audio below this level as silence [ -dB]") real "" 26 0 100
|
$control threshold (_ "Treat audio below this level as silence (dB)") real "" -30 -100 0
|
||||||
$control sil-dur (_ "Minimum duration of silence [seconds]") real "" 1.0 0.1 5.0
|
$control min-silence (_ "Minimum duration of silence (seconds)") real "" 1.0 0.1 5.0
|
||||||
$control labelbeforedur (_ "Label placement [seconds before silence ends]") real "" 0.3 0.0 1.0
|
$control label-position (_ "Label placement (seconds before silence ends)") real "" 0.3 0.0 1.0
|
||||||
|
|
||||||
;Create a function to make the sum the two channels if they are stereo
|
(setf threshold (db-to-linear threshold))
|
||||||
(defun mono-s (s-in) (if (arrayp s-in) (snd-add (aref s-in 0) (aref s-in 1))
|
; Label is usually offset to earlier time.
|
||||||
s-in))
|
(setf label-position (- label-position))
|
||||||
|
|
||||||
;Create a function to reduce the sample rate and prepare the signal for
|
;i18n-hint: Abbreviation of "Silence".
|
||||||
;analysis. RMS is good to monitor volume the way humans hear it, but is not
|
(setf *labeltxt* (_ "S"))
|
||||||
;available in Audacity. Used a peak-calculating function instead.
|
(setf *labels* NIL)
|
||||||
;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.
|
|
||||||
;PERFORMANCE vs. ACCURACY
|
|
||||||
;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. Set the number after (snd-srate s)
|
|
||||||
;higher to increase the number of samples.
|
|
||||||
|
|
||||||
(defun my-s (s-in)
|
|
||||||
(setq my-srate-ratio (truncate (/ (snd-srate (mono-s s-in)) 100)))
|
|
||||||
(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)
|
(defun to-mono (sig)
|
||||||
(setq thres (db-to-linear (* -1 sil-lev)))
|
;;; coerce sig to mono.
|
||||||
;Store the sample rate of the sound
|
(if (arrayp sig)
|
||||||
(setq s1-srate (snd-srate (my-s s)))
|
(s-max (s-abs (aref sig 0))
|
||||||
;Initialize the variable that will hold the length of the sound.
|
(s-abs (aref sig 1)))
|
||||||
;Do not calculate it now with snd-length, because it would waste memory.
|
sig))
|
||||||
;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 reduce-srate (sig)
|
||||||
(defun add-label (l-time l-text)
|
;;; Reduce sample rate to (about) 100 Hz.
|
||||||
(setq l (cons (list l-time l-text) l))
|
(let ((ratio (round (/ *sound-srate* 100))))
|
||||||
)
|
(snd-avg sig ratio ratio OP-PEAK)))
|
||||||
|
|
||||||
;The main working part of the program, it counts
|
(defun add-label (samples srate offset)
|
||||||
;the number of sequential samples with volume under
|
;;; Add new label to *labels*
|
||||||
;the threshold. It adds to a list of markers ever time
|
(let ((time (+ (/ samples srate) offset)))
|
||||||
;there is a longer period of silence than the silence
|
(push (list time *labeltxt*) *labels*)))
|
||||||
;duration amount.
|
|
||||||
|
|
||||||
;It runs through a loop, adding to the list of markers (l)
|
(defun format-time (s)
|
||||||
;each time it finds silence.
|
;;; format time in seconds as h m s.
|
||||||
(let (s1) ;Define s1 as a local variable to allow efficient memory use
|
(let* ((hh (truncate (/ s 3600)))
|
||||||
; Get the sample into s1, then free s to save memory
|
(mm (truncate (/ s 60))))
|
||||||
(setq s1 (my-s s))
|
;i18n-hint: hours minutes and seconds. Do not translate "~a".
|
||||||
(setq s nil)
|
(format nil (_ "~ah ~am ~as")
|
||||||
;Capture the result of this "do" loop, because we need the sountd's legnth
|
hh (- mm (* hh 60)) (rem (truncate s) 60))))
|
||||||
;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
|
(defun label-silences (sig)
|
||||||
;then mark the passage.
|
;;; Label silences that are longer than 'min-len' samples.
|
||||||
(if (and (> v thres) (> sil-c sil-length))
|
(let* ((sample-count 0)
|
||||||
;Mark the user-set number of seconds BEFORE this point to avoid clipping the start
|
(sil-count 0)
|
||||||
;of the material.
|
(srate (snd-srate sig))
|
||||||
; Should "S" be translated or become a control value?
|
(min-len (* min-silence srate)))
|
||||||
(add-label (- (/ n s1-srate) labelbeforedur) "S")
|
(do ((val (snd-fetch sig) (snd-fetch sig)))
|
||||||
)
|
((not val) sil-count)
|
||||||
;If this sample is NOT silent, then reset the silence counter
|
(incf sample-count)
|
||||||
(if (> v thres)
|
(cond
|
||||||
(setq sil-c 0)
|
((< val threshold)
|
||||||
)
|
(setf sil-count (1+ sil-count)))
|
||||||
)
|
(t (when (> sil-count min-len)
|
||||||
)
|
(add-label sample-count srate label-position))
|
||||||
)
|
(setf sil-count 0))))
|
||||||
|
;; If long trailing silence, add final label at 'min-silence' AFTER last sound.
|
||||||
|
(when (> sil-count min-len)
|
||||||
|
(setf final-silence (- sample-count sil-count))
|
||||||
|
(add-label final-silence srate min-silence))
|
||||||
|
*labels*))
|
||||||
|
|
||||||
;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) "S")
|
|
||||||
)
|
|
||||||
|
|
||||||
;If no silence markers were found, return a message
|
;; Bug 2352: Throw error if selection too long for Nyquist.
|
||||||
(if (null l)
|
(let* ((dur (- (get '*selection* 'end)
|
||||||
(setq l (format nil (_ "No silences found. Try reducing the silence~%level and minimum silence duration.")))
|
(get '*selection* 'start)))
|
||||||
)
|
(samples (* dur *sound-srate*))
|
||||||
l
|
(max-samples (1- (power 2 31))))
|
||||||
|
(if (>= samples max-samples)
|
||||||
|
(format nil "Error.~%Selection must be less than ~a."
|
||||||
|
(format-time (/ max-samples *sound-srate*)))
|
||||||
|
;; Selection OK, so run the analyzer.
|
||||||
|
(let ((sig (reduce-srate (to-mono *track*))))
|
||||||
|
(setf *track* nil) ;free *track* from memory
|
||||||
|
(if (label-silences sig)
|
||||||
|
*labels*
|
||||||
|
(_ "No silences found.
|
||||||
|
Try reducing the silence level and
|
||||||
|
the minimum silence duration.")))))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user