increasing accuracy of Silence Marker

Using Nyquist scripts in Audacity.
Post and download new plug-ins.

If you require help using Audacity, please post on the forum board relevant to your operating system:
Windows
Mac OS X
GNU/Linux and Unix-like

Re: increasing accuracy of Silence Marker

Permanent link to this post Posted by Edgar » Fri Nov 28, 2014 3:11 am

Way too slow - three minutes on a 39 minute stereo track. Also, not very useful to me; I tried your default settings then I used these settings:
Code: Select all
(setf threshold -26)  ;dB
(setf min-length 1.0) ;seconds
(setf label-text "")  ;the label text

which gave me way too many labels:
Smith.png
Smith.png (158.57 KiB) Viewed 928 times

I want point labels centered in the track. I also tried:
Code: Select all
(setf threshold -36)  ;dB
(setf min-length 2.5) ;seconds
(setf label-text "")  ;the label text

which eliminated all the false positives but missed about one third of the real track gaps.

Don't spend any more time on it for me, I have got the C++ code well in hand.
-Edgar
compiling Audacity daily
64-bit Windows 7
Edgar
Forum Crew
 
Posts: 1490
Joined: Thu Sep 03, 2009 9:13 pm
Operating System: Windows 7

Re: increasing accuracy of Silence Marker

Permanent link to this post Posted by steve » Fri Nov 28, 2014 4:00 am

Edgar wrote:Way too slow

That's why we don't do it like that - but it is extremely "accurate".

Edgar wrote:which eliminated all the false positives but missed about one third of the real track gaps.

Not the same as "accurate". To get good gap detection on real world recording, you will need noise filtering prior to the threshold detection. A simple form is bandpass filtering to reduce rumble, hiss, clicks and crackle. That will throw out any measurements that you may have made of the noise level, so you will need to compensate with your settings. You can be quite aggressive with the filtering because you are not actually going to hear the filtered sound - it's only for the detector.
9/10 questions are answered in the FREQUENTLY ASKED QUESTIONS (FAQ)
steve
Site Admin
 
Posts: 45394
Joined: Sat Dec 01, 2007 11:43 am
Operating System: Linux *buntu

Re: increasing accuracy of Silence Marker

Permanent link to this post Posted by Edgar » Fri Nov 28, 2014 4:33 am

steve wrote:To get good gap detection on real world recording, you will need noise filtering prior to the threshold detection. A simple form is bandpass filtering to reduce rumble, hiss, clicks and crackle. […]You can be quite aggressive with the filtering

OK, so I make a copy of the audio, apply aggressive filtering (as long as it never changes the length of the audio - maybe even caching absolute peak value), do something like normalizing, figure out the timestamps for my point labels, throw out the copy of the audio then create my label track immediately under the current audio track. I will need to figure out how to apply aggressive filtering in C++…

Thanks for all the help Steve!
-Edgar
compiling Audacity daily
64-bit Windows 7
Edgar
Forum Crew
 
Posts: 1490
Joined: Thu Sep 03, 2009 9:13 pm
Operating System: Windows 7

Re: increasing accuracy of Silence Marker

Permanent link to this post Posted by steve » Fri Nov 28, 2014 5:39 am

Give this one a go. It'll be a lot faster than the previous one, and it includes filtering:
Code: Select all
(setf threshold -30)  ;dB
(setf min-length 1.0) ;seconds
(setf label-text "Silence")  ;the label text

;; --------- End of user input -------------

(setf step 100) ;tweak this for performance
(setf threshold (db-to-linear threshold))



(setf s (highpass2 s 150))

(setf s
  (if (arrayp s)
      (s-max (s-abs (aref s 0))
                     (s-abs (aref s 1)))
      (s-abs s)))

(setf s
  (mult 2
    (snd-avg s step step op-average)))

(let* ((sr (snd-srate s))
       (minlen (* min-length sr))
       (labels ())
       (start 0)
       (silcount 0))
  (do ((val (snd-fetch s)(snd-fetch s))
       (count 0 (1+ count)))
      ((not val) (if (> (length labels) 0)
                     labels
                     "No silence found."))
    (cond
      ((< val threshold)
        (if (= silcount 0)
            (setf start count))
        (incf silcount))
      ((> silcount minlen)
        (push
          (list (/ (+ start count) (* 2 sr)) label-text)
          labels)
        (setf silcount 0))
      (T (setf silcount 0)))))
9/10 questions are answered in the FREQUENTLY ASKED QUESTIONS (FAQ)
steve
Site Admin
 
Posts: 45394
Joined: Sat Dec 01, 2007 11:43 am
Operating System: Linux *buntu

Re: increasing accuracy of Silence Marker

Permanent link to this post Posted by Edgar » Fri Nov 28, 2014 6:31 am

Almost perfect! I bumped the minimum length up to 2.5 (my experience has been that vinyl pressed before 1970-ish has gaps of about 5-6 seconds and thereafter a very uniform three seconds). The original effect (processing a 39 minute A+B sides stereo track) took three seconds, this one took 4.5 seconds. The original threw a number of false positives and missed about a third of the gaps. This version threw no false positives and correctly identified each track gap.

The only problem with this is that it is putting the point label .5 seconds in front of the whole gap:
Aerosmith.png
Aerosmith.png (19.4 KiB) Viewed 921 times

Close inspection will show you that the track gap is 3.470 seconds, and the created point label is .512 seconds before the beginning of the track gap. I want the label to fall in the center of the track gap.
-Edgar
compiling Audacity daily
64-bit Windows 7
Edgar
Forum Crew
 
Posts: 1490
Joined: Thu Sep 03, 2009 9:13 pm
Operating System: Windows 7

Re: increasing accuracy of Silence Marker

Permanent link to this post Posted by Edgar » Fri Nov 28, 2014 11:09 pm

It looks to me like:
Code: Select all
(setf s
  (mult 2
    (snd-avg s step step op-average)))

is averaging the left & right channels and then doubling that value. I would like to try just summing the two channels (which should be quicker and result in the same mathematical value). I would also like to try twice the value of the loudest channel but I have no idea how to express either of those in Nyquist. What I am looking for is to eliminate what few false positives I have seen. They seemed predominantly to be when one channel is silent and the other channel has a musician "sneaking in".
-Edgar
compiling Audacity daily
64-bit Windows 7
Edgar
Forum Crew
 
Posts: 1490
Joined: Thu Sep 03, 2009 9:13 pm
Operating System: Windows 7

Re: increasing accuracy of Silence Marker

Permanent link to this post Posted by Robert J. H. » Sat Nov 29, 2014 1:20 am

Edgar wrote:It looks to me like:
Code: Select all
(setf s
  (mult 2
    (snd-avg s step step op-average)))

is averaging the left & right channels and then doubling that value. I would like to try just summing the two channels (which should be quicker and result in the same mathematical value). I would also like to try twice the value of the loudest channel but I have no idea how to express either of those in Nyquist. What I am looking for is to eliminate what few false positives I have seen. They seemed predominantly to be when one channel is silent and the other channel has a musician "sneaking in".


No, the code snippet works only with a single channel.
Step refers to samples, i.e. step samples are averaged and then the buffer advances step samples to average those again.
In other words, the chunk size (first step) is equal to the hop size (second step in 'snd-avg'.
The resulting sample rate is the track's sample rate divided by (2nd) step.

You can get the per-sample maximum of the two channels like this:
Code: Select all
(setf s (s-max (s-abs (aref s 0)) (s-abs(aref s 1))))

Does this help?
Robert J. H.
 
Posts: 1813
Joined: Thu May 31, 2012 8:33 am
Operating System: Windows 7

Re: increasing accuracy of Silence Marker

Permanent link to this post Posted by Edgar » Sat Nov 29, 2014 10:05 pm

Robert J. H. wrote:You can get the per-sample maximum of the two channels like this:
Code: Select all
(setf s (s-max (s-abs (aref s 0)) (s-abs(aref s 1))))

Does this help?

Not quite what I need, I either need the sum of the individual channel absolute values or twice the maximum channel's absolute value. Given:
Code: Select all
T is the target - anything at or below this is "silence"
R = AbsoluteValue (channel 0 sample)
L = AbsoluteValue (channel 1 sample)
B = R + L
M = 2 * (the maximum of R and L - whichever is greater)

I want to try:
Code: Select all
If B <= T Then the sample is silence

and I also want to try:
Code: Select all
If M <= T Then the sample is silence

and I might even try:
Code: Select all
If (B <= T) Or (M <= T) Then the sample is silence

I have attached the code that I am currently using as a Nyquist effect file (note that it has no GUI). In Steve's original implementation the label was always placed before the actual region of silence so I must pad the label location. Unfortunately, I do not know how to calculate the center of the silence and place the label there (which is what I want). The current padding is almost good enough when tested on "modern" (created since 1970) vinyl which has fairly uniform 2.8-3.0 second track gaps. I processed all 23 of the recordings I had in the can - about 400 tracks - with about 5 false positives, about 10 missed gaps and all other labels falling within the track gap. The missed gaps were invariably caused by "clicks" within the track gap - I think that even a very rudimentary click repair would probably suppress these enough to eliminate all of these missed gaps but I have done no experimentation.
Attachments
AlbumGapMarker.ny
(1.38 KiB) Downloaded 40 times
-Edgar
compiling Audacity daily
64-bit Windows 7
Edgar
Forum Crew
 
Posts: 1490
Joined: Thu Sep 03, 2009 9:13 pm
Operating System: Windows 7

Re: increasing accuracy of Silence Marker

Permanent link to this post Posted by Robert J. H. » Sun Nov 30, 2014 1:07 pm

As far as I can see, the code works correctly and takes the maximum value of the two channels.
I'm not sure why you want to multiply by 2. You're aware that the -30 dB threshold is afterwards -26 dB?
Instead of the snd-avg function you can take the rms function instead.
Code: Select all
(rms s >target-samplerate<)

where target samplerate means the track's samplerate divided by the step size:
Code: Select all
(rms s (/ *sound-srate* step) step)

Again, the secondary step is the hop size and is optional. Use it only if you want a smaller value--that smoothens the curve while preserving the accuracy.
Also, there's no lowpassfilter applied yet, this would of course reduce spurious clicks as well.

I gather you want to apply it in a chain, don't you?
Otherwise, it would be easier to simply tell the program how many tracks there are on the LP and to let it search for those x longest gaps.
Another improvement is to use the zero crossing rate as well.
This is e.g. done in GSM standards to detect voice activity.
For instance:
A song might fade out with a droning bass tone, the amplitude might soon be under the threshold but the gap will actually be longer than it should. Placing the point label in the center could result in splitting the fading tone.
The zero crossing rate for the bass tone is pretty low while it is quite high for silence (or rather white or coloured noise).
The gap would be calculated from the intersection of the amplitude below the threshold and a ZCR above a secondary threshold (ee.g. 6000 Hz).
As for the center:
the last cond expression searches for pauses that are longer than minlen.
However, once this len is reached, the counting begins again. Thus the center would always be half of the minlen.

the following code counts til the end:
Code: Select all
    (cond
      ((> val threshold)
        (when  (and (/= silcount 0) (> silcount minlen))
            (setf start (- count (/ silcount 2)))
            (push
              (list (* (recip sr) start) label-text)
              labels)
        (setf  silcount 0)))
      ((<= val threshold)
        (incf silcount)))))


I would personally push the point nearer to the next song since fade-outs are much more common than fade-ins.
Again, the Zcr could be helpful for those overlong gaps.
Robert J. H.
 
Posts: 1813
Joined: Thu May 31, 2012 8:33 am
Operating System: Windows 7

Re: increasing accuracy of Silence Marker

Permanent link to this post Posted by steve » Sun Nov 30, 2014 1:53 pm

There seems to be some confusion about the code that I posted, so here is a heavily commented version that describes what it does:
Code: Select all
(setf threshold -30)  ;dB
(setf min-length 1.0) ;seconds
(setf label-text "Silence")  ;the label text

;; --------- End of user input -------------

;;"step" is the number of samples taken when
;;we are 'averaging' the sound in (snd-avg)
(setf step 100) ;tweak this for performance

;; convert "threshold" from db to linear.
(setf threshold (db-to-linear threshold))


;; apply second order butterworth highpass filter
;; corner frequency = 150 Hz.
(setf s (highpass2 s 150))

;; If the sound is stereo (an array), take the maximum
;; absolute sample value of the two channels.
;; Thus, if one channel has a sample value of -0.8 and
;; the other channel has a sample value of +0.5, the resulting
;; sample will be +0.8 [maximum of the absolute values].
;; For mono sounds, "S" becomes the absolute value of each
;; sample, thus a sample value of -0.5 becomes +0.5
(setf s
  (if (arrayp s)
      (s-max (s-abs (aref s 0))
                     (s-abs (aref s 1)))
      (s-abs s)))


;; find the average of "step" number of samples [100], then
;; step forward by "step" number of samples to the next
;; "step" number of samples. Thus, each 100 sample produce 1
;; sample that is an average of 100 sequential samples.
;; The average value will obviously be a lot lower than the peak
;; value, so it is then multiplied by 2 (+6 dB) as a ballpark
;; figure to make the output sound have roughly the same amplitude
;; as the input sound.
;; Taking the average value will substantially reduce the effect
;; of snap crackle and pop. [in effect, a lowpass filter].
(setf s
  (mult 2
    (snd-avg s step step op-average)))

;; initialise some variable:
(let* ((sr (snd-srate s)) ;the sample rate
       (minlen (* min-length sr)) ;the "minimum length" in samples
       (labels ()) ;an empty list for our labels
       (start 0) ;the start time [in samples] of the label
       (silcount 0)) ;the number of sequential "silent" samples
  ;; The main "while" loop.
  (do ((val (snd-fetch s)(snd-fetch s)) ; fetch the next sample
       (count 0 (1+ count))) ;increment "count"
      ; continue until no more samples [val=NIL]
      ; and return the labels, or an error.
      ((not val) (if (> (length labels) 0)
                     labels
                     "No silence found."))
    (cond
      ((< val threshold) ;value is below threshold
        (if (= silcount 0) ;the first "silent" sample
            (setf start count)) ;set the label start to the current sample number
        (incf silcount)) ;increment the count of sequential silent samples
      ;; OR [so we must be above the threshold]
      ((> silcount minlen) ;the count of silent samples is greater than "minlen"
        (push ;add a label to the list "labels"
          (list (/ (+ start count) (* 2 sr)) label-text)
          labels)
        (setf silcount 0)) ;reset "silcount" to zero
      ;; OR [so we are above the threshold, but
      ;; we don't have a long enough series of silent samples
      (T (setf silcount 0))))) ;ensure that the silent sample counter is zero.

9/10 questions are answered in the FREQUENTLY ASKED QUESTIONS (FAQ)
steve
Site Admin
 
Posts: 45394
Joined: Sat Dec 01, 2007 11:43 am
Operating System: Linux *buntu

PreviousNext

Return to Nyquist



Who is online

Users browsing this forum: No registered users and 5 guests