working with audio samples

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

working with audio samples

Permanent link to this post Posted by shravani » Thu Feb 13, 2014 8:43 am

hello,

While accessing the audio track, I want to save the amplitude and time of samples that are above a particular threshold. How can i do that? I had thought of using structure array the way we use in C language, but structure is not supported in Nyquist.
Please help me through this.

Hoping for a reply soon.
Thanks
shravani
 
Posts: 8
Joined: Wed Jan 29, 2014 7:43 am
Operating System: Please select

Re: working with audio samples

Permanent link to this post Posted by steve » Thu Feb 13, 2014 5:38 pm

Generally with Nyquist it is better to work with "sounds" rather than with "samples".
For example, if you want all of the samples that are above +0.5 in value (-6 dB positive going peaks, you could write a loop that reads the sample values and writes the value and index into a list of (index value) pairs:
Code: Select all
(setf peak-list
  (do ((val (snd-fetch s)(snd-fetch s))
       (i 0 (1+ i))
       (values ()))
      ((not val) values)
    (if (> val 0.5)
        (push (list i val) values))))

; test it
(format t "~a samples over 0.5 found.~%Last found sample index: ~a~%~
            Last found value: ~a"
  (length peak-list)
  (first (nth 0 peak-list))
  (second (nth 0 peak-list)))

(print "done")


However, running through samples in a LISP loop is pretty slow.
A much more efficient way is to use Nyquist built-in functions and work with "sounds":
Code: Select all
(setf peak-list-as-sound (s-max s 0.5))
(format nil "Done.~%Returned ~a samples." (snd-length peak-list-as-sound ny:all))


The second code calculates a "sound" that is the maximum of 0.5 and the original sound. It is very much faster than the LISP loop (even though it is also calculating the length of the returned sound)
To see the output sound from the second code, just run:
Code: Select all
(s-max s 0.5)

This will be a bit slower because Audacity has to calculate the waveform display and write the returned data to disk, but still a lot faster and more efficient than the LISP loop.

Whether or not you can work entirely with sounds and avoid looping through samples depends on your application (which you have not described), but if it is possible to do so, work with "sounds" rather than samples.

If you really have to work directly with samples, then it is often more efficient to convert the sound to a lower sample rate, so there are less samples to loop through. See "Silence Finder" (SilenceMarker.ny in your Audacity Plug-ins folder) as an example.
9/10 questions are answered in the FREQUENTLY ASKED QUESTIONS (FAQ)
steve
Site Admin
 
Posts: 45326
Joined: Sat Dec 01, 2007 11:43 am
Operating System: Linux *buntu

Re: working with audio samples

Permanent link to this post Posted by shravani » Fri Feb 14, 2014 3:42 pm

Thanks for the code. I will surely try and execute it.

the code mentioned below is from Analyse menu - Beat Finder.
I am not understanding how it works. If you could please explain.

Code: Select all
(do ((c 0.0)
     (l NIL)
     (p T)
     (v (snd-fetch s2)))
    ((not v) l)
  (if (and p (> v thres))
      (setq l (cons (list c "B") l)))
  (setq p (< v thres))
  (setq c (+ c 0.001))
  (setq v (snd-fetch s2)))


Thanks
Last edited by shravani on Fri Apr 24, 2015 4:30 pm, edited 3 times in total.
Reason: Added code tags and line indentation
shravani
 
Posts: 8
Joined: Wed Jan 29, 2014 7:43 am
Operating System: Please select

Re: working with audio samples

Permanent link to this post Posted by steve » Fri Feb 14, 2014 4:33 pm

I've added code tags and line indentations to the code that you posted making it easier to read.
(To add code tags, click on the "Code" button above the message composing box then insert your code between the code tags like this:
Code: Select all
[code]
... code goes here ...
[/code]


The code is a "Loop". It is described in the XLisp manual here: http://www.audacity-forum.de/download/e ... ef-093.htm

After the "DO" command, there are 4 local variables set. "Local" means that they only exist within this block of code.
"C" is set to an initial value of 0.0
"l" to NIL
"p" to "true" [note that "T" or "t" is a special symbol that has the boolean value "true"]
"v" is set to the value of a sample which is read with (snd-fetch s2), where "s2" must be a "sound". http://www.cs.cmu.edu/~rbd/doc/nyquist/ ... l#index260

The next line is a test to see if the loop should continue: (not v)
and then the return value "l".

Note that it is NOT generally good practice to use single characters as variable names. It may be OK where the variable is only used within a very limited context, but code is usually easier to read, easier to maintain and easier to bug fix if concise but meaningful names are used. "l" (lower case "L") is a particularly bad name to use as in some fonts it looks like the number "1".

So the loop initially sets these values, and then continues looping until "not v", which means until "v" is NIL. "v" will be NIL when there are no more samples to fetch.

Next comes the body of the loop:

Code: Select all
(if (and p (> v thres))
    (setq l (cons (list c "B") l)))

If "p" is "true" (not NIL) and "v" is less than "thresh"
then
set "L" to (cons (list c "B") l))

(cons (list c "B") l)) adds a list containing the value of "c" and the string value "B" to the list "L"

This could be written a lot more clearly.
If we used variable names:
"time" instead of "c"
"below-thresh" instead of "p"
"Label-list" instead of "l"
"val" instead of "v"

Code: Select all
(if (and below-thresh (> val thres))
    (push (list time "B") label-list))

"push" is described here: http://www.cs.cmu.edu/~rbd/doc/nyquist/ ... #index1000

The last part of the body sets new values for p, c and v.

For what its worth, I'd probably have written it something like this:
Code: Select all
(do ((label-list ())
     (below-thresh T)
     (time 0.0 (+ time 0.001))
     (val (snd-fetch s2) (snd-fetch s2)))
    ((not val) label-list)
  (if (and below-thresh
           (> val thres))
      (push (list time "B") label-list))
  (setq below-thresh (< val thres)))


Which with lost of comments:
Code: Select all
(do ((label-list ())    ; Initialise empty list.
     (below-thresh T)   ; Initialise flag as 'true'.
     ; Initialise 'time = 0'. Add 0.001 on each loop.
     (time 0.0 (+ time 0.001))
     ; Get sample values.
     (val (snd-fetch s2) (snd-fetch s2)))
    ; Until 'val = NIL'. Reurn label-list.
    ((not val) label-list)
  ; If crossing from below to above threshold
  (if (and below-thresh
           (> val thres))
      ; Add label to list.
      (push (list time "B") label-list))
  ; is val below threshold?
  (setq below-thresh (< val thres)))
9/10 questions are answered in the FREQUENTLY ASKED QUESTIONS (FAQ)
steve
Site Admin
 
Posts: 45326
Joined: Sat Dec 01, 2007 11:43 am
Operating System: Linux *buntu

Re: working with audio samples

Permanent link to this post Posted by shravani » Sat Feb 15, 2014 7:21 am

Hello,

Thank you so much :) . The beat finder is finally understood perfectly. Here the time is incremented by .001. But when i tried to increment time by 1 or 2, the output remains same as that for 0 .001.

When the wave rises above the threshold and again after some time goes below the threshold, within that section i want to mark the max db value as the beat. I want to save that particular amplitude and the time at which it occurs. Can you please tell me the function which can be used to extract the corresponding time.

Also, another query is, where and how are the variables declared? I also want to know about the global variables.

Thanks in advance.
shravani
 
Posts: 8
Joined: Wed Jan 29, 2014 7:43 am
Operating System: Please select

Re: working with audio samples

Permanent link to this post Posted by s2v2 » Sat Feb 15, 2014 9:07 am

Hello,
I added your code to a plug in as follows :

Code: Select all
;nyquist plug-in
;version 3

;type analyze
;categories "http://audacityteam.org/namespace#OnsetDetector"
;name "Testing"
;action "Calculation"

;; Released under terms of the GNU General Public License version 2:
;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html

;control thresval "Threshold Percentage" int "" 75 5 100
        (setf peak-list
      (do ((val (snd-fetch s)(snd-fetch s))
           (i 0 (1+ i))
           (values ()))
          ((not val) values)
        (if (> val 0.5)
            (push (list i val) values))))

    ; test it
    (format t "~a samples over 0.5 found.~%Last found sample index: ~a~%~
                Last found value: ~a"
      (length peak-list)
      (first (nth 0 peak-list))
      (second (nth 0 peak-list)))

    (print "done")




But initially it gives the as "Nyquist returned value :75" (75 is the threshold value that we hve set) and it is giving following error after clicking on debug option.


error: bad argument type - #(#<Sound: #b08f9450> #<Sound: #b08f94f0>)
Function: #<Subr-SND-FETCH: #8ffbae0>
Arguments:
#(#<Sound: #b08f9450> #<Sound: #b08f94f0>)
Function: #<FSubr-DO: #8ffceb4>
Arguments:
((VAL (SND-FETCH S) (SND-FETCH S)) (I 0 (1+ I)) (VALUES NIL))
((NOT VAL) VALUES)
(IF (> VAL 0.5) (PUSH (LIST I VAL) VALUES))
Function: #<FSubr-SETF: #8ff9670>
Arguments:
PEAK-LIST
(DO ((VAL (SND-FETCH S) (SND-FETCH S)) (I 0 (1+ I)) (VALUES NIL)) ((NOT VAL) VALUES) (IF (> VAL 0.5) (PUSH (LIST I VAL) VALUES)))
1> error: unbound variable - PEAK-LIST
if continued: try evaluating symbol again
Function: #<Subr-SND-FETCH: #8ffbae0>
Arguments:
#(#<Sound: #b08f9450> #<Sound: #b08f94f0>)
Function: #<FSubr-DO: #8ffceb4>
Arguments:
((VAL (SND-FETCH S) (SND-FETCH S)) (I 0 (1+ I)) (VALUES NIL))
((NOT VAL) VALUES)
(IF (> VAL 0.5) (PUSH (LIST I VAL) VALUES))
Function: #<FSubr-SETF: #8ff9670>
Arguments:
PEAK-LIST
(DO ((VAL (SND-FETCH S) (SND-FETCH S)) (I 0 (1+ I)) (VALUES NIL)) ((NOT VAL) VALUES) (IF (> VAL 0.5) (PUSH (LIST I VAL) VALUES)))
2> "done"
"done"
2> 1>





Can you tell where i am going wrong.
Thanks
s2v2
 
Posts: 3
Joined: Sat Feb 15, 2014 7:23 am
Operating System: Please select

Re: working with audio samples

Permanent link to this post Posted by steve » Sat Feb 15, 2014 12:32 pm

Variables do not need to be declared before use. A symbol becomes bound just by setting a value for it.
For simple values you can just use SET, but the symbol must be quoted so that LISP does not try to evaluate it before it is bound.
Code: Select all
(set value 3)  ; error: unbound variable - VALUE
(print (* 2 value))

Code: Select all
(set 'value 3)  ; VALUE is quoted
(print (* 2 value)) ; returns 6


"SET QUOTED" is so commonly used that it has shorthand notation:
Code: Select all
(setq value 3)
(print (* 2 value)) ; returns 6

This is the standard way to bind a value to a symbol.


In some more complex situations we need a more powerful command than SETQ, for example if we want to set the value of an array element. In this case we use SETF [set field].
Code: Select all
(setq myarray (vector 1 2 3 4)) ; zero indexed array
(setf (aref myarray 2) 30)
(print myarray) ; returns #(1 2 30 4)



s2v2 wrote:error: bad argument type - #(#<Sound: #b08f9450> #<Sound: #b08f94f0>)

You are applying the code to a stereo track.
A stereo track is passed to Nyquist as an array of 2 sounds, bound to the symbol "S".
(snd-fetch s) tries to get the next sample value from the sound "s", but if "s" is not a sound it will fail.

See here for how to handle stereo sounds: http://wiki.audacityteam.org/wiki/Nyqui ... reo_Tracks
9/10 questions are answered in the FREQUENTLY ASKED QUESTIONS (FAQ)
steve
Site Admin
 
Posts: 45326
Joined: Sat Dec 01, 2007 11:43 am
Operating System: Linux *buntu

Re: working with audio samples

Permanent link to this post Posted by Robert J. H. » Sat Feb 15, 2014 1:27 pm

Some additional facts about "Set" and related functions.

'Setq' is widely regarded as obsolete. You can always use 'setf' since it combines 'setq' and 'setf' and uses either depending on the context.
'Set' is useful to set a global variable when a variable of the same name is within a local construct. Thus:
Code: Select all
(let (var)
   (setf var 10); local assignment
   (set 'var 8); global assignment
   (print var)); local print --> 10
(print var); global print --> 8


Another useful function for multiple assignments is 'psetq':
Code: Select all
(psetq var 5 next-var 10 message "done")
Robert J. H.
 
Posts: 1813
Joined: Thu May 31, 2012 8:33 am
Operating System: Windows 7

Re: working with audio samples

Permanent link to this post Posted by steve » Sat Feb 15, 2014 5:36 pm

Robert J. H. wrote:'Setq' is widely regarded as obsolete.

In some dialects of Lisp SETQ may be less fashionable, but "obsolete"?
Even if it were deprecated in Common Lisp (which it isn't), Nyquist is based on a relatively old version of XLisp. At some point in the future SETQ "may" be deprecated, at which point programmers should stop using it.

Personally I prefer to use SETQ when setting a variable to a value and SETF for lists, arrays, sounds and strings. By using the same scheme consistently I find it serves as a reminder and aids readability. As soon as I see SETF in my code I know that it is not a single value, whereas if I see SETQ I know that it is.

Nice tip regarding use of SET to set a global variable within a local construct, though unless writing a very large program it should be easy to avoid re-using a global variable name, so for clarity I'd prefer to use a different variable name within the local construct.
Code: Select all
(let (lvar)
   (setq lvar 10); local assignment
   (setq var 8); global assignment
   (print lvar)); local print --> 10
(print var); global print --> 8
(print lvar); error: unbound variable - LVAR
9/10 questions are answered in the FREQUENTLY ASKED QUESTIONS (FAQ)
steve
Site Admin
 
Posts: 45326
Joined: Sat Dec 01, 2007 11:43 am
Operating System: Linux *buntu

Re: working with audio samples

Permanent link to this post Posted by s2v2 » Mon Feb 17, 2014 4:46 pm

Hello,
When the wave rises above the threshold and again after some time goes below the threshold, within that section i want to mark the max db value as the beat. I want to save that particular amplitude and the time at which it occurs. Can you please tell me the function which can be used to extract the corresponding time.

Thanks in advance.
s2v2
 
Posts: 3
Joined: Sat Feb 15, 2014 7:23 am
Operating System: Please select

Next

Return to Nyquist



Who is online

Users browsing this forum: No registered users and 3 guests

cron