I love Audacity but am new to programming in Nyquist. I have read several Tutorials at this point, at least one FAQ, and have searched this forum.
I still cannot figure this out, and I apologize if this is covered somewhere – it must be such a common question!
How does one specify operating directly on samples themselves?
I have a function I would like to specify, call it f(t). How to access the sound array values directly, and the time or sample number index?
All I see in the tutorials and documentation are various predefined APIs for various standard operations (many of which start with “snd-”), I have yet to find instructions on how to implement ideas apart from these functions. Is there not some way of doing, e.g…
(for i = 0 to [end of array]
snd _= [whatever I want it to be]
end do)
…?
Thank in advance for your help!
P.S.- Having been on forum sites before, here is a quick FAQ for this post:
Q: Why are you trying to do this?
A: For lots of reasons. I have functions I would like to “hear”; I have DSP ideas I’d like to implement.
Q: What are you really trying to do?
A: Precisely what I said above. Specify a function of time directly, or operate on specific samples directly.
Q: You know this is going to be super-slow; this is why we wrote these APIs for optimized routines.
A: Yes but I cannot find the APIs to implement what I want, yet. Slow is better than nothing.
Q: Have you read this other forum post where this issue was addressed? (link)
A: No, but thank you, I will!
Q: What version of Audacity are you using, and what platform?
A: Audacity 2.0.4 for Mac._
Generally one would avoid doing that unless it is a last resort.
There’s two main ways of doing sample-wise DSP in Nyquist. There’s the frustratingly awkward, difficult and slow way, and there’s the much easier and very slow way.
Here’s a trivial example. Note that this is only suitable for short selections (a few seconds) and is only written for mono tracks. It can be run in the Nyquist Prompt effect:
;;; Amplify by +3 dB
(setq gain (db-to-linear 3.0))
;len is a global variable for number of samples in the selection
(let* ((Samples (truncate len))
(MySoundArray (snd-fetch-array s Samples Samples)))
(do ((i 0 (1+ i)))
((= i Samples) ;test
(snd-from-array 0 *sound-srate* MySoundArray)) ;return value
(setf (aref MySoundArray i)(* gain (aref MySoundArray i)))))
If you want to run something like this on a long selection, the way to do it would be to fetch a few thousand samples in an array, process them, then store that as a sound. Then fetch the next lot of samples with the array, process them, and add that to the end of the sound. Iterate through the selection, then process the last bit and add that to the end of the sound.
Of course, for this trivial example it would be much more efficient to simply:
I’ve been meaning to write an example of this, so here’s an example:
;type process
;;; Amplify by +3 dB
(setq gain (db-to-linear 3.0))
;;; DSP function
(defun amp (a g)
(dotimes (i (length a) a)
(setf (aref a i)(* g (aref a i)))))
;;; Iterate through audio selection
;;; grabbing an array full of samples at a time.
(defun amplify (sig gain)
; len is a global variable for number of samples in the selection
(let* ((Samples (truncate len))
(alen 10000) ; array size
(it (truncate (/ len alen))) ; iterations
(remain (rem samples alen)) ; samples left over
(adur (/ alen *sound-srate*)) ; duration of alen as sound
(remaindur (/ remain *sound-srate*))
(out (s-rest 0))) ; initialise output
(do ((i 0 (1+ i))
tmpsnd
(tnext 0 (+ adur tnext)))
((= i it)) ; do until i=it
; Get the samples
(setf sndarray (snd-fetch-array sig alen alen))
; Apply the DSP
(setf sndarray (amp sndarray gain))
; Convert back to a ]temporary] sound
(setf tmpsnd (snd-from-array 0 *sound-srate* sndarray))
;;Add 'tmpsnd' to the end of 'out'
(setf out
(sim out
(at-abs tnext (cue tmpsnd)))))
;; Now do any remaining samples
(if (> remain 0)
(progn
(setf sndarray (snd-fetch-array sig remain remain))
(setf sndarray (amp sndarray gain))
(sim out
(at-abs (- dur remaindur)
(cue (snd-from-array 0 *sound-srate* sndarray)))))
out)))
; Duration in seconds
(setq dur (get-duration 1))
; 'expand' the AMPLIFY function for each channel of 's'.
(multichan-expand #'amplify s gain)
One follow-up: Has anyone written —sorry I haven’t noticed it yet – an API for specifying a mathematical equation as a function of time,
and creating/overwriting a sound signal accordingly? e.g. something where by I could send in a string to be parsed & executed?
Thanks.