|
| related documentation: | snd.html | extsnd.html | grfsnd.html | sndclm.html | sndlib.html | libxm.html | fm.html | index.html |
This file contains notes on the Scheme, Ruby, and Forth files included with Snd. To use any of these files, load them:
Scheme: (load "dsp.scm") or (load-from-path "dsp.scm") Ruby: load "dsp.rb" Forth: "dsp.fs" file-eval
To start Snd with
the file already loaded, snd -l v.scm, or put the load statement in your initialization file.
For help with Forth and Snd/CLM, see the Forth documentation section "Snd, CLM, and Fth".
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
make-butterworth-lowpass (order fcut)make-butterworth-highpass (order fcut)make-butterworth-bandpass (order flo fhi)make-butterworth-bandstop (order flo fhi)make-chebyshev-lowpass (order fcut (ripple-dB 1.0))make-chebyshev-highpass (order fcut (ripple-dB 1.0))make-chebyshev-bandpass (order flo fhi (ripple-dB 1.0))make-chebyshev-bandstop (order flo fhi (ripple-dB 1.0))make-inverse-chebyshev-lowpass (order fcut (loss-dB 60.0))make-inverse-chebyshev-highpass (order fcut (loss-dB 60.0))make-inverse-chebyshev-bandpass (order flo fhi (loss-dB 60.0))make-inverse-chebyshev-bandstop (order flo fhi (loss-dB 60.0))make-bessel-lowpass (order fcut)make-bessel-highpass (order fcut)make-bessel-bandpass (order flo fh)make-bessel-bandstop (order flo fh)make-elliptic-lowpass (order fcut (ripple-dB 1.0) (loss-dB 60.0))make-elliptic-highpass (order fcut (ripple-dB 1.0) (loss-dB 60.0))make-elliptic-bandpass (order flo fhi (ripple-dB 1.0) (loss-dB 60.0))make-elliptic-bandstop (order flo fhi (ripple-dB 1.0) (loss-dB 60.0)) |
;; fcut = cutoff frequency in terms of srate = 1.0,
;; flo = low freq of band, fhi = high freq of band
|
analog-filter.scm has the usual IIR filters: Butterworth, Chebyshev, inverse Chebyshev, Bessel, and Elliptic filters in lowpass, highpass, bandpass, and bandstop versions. Each of the lowpass and highpass "make" functions returns a filter generator, whereas the bandstop and bandpass make functions return a function of one argument, the current input (the filter generators are built-in in these cases). The filter order should be an even number; very high orders can cause numerical disaster! If you want to push these guys, be sure to build Snd with the --with-doubles configuration switch. The elliptic filters depend on GSL, so you'll also need GSL (Snd's configure script includes it by default, if possible).
(let* ((flt (make-elliptic-lowpass 8 .1))) ; 8th order elliptic with cutoff at .1 * srate
(map-channel flt)) ; flt is a clm filter generator
One quick way to see the frequency response of your filter is to create a sound that sweeps a sinewave upward in frequency, run it through the filter, then view the entire sound, treating the x axis as frequency in terms of srate = 1.0 (for convenience):
(define (filter-sweep flt chan)
(let ((phase 0.0)
(freq 0.0)
(incr (/ (* 2 pi) 44100.0))
(samps (seconds->samples 0.5)))
(do ((i 0 (1+ i)))
((= i samps))
(let ((sval (* .8 (sin phase))))
(set! phase (+ phase freq))
(set! freq (+ freq incr))
(out-any i (flt sval) chan)))))
(with-sound (:channels 5 :output "test.snd")
(filter-sweep (make-butterworth-lowpass 8 .1) 0)
(filter-sweep (make-bessel-lowpass 8 .1) 1)
(filter-sweep (make-chebyshev-lowpass 8 .1) 2)
(filter-sweep (make-inverse-chebyshev-lowpass 8 .1) 3)
(filter-sweep (make-elliptic-lowpass 8 .1) 4))
|
|
see also: dsp examp moog maxf prc95 graphEq clm |
|
People paint birds; why not bird songs? Check out a Hermit Thrush song down 2 or 3 octaves and slowed down as well (via
granular synthesis, for example) — incredibly beautiful 2-part microtonal counterpoint.
animals.scm contains several synthesized animal sounds: frogs, birds, insects, and one mammal.
To hear them all, (calling-all-animals).
|
Back in 1980, I wanted some bird songs for "Colony", but my stabs at a fake bird song were completely unconvincing. So I went to my battered bird book (Robbins, Bruun, Zim, Singer "Birds of North America" Golden Press, NY 1966) which had sonograms of lots of bird songs. Unfortunately, the graphs were so tiny that I could barely read them:
|
Lincoln's Sparrow approximately original size, but blurrier due to incredibly bad scanner software. |
Graphs like this became bird.scm. It surprised me that the synthetic song could sound good even with just a sinewave and a couple sketchy envelopes. But squawks and screeches were harder. 27 years later, I tackled animal sounds again, but now using Snd and some extremely high quality recordings, mainly from Cornell. It's not that hard to match some animal sounds; perhaps someone would like to see the steps I took to match a Hairy Woodpecker call (hairy-woodpecker in animals.scm).
Open the Hairy Woodpecker. Find a squawk that seems within reach, and select it.
Zoom onto the first channel (the vertical slider on the right — we don't care about stereo here), and center the squawk in the time domain window (C-x v). Get the selection duration in seconds. Start the envelope editor dialog (under the Edit menu). Choose "selection" and "wave" in that dialog.
Since the goal is a CLM instrument that synthesizes this sound, get a "blank bird", and fill in the duration.
(definstrument (hairy-woodpecker beg amp)
(let* ((start (seconds->samples beg))
(dur 0.08) ; filled in from the selection duration
(stop (+ start (seconds->samples dur)))
(ampf (make-env ' ; left blank for the moment
:duration dur :scaler amp))
(gen1 (make-oscil))
(frqf (make-env ' ; ditto
:duration dur :scaler (hz->radians 1.0))))
(run
(lambda ()
(do ((i start (1+ i)))
((= i stop))
(outa i (* (env ampf) ; just a first guess at the synthesis algorithm
(oscil gen1 (env frqf)))))))))
Now to get an amplitude envelope, set the y axis limits to be from 0.0 to the selection maxamp:
(set! (y-bounds (selected-sound) (selected-channel)) (list 0 (selection-maxamp)))
I have this action bound to the "m" key in my ~/.snd initialization file. The change is reflected in the envelope editor. We can draw the amplitude envelope simply by approximating the shape. Call it "hairy-amp".
Now go to the listener and get the value of hairy-amp as a list of breakpoints. I usually use this function to get the breakpoints:
>(define (clean-string e)
(string-concatenate (append (list "(")
(map (lambda (n) (format #f "~,3F " n)) e)
(list ")"))))
#<unspecified>
>(clean-string hairy-amp)
"(0.000 0.000 0.099 0.188 0.152 0.148 0.211 0.558 0.242 0.267 0.278 0.519 0.434 0.472 0.527 0.543 0.612 0.479
0.792 0.941 0.831 0.523 0.854 1.000 0.913 0.422 0.927 0.200 0.946 0.430 0.971 0.304 1.000 0.000 )"
Plug this list into the bird instrument (ampf). Now switch to the FFT view (click "f" and "w"), open the FFT dialog (Options:Transform), choose Blackman10 window, sonogram, 512. In the color dialog, choose the "jet" colormap. In the envelope editor, switch from "amp" to "flt", and resize the dialog window so that its graph fits the displayed spectrum. For fast moving sounds, it's important to align the amp and freq envelopes exactly, but the FFT delays or blurs stuff, so mess with the graph placement until the amplitude envelope and the spectrum match (bright spots at loud spots and so on). I bind the arrow keys to precision movements for this reason (in my ~/.snd file, see move-one-pixel).
Reset the envelope editor (to erase the amp envelope), and press "flt" and "wave" again. Now zoom in to the main spectral component (drag the FFT y axis up), and trace out the frequency curve in the envelope editor. Call it hairy-freq, and get the list of breakpoints as before in the listener. Plug that into the bird instrument. The "scaler" for the frequency envelope is the top of the FFT graph in Hz; it is 10KHz in this case.
(definstrument (hairy-woodpecker beg amp)
(let* ((start (seconds->samples beg))
(dur 0.08)
(stop (+ start (seconds->samples dur)))
(ampf (make-env '(0.000 0.000 0.099 0.188 0.152 0.148 0.211 0.558 0.242 0.267 0.278 0.519 0.434 0.472
0.527 0.543 0.612 0.479 0.792 0.941 0.831 0.523 0.854 1.000 0.913 0.422 0.927 0.200
0.946 0.430 0.971 0.304 1.000 0.000 )
:duration dur :scaler amp))
(frqf (make-env '(0.000 0.180 0.056 0.213 0.135 0.241 0.167 0.305 0.191 0.396 0.212 0.402 0.242 0.485
0.288 0.506 0.390 0.524 0.509 0.530 0.637 0.537 0.732 0.530 0.770 0.503 0.808 0.503
0.826 0.427 0.848 0.366 0.889 0.345 0.913 0.232 1.000 0.198)
:duration dur :scaler (hz->radians 10000.0)))
(gen1 (make-oscil)))
(run
(lambda ()
(do ((i start (1+ i)))
((= i stop))
(outa i (* (env ampf)
(oscil gen1 (env frqf)))))))))
This squawk has more than one component (it is not just a sine wave), and the components follow more or less the same amplitude envelope (so we can use polywave). Go to the "single transform" view (in the Transform dialog), make the FFT size bigger, move the time domain window into about the middle of the call, and get some estimate of the number of components and their relative amplitudes (concentrating for now on the steady state). Change the "make-oscil" to "make-polywave" and give some first stab at the steady-state spectrum:
... (gen1 (make-polywave :partials (list 1 .9 2 .1 3 .01))) ... (polywave gen1 (env frqf)) ...
Load ws.scm, load the current woodpecker code, and
listen to the squawk: (with-sound (:play #t) (hairy-woodpecker 0.0 0.5)).
Not terrible. If it's cut off during playback, add a dummy silent call to the end:
(with-sound (:play #t) (hairy-woodpecker 0.0 0.5) (hairy-woodpecker 0.5 0.0)).
We're happy at this stage if it's in the right ballpark.
The attack and decay sections need work. Returning to either FFT view, it's clear there's a set of components moving together at half the steady state frequency, so add another polywave with its own amplitude envelope:
(definstrument (hairy-woodpecker beg amp)
(let* ((start (seconds->samples beg))
(dur 0.08)
(stop (+ start (seconds->samples dur)))
(ampf (make-env '(0.000 0.000 0.099 0.188 0.152 0.148 0.211 0.558 0.242 0.267 0.278 0.519 0.434 0.472
0.527 0.543 0.612 0.479 0.792 0.941 0.831 0.523 0.854 1.000 0.913 0.422 0.927 0.200
0.946 0.430 0.971 0.304 1.000 0.000 )
:duration dur :scaler amp))
(frqf (make-env '(0.000 0.180 0.056 0.213 0.135 0.241 0.167 0.305 0.191 0.396 0.212 0.402 0.242 0.485
0.288 0.506 0.390 0.524 0.509 0.530 0.637 0.537 0.732 0.530 0.770 0.503 0.808 0.503
0.826 0.427 0.848 0.366 0.889 0.345 0.913 0.232 1.000 0.198)
:duration dur :scaler (hz->radians 10000.0)))
(gen1 (make-polywave :partials (list 1 .9 2 .09 3 .01)))
(gen2 (make-polywave :partials (list 1 .2 2 .1 3 .1 4 .1 5 .1 6 .05 7 .01))) ; attack and decay
(ampf2 (make-env '(0 1 .3 1 .4 0 .75 0 .8 1 1 1) :duration dur :scaler 1.0))) ; its amplitude
(run
(lambda ()
(do ((i start (1+ i)))
((= i stop))
(let ((frq (env frqf)))
(outa i (* (env ampf)
(+ (polywave gen1 frq)
(* (env ampf2)
(polywave gen2 (* 0.5 frq))))))))))))
Now patience is the key. Use the speed control to slow playback down by an octave or two. (Perhaps the frequency envelope should end at a higher point?) Keep tweaking the envelopes and spectral amplitudes until it sounds right! Total elapsed time? Two or three hours probably.
animals.scm has all the functions, key bindings, and dialog variable settings mentioned here. They can save you a ton of time.
see also: birds |
|
auto-save ()cancel-auto-save () |
The auto-save code sets up a background process that checks periodically for
unsaved edits, and if any are found it saves them in a temporary file (the name is the base file name enclosed in "#...#" and placed in
the temp-dir directory).
The time between checks
is set by the variable auto-save-interval which defaults to 60.0 seconds.
To start auto-saving, (load "autosave.scm"). Thereafter (cancel-auto-save)
stops autosaving, and (auto-save) restarts it.
|
bess.scm is a Scheme file that can be run either as a
Guile script (independent of Snd) or loaded into Snd.
As a Guile script,
it loads sndlib and xmlib, and creates its own application windows.
In Snd, it creates a new dialog.
In either case, it opens the DAC, puts up a bunch of scale widgets, and starts two CLM oscils doing
frequency modulation in semi-real-time (how "real-time" it is depends on your
audio setup).
Michael Scholz has contributed a Ruby translation of this with many improvements:
bess.rb. To use bess.scm as a script, you may need to make it executable: chmod 777 bess.scm.
If you get "command not found", you need to edit the path to guile in the source.
|
;; bess opens the DAC and continuously sends the following:
(* amp
(oscil carosc
(+ (hz->radians frequency)
(* index (oscil modosc
(hz->radians (* ratio frequency)))))))
|
bess1.scm and bess1.rb are scripts (independent of Snd), similar to bess.scm and bess.rb, that give you real-time GUI-based control over the fm-violin while it cycles around in a simple compositional algorithm. Both were written by Michael Scholz, based on CLM's bess5.cl and rt.lisp.
see also: fm |
|
bird (start dur frequency freqskew amplitude freq-envelope amp-envelope)bigbird (start dur frequency freqskew amplitude freq-envelope amp-envelope partials)one-bird (beg maxdur func birdname)make-birds (:optional (output-file "test.snd")) |
bird.scm is a translation of the Sambox/CLM bird songs. The two instruments set
up a simple sine wave (bird) and simple waveshaping synthesis (bigbird). Use a
low-pass filter for distance effects (a bird song sounds really silly
reverberated). All the real information is in the amplitude and frequency
envelopes. These were transcribed from sonograms found in some bird guides and articles from
the Cornell Ornithology Lab.
Many of these birds were used in "Colony". To hear all the
birds, (make-birds). This writes the sequence out as "test.snd" using with-sound.
Waveshaping is described in Le Brun, "Digital Waveshaping Synthesis", JAES 1979 April, vol 27, no 4, p250.
The lines
... (coeffs (partials->polynomial (normalize-partials partials))) ... (polynomial coeffs (oscil os (env gls-env))))))
setup and run the waveshaping synthesis (in this case it's just a fast additive synthesis). partials->polynomial calculates the Chebyshev polynomial coefficients given the desired spectrum; the spectrum then results from driving that polynomial with an oscillator. Besides the bird guides, there are now numerous recordings of birds that can be turned into sonograms and transcribed as envelopes. sndclm.html has the code for the bird instrument in several languages.
see also: animals |
|
This file provides a set of noise reduction functions packaged up in:
clean-channel (:optional snd chn)clean-sound (:optional snd) |
clean-channel tries to fix up clicks, pops, hum, DC offset, clipped portions, and hiss using a variety of functions from dsp.scm. The final low-pass filter is relatively conservative (that is, it's not a very intense filter), so you may want to run another filter over the data after calling clean-channel.
|
There is no built-in noise reduction function in Snd. I believe the most common such function is some variant of Perry Cook's Scrubber program (see anoi in clm-ins.scm or fft-squelch in examp.scm). Secondary tricks involve smoothing functions similar to smooth-channel, and enveloping to silence stuff between tracks, and so on. clean-channel came about when I blithely offered to clean up some recorded telephone conversations. The first step was to find the clipped locations (where the conversation was accidentally over-recorded). I did this first because there were places in the recordings where the DC offset was huge, causing clipping in a signal that would otherwise have been safe. I hoped to reconstruct the signal at the clipped points, but these would be hard to find once the DC was removed. A quick check:
(count-matches (lambda (y) (or (> y .9999) (< y -.9999))))
returned 5437 (in 18 minutes of sound). That seemed high, and I thought "maybe those are just one sample clicks that can easily be smoothed over", so
(define* (count-clips :optional snd chn)
(let ((y0 0.0))
(count-matches
(lambda (y) (let ((val (and (or (> y0 .9999) (< y0 -.9999))
(or (> y .9999) (< y -.9999)))))
(set! y0 y)
val))
0 snd chn)))
|
But this returned 4768! I made a list of clipped portions (this function has at least one bug, but I plowed past it — no time for perfection...):
(define* (list-clips :optional snd chn)
(let* ((max-clips (count-clips snd chn))
(clip-data (make-vector (* 2 max-clips) 0))
(clip-ctr 0)
(clip-beg 0)
(clip-end 0)
(clip-max-len 0)
(in-clip #f)
(samp 0))
(scan-channel
(lambda (y)
(if (or (> y .9999) (< y -.9999))
(if in-clip
(set! clip-end samp)
(begin
(set! in-clip #t)
(set! clip-beg samp)
(set! clip-end samp)))
(if in-clip
(begin
(set! in-clip #f)
(vector-set! clip-data clip-ctr clip-beg)
(vector-set! clip-data (1+ clip-ctr) clip-end)
(set! clip-max-len (max clip-max-len (1+ (- clip-end clip-beg))))
(set! clip-ctr (+ clip-ctr 2)))))
(set! samp (1+ samp))
#f)) ; make sure scan doesn't quit prematurely
(list clip-ctr clip-max-len clip-data)))
|
which returned a vector of 669 clipped portions, the worst being 42 samples long! I saved that data in a separate file, just in case of disaster:
(with-output-to-file "clips" (display (list-clips)))
I decided to try to reconstruct the clipped portions before filtering them. This produced sample values outside -1.0 to 1.0, so I reset the graph y bounds:
(set! (y-bounds) (list -1.5 1.5))
Now to conjure up a plausible sine wave between the clip begin and end points. (This is also "just-good-enough" software).
(if (not (defined? 'pi)) (define pi 3.141592653589793))
(define (fix-clip clip-beg-1 clip-end-1)
(if (> clip-end-1 clip-beg-1)
(let* ((dur (1+ (- clip-end-1 clip-beg-1)))
(samps (channel->vct (- clip-beg-1 4) (+ dur 9)))
(clip-beg 3)
(clip-end (+ dur 4)))
(let ((samp0 (vct-ref samps clip-beg))
(samp1 (vct-ref samps clip-end)))
(if (or (> samp0 .99) (< samp0 -.99))
(begin
;; weird! some of the clipped passages have "knees"
;; this looks nuts, but no time to scratch my head
(set! clip-beg (1- clip-beg))
(set! samp0 (vct-ref samps clip-beg))
(if (or (> samp0 .99) (< samp0 -.99))
(begin
(set! clip-beg (1- clip-beg))
(set! samp0 (vct-ref samps clip-beg))))))
(if (or (> samp1 .99) (< samp1 -.99))
(begin
(set! clip-end (1+ clip-end))
(set! samp1 (vct-ref samps clip-end))
(if (or (> samp1 .99) (< samp1 -.99))
(begin
(set! clip-end (1+ clip-end))
(set! samp1 (vct-ref samps clip-end))))))
;; now we have semi-plausible bounds
;; make sine dependent on rate of change of current
(let* ((samp00 (vct-ref samps (1- clip-beg)))
(samp11 (vct-ref samps (1+ clip-end)))
(dist (- clip-end clip-beg))
(incr (/ pi dist))
(amp (* .125 (+ (abs (- samp0 samp00)) (abs (- samp1 samp11))) dist)))
(if (> samp0 0.0)
;; clipped at 1.0
(do ((i (1+ clip-beg) (1+ i))
(angle incr (+ angle incr)))
((= i clip-end))
(vct-set! samps i (+ 1.0 (* amp (sin angle)))))
;; clipped at -1.0
(do ((i (1+ clip-beg) (1+ i))
(angle incr (+ angle incr)))
((= i clip-end))
(vct-set! samps i (- -1.0 (* amp (sin angle))))))
(vct->channel samps (- clip-beg-1 4))))
#t) ; return values so I can tell when I hit a 1-sample section during testing
#f))
(define (fix-it n)
;; turn off graphics and fix all the clipped sections
(set! (squelch-update) #t)
(do ((i 0 (1+ i)))
((or (= i n) (c-g?)))
;; "clips" here is a list form of the earlier vector of clip locations
(fix-clip (list-ref clips (* i 2))
(list-ref clips (1+ (* i 2)))))
(set! (squelch-update) #f))
(fix-it 669)
|
This produced 418 edits, with a maxamp of 2.26. So scale it back down:
(scale-to .9).
Next I ran some large ffts to see what sort of overall spectrum I had:
(set! (transform-size) (expt 2 23)).
This showed a massive DC component, and numerous harmonics of 60 Hz.
I decided to get rid of the portions that were clearly noise. Since I was dealing with
telephone recordings, I assumed anything under 40 Hz or above
4000 Hz was extraneous:
(define* (notch-out-rumble-and-hiss :optional snd chn) (let* ((cur-srate (exact->inexact (srate snd)))) (filter-sound (list 0.0 0.0 ; get rid of DC (/ 80.0 cur-srate) 0.0 ; get rid of anything under 40 Hz (1.0=srate/2 here) (/ 90.0 cur-srate) 1.0 ; now the passband (/ 7000.0 cur-srate) 1.0 (/ 8000.0 cur-srate) 0.0 ; end passband (40..4000) 1.0 0.0) ; get rid of some of the hiss ;; since I'm assuming the minimum band is 10 Hz here, ;; cur-srate/10 rounded up to next power of 2 seems a safe filter size ;; filter-sound will actually use overlap-add convolution in this case (inexact->exact (expt 2 (ceiling (/ (log (/ cur-srate 10.0)) (log 2.0))))) snd chn))) (notch-out-rumble-and-hiss) |
By now it was obvious I needed a simple way to play portions of the sound before and after an edit, sometimes with a tracking cursor. So I bound a few keys:
(define (play-from-cursor current) (play (cursor) #f #f #f #f (if current #f (1- (edit-position))))) (define (play-from-cursor-with-tracking current) ;; patterned after pfc in extsnd.html (let ((old-tracking (with-tracking-cursor))) (set! (with-tracking-cursor) #t) (add-hook! stop-playing-hook (lambda (snd) (set! (with-tracking-cursor) old-tracking))) (play (cursor) #f #f #f #f (if current #f (1- (edit-position)))))) (bind-key #\p 0 (lambda () "play from cursor" (play-from-cursor #t) keyboard-no-action)) (bind-key #\P 0 (lambda () "play previous from cursor" (play-from-cursor #f) keyboard-no-action)) (bind-key #\p 4 (lambda () "play from cursor with tracking" (play-from-cursor-with-tracking #t) keyboard-no-action)) |
In words, if the mouse is in the channel graph, 'p' plays from the cursor, 'P' plays the previous version from the cursor, and 'C-p' plays from the cursor with a "tracking cursor". In several of the sections (the overall sound consisted of a couple dozen separate conversations), there was a loud mid-range tone. To figure out what its component frequencies were, I FFT'd a portion containing only that noise and got this spectrum (plus a zillion other peaks that didn't look interesting):
((425 .05) (450 .01) (546 .02) (667 .01) (789 .034) (910 .032) (470 .01))
To hear that, I passed this list to play-sines:
(play-sines '((425 .05) (450 .01) (546 .02) (667 .01) (789 .034) (910 .032) (470 .01)))
And to my surprise, the result was close to the main portion of the hum.
So now to notch out those frequencies,
and see what is left: (notch-sound (list 425 450 470 546 667 789 910) #f 1 10).
This erased most of the hum, but it also
changed the timbre of the voices which wasn't acceptable.
I goofed around with the notch-width and filter-size parameters, looking
for something that that would still do the trick without removing
the personal side of the voices, but in only a few cases was the result
usable. What was being said was not very important, but the
individual characteristics of each voice were.
The next step was to take out noisy sections between snippets, mostly
using (env-selection '(0 1 1 0 10 0 11 1))
and equalizing each snippet, more or less, with scale-selection-by.
There were a few "you-are-being-recorded" beeps which I deleted (via the Edit
menu delete selection option).
In some of the conversations,
between sections of speech the background hum would gradually increase, then
the voice would abruptly start with a large peak amplitude. These
were fixed mostly with small-section scale-by's and envelopes.
In the female voice sections, it seemed to help to:
(filter-selection '(0 0 .01 0 .02 1 1 1) 1024)
which got rid of some of the rumble without noticeably affecting
the vocal timbre.
|
|
These are instruments from CLM translated for use in Snd. All expect to be called within with-sound or some equivalent environment. This set of instruments is a bit of a grab-bag; some are just examples of synthesis techniques; a few others are historical, rather than useful. If I were using, for example, the fm trumpet, I'd remove all the attack and decay parameters, moving that up a level to Common Music or whoever calls the trumpet, and combine several other parameters to reflect the desired output, rather than the details of the algorithm; 30 parameters could be reduced to less than 10, and the resulting instrument would be much easier to use. But, it is an historical artifact, so I'm reluctant to change it.
To try out any of these instruments, start Snd, set optimization to 6, load ws.scm and clm-ins.scm, then simply paste the with-sound call into the listener. It will automatically write the new sound file and open it in Snd.
anoi (file start dur :optional (fftsize 128) (amp-scaler 1.0) (r 6.28))
| ||||
anoi is a stab at noise reduction
based on Perry Cook's Scrubber.m. It tracks an on-going average spectrum, then tries
to squelch that, obviously aimed at reducing background noise in an intermittent signal.
(with-sound () (anoi "now.snd" 0 2))
| ||||
attract (beg dur amp c)
| ||||
attract is a translation to CLM of an instrument developed by James McCartney (CMJ vol 21 no 3 p 6),
based on a "chaotic" equation.
'c' should be between 1 and 10 or thereabouts.
(with-sound () (attract 0 1 .1 1) (attract 1 1 .1 5))
| ||||
bes-fm (beg dur freq amp ratio index)
| ||||
bes-fm is J1(J1): (bes-j1 (* index (bes-j1 phase))); it uses the Bessel functions where FM uses sinusoids. J0 is also good in this context,
and the few other Jn options that I've tried were ok.
Scheme: (with-sound () (bes-fm 0 1 440 10.0 1.0 4.0)) Ruby: with_sound() do bes_fm(0, 0.5, 440, 5, 1, 8) endSo why does this work? My "back-of-the-envelope" guess is that the Bessel functions are basically a bump at the start followed by a decaying sinusoid, so the bump gives us a percussive attack, and the damped sinusoid gives us a dynamic spectrum, mimicking FM more or less. The Bessel functions I0, Jn, and Yn are built-in; Kn and In are implemented in Scheme in snd-test.scm. See bess and friends for many more examples. | ||||
canter (beg dur freq amp ...)
| ||||
canter is half of a bagpipe instrument developed by Peter Commons (the other portion is drone below).
The (required) trailing parameters are:
deg dis pcrev ampfun ranfun skewfun skewpc ranpc ranfreq indexfun atdr dcdr ampfun1 indfun1 fmtfun1 ampfun2 indfun2 fmtfun2 ampfun3 indfun3 fmtfun3 ampfun4 indfun4 fmtfun4Here is a portion of a bagpipe tune:
(let ((fmt1 '(0 1200 100 1000))
(fmt2 '(0 2250 100 1800))
(fmt3 '(0 4500 100 4500))
(fmt4 '(0 6750 100 8100))
(amp1 '(0 .67 100 .7))
(amp2 '(0 .95 100 .95))
(amp3 '(0 .28 100 .33))
(amp4 '(0 .14 100 .15))
(ind1 '(0 .75 100 .65))
(ind2 '(0 .75 100 .75))
(ind3 '(0 1 100 1))
(ind4 '(0 1 100 1))
(skwf '(0 0 100 0))
(ampf '(0 0 25 1 75 1 100 0))
(ranf '(0 .5 100 .5))
(index '(0 1 100 1))
(solid '(0 0 5 1 95 1 100 0))
(bassdr2 '(.5 .06 1 .62 1.5 .07 2.0 .6 2.5 .08 3.0 .56 4.0 .24 5 .98 6 .53 7
.16 8 .33 9 .62 10 .12 12 .14 14 .86 16 .12 23 .14 24 .17))
(tenordr '(.3 .04 1 .81 2 .27 3 .2 4 .21 5 .18 6 .35 7 .03 8 .07 9 .02 10 .025 11 .035)))
(with-sound (:reverb nrev)
(drone .000 4.000 115.000 (* .25 .500) solid bassdr2 .100 .500 .030 45.000 1 .010 10)
(drone .000 4.000 229.000 (* .25 .500) solid tenordr .100 .500 .030 45.000 1 .010 11)
(drone .000 4.000 229.500 (* .25 .500) solid tenordr .100 .500 .030 45.000 1 .010 9)
(canter .000 2.100 918 (* .25 .700) 45.000 1 .050 ampf ranf skwf
.050 .010 10 index .005 .005 amp1 ind1 fmt1 amp2 ind2 fmt2 amp3 ind3 fmt3 amp4 ind4 fmt4)
(canter 2.100 .300 688.5 (* .25 .700) 45.000 1 .050 ampf ranf skwf
.050 .010 10 index .005 .005 amp1 ind1 fmt1 amp2 ind2 fmt2 amp3 ind3 fmt3 amp4 ind4 fmt4)
(canter 2.400 .040 826.2 (* .25 .700) 45.000 1 .050 ampf ranf skwf
.050 .010 10 index .005 .005 amp1 ind1 fmt1 amp2 ind2 fmt2 amp3 ind3 fmt3 amp4 ind4 fmt4)
(canter 2.440 .560 459 (* .25 .700) 45.000 1 .050 ampf ranf skwf
.050 .010 10 index .005 .005 amp1 ind1 fmt1 amp2 ind2 fmt2 amp3 ind3 fmt3 amp4 ind4 fmt4)
(canter 3.000 .040 408 (* .25 .700) 45.000 1 .050 ampf ranf skwf
.050 .010 10 index .005 .005 amp1 ind1 fmt1 amp2 ind2 fmt2 amp3 ind3 fmt3 amp4 ind4 fmt4)
(canter 3.040 .040 619.65 (* .25 .700) 45.000 1 .050 ampf ranf skwf
.050 .010 10 index .005 .005 amp1 ind1 fmt1 amp2 ind2 fmt2 amp3 ind3 fmt3 amp4 ind4 fmt4)
(canter 3.080 .040 408 (* .25 .700) 45.000 1 .050 ampf ranf skwf
.050 .010 10 index .005 .005 amp1 ind1 fmt1 amp2 ind2 fmt2 amp3 ind3 fmt3 amp4 ind4 fmt4)
(canter 3.120 .040 688.5 (* .25 .700) 45.000 1 .050 ampf ranf skwf
.050 .010 10 index .005 .005 amp1 ind1 fmt1 amp2 ind2 fmt2 amp3 ind3 fmt3 amp4 ind4 fmt4)
(canter 3.160 .290 459 (* .25 .700) 45.000 1 .050 ampf ranf skwf
.050 .010 10 index .005 .005 amp1 ind1 fmt1 amp2 ind2 fmt2 amp3 ind3 fmt3 amp4 ind4 fmt4)
(canter 3.450 .150 516.375 (* .25 .700) 45.000 1 .050 ampf ranf skwf
.050 .010 10 index .005 .005 amp1 ind1 fmt1 amp2 ind2 fmt2 amp3 ind3 fmt3 amp4 ind4 fmt4)
(canter 3.600 .040 826.2 (* .25 .700) 45.000 1 .050 ampf ranf skwf
.050 .010 10 index .005 .005 amp1 ind1 fmt1 amp2 ind2 fmt2 amp3 ind3 fmt3 amp4 ind4 fmt4)
(canter 3.640 .040 573.75 (* .25 .700) 45.000 1 .050 ampf ranf skwf
.050 .010 10 index .005 .005 amp1 ind1 fmt1 amp2 ind2 fmt2 amp3 ind3 fmt3 amp4 ind4 fmt4)
(canter 3.680 .040 619.65 (* .25 .700) 45.000 1 .050 ampf ranf skwf
.050 .010 10 index .005 .005 amp1 ind1 fmt1 amp2 ind2 fmt2 amp3 ind3 fmt3 amp4 ind4 fmt4)
(canter 3.720 .180 573.75 (* .25 .700) 45.000 1 .050 ampf ranf skwf
.050 .010 10 index .005 .005 amp1 ind1 fmt1 amp2 ind2 fmt2 amp3 ind3 fmt3 amp4 ind4 fmt4)
(canter 3.900 .040 688.5 (* .25 .700) 45.000 1 .050 ampf ranf skwf
.050 .010 10 index .005 .005 amp1 ind1 fmt1 amp2 ind2 fmt2 amp3 ind3 fmt3 amp4 ind4 fmt4)
(canter 3.940 .260 459 (* .25 .700) 45.000 1 .050 ampf ranf skwf
.050 .010 10 index .005 .005 amp1 ind1 fmt1 amp2 ind2 fmt2 amp3 ind3 fmt3 amp4 ind4 fmt4)))
It is not easy to keep track of all these arguments in a long note-list; hence the
development of programs such as Score (Leland Smith), Pla (yers truly), and Common Music (Rick Taube).
The full note list is bag.clm in the CLM tarball.
| ||||
cellon (beg dur freq amp ...)
| ||||
cellon, developed by Stanislaw Krupowiecz, uses feedback FM as in some old synthesizers. There's a brief discussion of it in fm.html.
The trailing parameters are:
ampfun betafun beta0 beta1 betaat betadc ampat ampdc dis pcrev deg pitch1 glissfun glissat
glissdc pvibfreq pvibpc pvibfun pvibat pvibdc rvibfreq rvibpc rvibfun
and I actually don't know what they all do. I think they're dealing with attack and decay portions
of envelopes; in the old days we felt we had to store one envelope, then kludge around with attack and decay
timings to bash that envelope into the correct shape; this made instruments needlessly messy.
Here's a call:
(with-sound ()
(cellon 0 2 220 .1 '(0 0 25 1 75 1 100 0) '(0 0 25 1 75 1 100 0) .75 1.0 0 0 0 0 1 0 0 220
'(0 0 25 1 75 1 100 0) 0 0 0 0 '(0 0 100 0) 0 0 0 0 '(0 0 100 0)))
The use of x axis values between 0 and 100, rather than 0.0 and 1.0 is a dead give-away that
this is really ancient stuff.
| ||||
clm-expsrc (beg dur input-file exp-ratio src-ratio amp :optional rev start-in-file)
| ||||
clm-expsrc can stretch or compress a sound (using granular synthesis) while optionally changing its sampling rate.
'exp-ratio' sets the expansion amount (greater than 1.0 makes the sound longer), and
'src-ratio' sets the sampling rate change (greater than 1.0 makes it higher in pitch).
So to make a sound twice as long, but keep the pitch the same:
(with-sound () (clm-expsrc 0 4 "oboe.snd" 2.0 1.0 1.0))
'start-in-file' sets where we start reading the input file (in seconds); it defaults to 0.0.
| ||||
drone (beg dur freq amp ampfun synth ampat ampdc amtrev deg dis rvibamt rvibfreq)
| ||||
| This is the other half of Peter Common's bagpipe — see canter above. 'synth' is a list of partials loaded into a table and read via table-lookup. | ||||
expandn time duration file amp ...
| ||||
|
Here is the documentation from Rick Taube's granular synthesis page, edited slightly for the Scheme CLM. The expandn instrument by Michael Klingbeil performs granular syntheisis by time-stretching (expanding/compressing) an input file. This effect is achieved by chopping up the input sound into very small segments (grains) that are then overlayed in the ouput stream. The larger the segments, the more the output sound is smeared, an effect approaching reverberation. The expandn instrument parameters are: time duration filename amplitude :key (expand 1.0) (matrix #f) (ramp 0.4) (seglen 0.15) (srate 1.0) (hop .05) (amp-env '(0 0 50 1 100 0)) (input-start 0.0) (grain-amp 0.8) (reverb #f) 'time' is the start time of the sound in the output file. 'duration' is the duration of expanded sound. To expand an entire sound, set this to the expansion factor times the input sound's duration. 'filename' is the input file to expand. 'amplitude' is a scaler on the ampitude of the input file. Since the output is created by overlaying many copies of the intput this value is generally less than 1. 'hop' can be a number or an envelope. It is the average length in time between segments (grains) in the output. 'expand' can be a number or an envelope. It sets the amount of expansion to produce in the output file. 'seglen'can be a number or an envelope. It is the length in time of the sound segments (grains). 'srate' can be a number or an envelope. It sets the sampling rate change to apply to the output file. 'amp-env' is the amplitude envelope for the output sound. 'input-start' sets where to start reading in the input file. 'grain-amp' is a scaler on each grain's amplitude. 'matrix' is a list, a mixing matrix. 'reverb' is the reverb amount. | ||||
expfil (start duration hopsecs rampsecs steadysecs file1 file2)
| ||||
expfile interleaves two granular synthesis processes (two readers pasting in tiny sections
of their file, one after the other).
(with-sound ()
(expfil 0 2 .2 .01 .1 "oboe.snd" "fyow.snd")
(expfil 2 2 .01 .01 .02 "oboe.snd" "fyow.snd"))
| ||||
exp-snd (file beg dur amp :optional (exp-amt 1.0) (ramp .4) (seglen .15) (sr 1.0) (hop .05) ampenv)
| ||||
exp-snd is a granular synthesis instrument with envelopes on
the expansion amount ('exp-amt' as a list), segment ramp steepness ('ramp' as a list),
segment length ('seglen' as a list), hop length ('hop' as a list), amplitude ('ampenv'),
and resampling rate ('sr' as a list).
In the next example, the expansion amount in both calls goes from 1 to 3 over the course of the note,
the ramp time and segment lengths stay the same, the sampling rate changes from 2 to 0.5, and the hop
stays the same (.05 in the first, and .2 in the second).
(with-sound ()
(exp-snd "fyow.snd" 0 3 1 '(0 1 1 3) 0.4 .15 '(0 2 1 .5) 0.05)
(exp-snd "oboe.snd" 1 3 1 '(0 1 1 3) 0.4 .15 '(0 2 1 .5) 0.2))
| ||||
fm-bell (beg dur frequency amplitude amp-env index-env index)
| ||||
fm-bell is an FM instrument developed by Michael McNabb in Mus10 in the late '70s. It is intended
for low bell sounds (say middle C or so). The lines
(mod1 (make-oscil (* frequency 2))) (mod2 (make-oscil (* frequency 1.41))) (mod3 (make-oscil (* frequency 2.82))) (mod4 (make-oscil (* frequency 2.4))) (car1 (make-oscil frequency)) (car2 (make-oscil frequency)) (car3 (make-oscil (* frequency 2.4)))set up three FM pairs, car1 and mod1 handling the basic harmonic spectra, car2 and mod2 creating inharmonic spectra (using the square root of 2 more or less at random), and car3 and mod3 putting a sort of formant at the minor third (2.4 = a ratio of 12/5 = octave+6/5 = minor tenth). (with-sound () (let ((fbell '(0 1 2 1.1000 25 .7500 75 .5000 100 .5000)) (abell '(0 0 .1000 1 10 .6000 25 .3000 50 .1500 90 .1000 100 0))) (fm-bell 0.0 2.0 220.0 .5 abell fbell 0.5))) | ||||
fm-drum (beg dur freq amp ind :optional (high #f) (deg 0.0) (dist 1.0) (rev-amount 0.01))
| ||||
The fm-drum uses "cascade FM" (see fm.html); it was developed by Jan Mattox.
(with-sound () (fm-drum 0 1.5 55 .3 5 #f) (fm-drum 1.5 1.5 66 .3 4 #t))
| ||||
fm-insect (beg dur freq amp ampenv modfreq modskew modenv index indexenv fmindex ratio deg dist rev)
| ||||
The fm-insect started as an attempt to get cicada sounds from FM (for the 5th movement of "Colony"), but
ended with:
(with-sound (:srate 22050) (let ((locust '(0 0 40 1 95 1 100 .5)) (bug_hi '(0 1 25 .7 75 .78 100 1)) (amp '(0 0 25 1 75 .7 100 0))) (fm-insect 0 1.699 4142.627 .015 amp 60 -16.707 locust 500.866 bug_hi .346 .500) (fm-insect 0.195 .233 4126.284 .030 amp 60 -12.142 locust 649.490 bug_hi .407 .500) (fm-insect 0.217 2.057 3930.258 .045 amp 60 -3.011 locust 562.087 bug_hi .591 .500) (fm-insect 2.100 1.500 900.627 .06 amp 40 -16.707 locust 300.866 bug_hi .346 .500) (fm-insect 3.000 1.500 900.627 .06 amp 40 -16.707 locust 300.866 bug_hi .046 .500) (fm-insect 3.450 1.500 900.627 .09 amp 40 -16.707 locust 300.866 bug_hi .006 .500) (fm-insect 3.950 1.500 900.627 .12 amp 40 -10.707 locust 300.866 bug_hi .346 .500) (fm-insect 4.300 1.500 900.627 .09 amp 40 -20.707 locust 300.866 bug_hi .246 .500)))See animals.scm for much more convincing insect calls. | ||||
fm-trumpet (beg dur ...)
| ||||
This is Dexter Morrill's FM-trumpet; see CMJ feb 77 p51.
(with-sound () (fm-trumpet 0 .25))
As with many instruments from that era, it has a million parameters:
beg dur :key (frq1 250.0) (frq2 1500.0) (amp1 0.5) (amp2 0.1)
(ampatt1 0.03) (ampdec1 0.35) (ampatt2 0.03) (ampdec2 0.3)
(modfrq1 250.0) (modind11 0.0) (modind12 2.66)
(modfrq2 250.0) (modind21 0.0) (modind22 1.8)
(rvibamp 0.007) (rvibfrq 125.0) (vibamp 0.007) (vibfrq 7.0) (vibatt 0.6) (vibdec 0.2)
(frqskw 0.03) (frqatt 0.06)
(ampenv1 '(0 0 25 1 75 .9 100 0)) (ampenv2 '(0 0 25 1 75 .9 100 0))
(indenv1 '(0 0 25 1 75 .9 100 0)) (indenv2 '(0 0 25 1 75 .9 100 0))
(degree 0.0) (distance 1.0) (reverb-amount 0.005)
The pitch depends on the 'modfrq1' and 'modfrq2' parameters, as well as 'frq1' and 'frq2':
(with-sound () (fm-trumpet 0 1 :frq1 400 :frq2 1600 :modfrq1 400 :modfrq2 400))
| ||||
fofins (beg dur frq amp uvib f0 a0 f1 a1 f2 a2 :optional (amp-env '(0 0 1 1 2 1 3 0)))
| ||||
fofins is an implementation of FOF synthesis, taken originally from
fof.c of Perry Cook and the article
"Synthesis of the Singing Voice" by Bennett and Rodet in
"Current Directions in Computer Music Research" (MIT Press).
FOF synthesis sets up a wave with the desired spectrum (to mimic vocal formats, for example),
then calls wave-train to turn that into a tone.
fofins just adds an amplitude envelope and vibrato.
In the Scheme version, there is also an optional trailing vibrato envelope argument (this is slightly different from the CL version):
(with-sound () ; slowly ramp up the vibrato (fofins 0 4 270 .1 0.005 730 .6 1090 .3 2440 .1 '(0 0 .5 1 3 .5 10 .2 20 .1 50 .1 60 .2 85 1 100 0) '(0 0 40 0 75 .2 100 1) ) (fofins 0 4 (* 6/5 540) .1 0.005 730 .6 1090 .3 2440 .1 '(0 0 .5 .5 3 .25 6 .1 10 .1 50 .1 60 .2 85 1 100 0) '(0 0 40 0 75 .2 100 1) ) (fofins 0 4 135 .1 0.005 730 .6 1090 .3 2440 .1 '(0 0 1 3 3 1 6 .2 10 .1 50 .1 60 .2 85 1 100 0) '(0 0 40 0 75 .2 100 1))) | ||||
fullmix (infile :optional beg outdur inbeg matrix srate reverb-amount)
| ||||
fullmix is a complicated way to mix stuff. It's built into the CL version of CLM, so there was clamor for some sort
of replacement in other versions of CLM.
fullmix provides a mixer that can handle any number
of channels of data in and out with scalers and envelopes on any path, sampling rate conversion,
reverb — you name it!
'infile' is the file to be mixed:
(with-sound () (fullmix "pistol.snd")) ; this places pistol.snd at time 0
'beg' is the start time of the mix in the output sound;
'outdur' is the duration of the mixed-in portion in the output;
'inbeg' is where to start the mix in the input file:
(with-sound () (fullmix "pistol.snd" 1.0 2.0 0.25))
;; start at 0.25 in pistol.snd, include next 2 secs, put at time 1.0 in output
'srate' is the amount of sampling rate conversion to apply, and
'reverb' is the amount of the signal to send to the reverberator:
(with-sound (:reverb nrev) (fullmix "pistol.snd" 1.0 2.0 0.25 #f 2.0 0.1)) ; up an octave, lots of reverb!
The 'matrix' parameter is much harder to describe. It is either a number or a list of lists.
In the first case, that number is the amplitude scaler on the output:
(with-sound (:reverb nrev) (fullmix "pistol.snd" 1.0 2.0 0.25 0.2 2.0 0.1)) ; same but much softer (0.2 amp)
If 'matrix' is a list of lists, each element of the inner lists can be either a number or list a breakpoints (an envelope).
If a number, it is treated as an amplitude scaler for that input and output channel combination. Each inner list
represents an input channel, so if we have a stereo input file going to a stereo output file and we want
the channels to be mixed straight, but channel 0 at .5 amp and channel 1 at .75:
(with-sound (:channels 2) (fullmix "2a.snd" #f #f #f '((0.5 0.0) (0.0 0.75))))
;; ^ ^ ^ ^
;; | | | |
;; 0->0 | 1->0 |
;; 0->1 1->1
So, 2a.snd's first channel gets mixed into the output's first channel, scaled by 0.5,
and its second channel goes to the output second channel scaled by 0.75.
If we have four channels in and are writing a mono file, and want to mix in
only the second channel of the input:
(with-sound (:channels 1) (fullmix "4.aiff" #f #f #f '((0.0) (1.0) (0.0) (0.0))))The next complication is that each entry in the inner lists can also be a list of envelope breakpoints. In that case, an envelope is applied to that portion of the mix, rather than just a scaler:
(with-sound (:channels 2) (fullmix "oboe.snd" #f #f #f (list (list (list 0 0 1 1 2 0) 0.5))))
;; mono input so one list, envelope output chan 0, scale output chan 1 (two copies of input)
And finally(!) each inner list element can also be a CLM env generator:
(with-sound (:channels 2)
(fullmix "oboe.snd" 1 2 0 (list (list .1 (make-env '(0 0 1 1) :duration 2 :scaler .5)))))
Here's a Ruby example:
with_sound(:channels, 2, :statistics, true) do
fullmix("pistol.snd")
fullmix("oboe.snd", 1, 2, 0, [[0.1, make_env([0, 0, 1, 1], :duration, 2, :scaler, 0.5)]])
end
Now we need filters!
| ||||
gong (beg dur freq amp :key (degree 0.0) (distance 1.0) (reverb-amount 0.005))
| ||||
gong is an FM instrument developed by Paul Weineke.
Scheme: (with-sound () (gong 0 3 261.61 .3))
Ruby: with_sound() do gong(0, 3, 261.61, 0.6) end
| ||||
gran-synth (beg dur freq grain-dur grain-hop amp)
| ||||
gran-synth sets up a wave-train playing an enveloped
sinusoid (the "grain" in this case). 'grain-dur' sets the grain's length (in seconds),
'grain-hop' sets the frequency of the wave-train generator (how quickly the grain is
repeated), and 'freq' sets the grain sinusoid's frequency.
(with-sound () (gran-synth 0 1 300 .0189 .03 .4)) ; grain freq 300Hz, repetition rate 33Hz
| ||||
graphEq (file :key beg dur or-beg amp (amp-env '(0 1.0 0.8 1.0 1.0 0.0)) (amp-base 1.0) ...)
| ||||
graphEq is a sort of non-graphical graphical equalizer, developed by Marco Trevisani. It sets up a bank of formant
generators with an optional envelope on each formant, then filters and envelopes the input file.
Its trailing parameters are:
(offset-gain 0)
(gain-freq-list '((0 1 1 0) 440 (0 0 1 1) 660))
(filt-gain-scale 1)
(filt-gain-base 1)
(a1 .99)
(stats #t)
'a1' is the formant radius.
'gain-freq-list' is a list of gains and frequencies to
filter
The gains can be either numbers or envelopes (one or the other, not a mixture).
'offset-gain' is an offset (addition) to all the gains.
'filt-gain-scale' and 'filt-gain-base' are similar, but apply to the envelopes, if any.
'stats' prints encouraging numbers if #t.
(with-sound () (graphEq "oboe.snd")) ; accept all the defaults (Scheme is case sensitive)
If we want just steady bands:
(with-sound () (graphEq "oboe.snd" 0 0 0 1.0 '(0 1 1 0) 1.0 0 '(.1 440 .3 1500 .2 330)))
| ||||
hammondoid (beg dur freq amp)
| ||||
hammondoid is Perry Cook's additive-synthesis Hammond organ.
(with-sound () (hammondoid 0 1 440 .1))
| ||||
jl-reverb (:optional (decay 3.0))
| ||||
jl-reverb is a cavernous version of John Chowning's ancient reverberator. You can never get enough reverb!
(with-sound (:reverb jl-reverb) (fm-violin 0 .1 440 .1 :reverb-amount .1))
'decay' is the reverb decay time tacked onto the end of the output sound.
To pass parameters to a reverberator, use the with-sound parameter :reverb-data. So, if we want
5 seconds of decay:
(with-sound (:reverb jl-reverb :reverb-data '(5.0)) (fm-violin 0 .1 440 .1 :reverb-amount .1))
;; ^ this is passed as (jl-reverb 5.0)
| ||||
lbj-piano (beg dur freq amp :key (pfreq frequency) (degree 45) (reverb-amount 0) (distance 1))
| ||||
lbj-piano, developed by Doug Fulton, uses James A Moorer's piano spectra and
additive synthesis to mimic a piano.
(with-sound () (lbj-piano 0 2 110.0 .2))
Doug says, "The high notes sound pretty rotten" and thinks perhaps
one major problem is the lack of mechanical noise.
'pfreq' sets which spectrum to use; it defaults to whatever matches 'freq'.
(with-sound () (lbj-piano 0 2 110.0 .2 :pfreq 550))
| ||||
metal (beg dur freq amp)
| ||||
metal is another Perry Cook creation (HeavyMtl); it's an FM instrument:
(with-sound () (metal 0 1 440 .2))
| ||||
nrev (:key (reverb-factor 1.09) (lp-coeff 0.7) (volume-1 1.0))
| ||||
nrev, developed by Michael McNabb, is one of the more popular old-style reverbs.
It is much cleaner than jc-reverb.
(with-sound (:reverb nrev) (fm-violin 0 .1 440 .1 :reverb-amount .1))
'reverb-factor' controls the length of the decay — it should not exceed 1.21 or so.
'lp-coeff' controls the strength of the low pass filter inserted in the feedback loop.
'volume-1' can be used to boost the reverb output.
(with-sound (:reverb nrev :reverb-data '(:lp-coeff 0.9 :volume-1 2.0))
(fm-violin 0 .1 440 .1 :reverb-amount .1))
| ||||
pins (beg dur file amp ...)
| ||||
pins is a simple implementation of the spectral modeling synthesis
of Xavier Serra and Julius Smith (sometimes known as "Sansy" or "SMS"). See
Serra, X., J. O. Smith. 1990. "Spectral Modeling Synthesis:A Sound Analysis/Synthesis Based on a Deterministic plus Stochastic Decomposition". Computer Music Journal, vol. 14(4), 1990.
The idea behind SMS is similar to the phase vocoder,
but tracks spectral peaks so that its resynthesis options are much more sophisticated.
The trailing parameters are:
:key (transposition 1.0) (time-scaler 1.0) (fft-size 256)
(highest-bin 128) (max-peaks 16) printit attack
'transposition' can be used to transpose a sound;
'time-scaler' changes the sound's duration;
'fft-size' may need to be larger if your sampling rate is 44100, or the input sound's
fundamental is below 300 Hz;
'highest-bin' sets how many fft bins we search for spectral peaks;
'max-peaks' sets how many peaks we track (at a maximum) through the sound;
'printit', if set to #t, causes the peak envelopes to be printied;
'attack' is an optional vct containing the attack portion of the new sound.
Scheme: (with-sound () (pins 0.0 1.0 "now.snd" 1.0 :time-scaler 2.0))
Ruby: with_sound() do pins(0, 1, "now.snd", 1, :time_scaler, 2) end
Xavier has a website devoted to this system, but it seems to move; search for CLAM or SMS.
| ||||
pluck (beg dur freq amp :optional (weighting .5) (lossfact .9))
| ||||
pluck is based on
the Karplus-Strong algorithm as extended by David Jaffe and Julius Smith — see
Jaffe and Smith, "Extensions of the Karplus-Strong Plucked-String Algorithm"
CMJ vol 7 no 2 Summer 1983, reprinted in "The Music Machine".
The basic idea is to fill an array with noise, then filter the array values as it is played repeatedly,
giving a sharp attack and a ringing decay, much like plucking a guitar. The CMJ article
gives many variations, changing pick position and so on. Jaffe's "Silicon Valley Breakdown"
makes great use of this instrument.
'weighting' is the ratio of the once-delayed to the twice-delayed samples. It defaults to .5 which gives a short decay;
anything other than .5 produces a longer decay. It should be between 0.0 and 1.0.
'lossfact' can be used to shorten decays. The most useful values are between .8 and 1.0.
with_sound() do pluck(0.05, 0.1, 330, 0.1, 0.95, 0.95) end | ||||
pqw-vox (beg dur freq spacing-freq amp ampfun freqfun freqscl phonemes formant-amps formant-shapes)
| ||||
pqw-vox is an extension of Marc LeBrun's instrument vox (described below) to use phase-quadrature (single-sideband)
waveshaping. It uses both Chebyshev polynomial kinds to set up spectra-producing pairs of waveshapers that will
add in such a way as to cancel either the upper or lower set of sidebands. These are then ganged together as in
the vox instrument to mimic moving formants.
(with-sound ()
(pqw-vox 0 1 300 300 .1 '(0 0 50 1 100 0) '(0 0 100 1) .3 '(0 L 100 L) '(.5 .25 .1)
'((1 1 2 .5) (1 .5 2 .5 3 1) (1 1 4 .5))))
(with-sound ()
(pqw-vox 0 2 200 200 .1 '(0 0 50 1 100 0) '(0 0 100 1) .1 '(0 UH 100 ER) '(.8 .15 .05)
'((1 1 2 .5) (1 1 2 .5 3 .2 4 .1) (1 1 3 .1 4 .5)))
(pqw-vox 2 2 200 314 .1 '(0 0 50 1 100 0) '(0 0 100 1) .01 '(0 UH 100 ER) '(.8 .15 .05)
'((1 1 2 .5) (1 1 4 .1) (1 1 2 .1 4 .05)))
(pqw-vox 4 2 100 414 .2 '(0 0 50 1 100 0) '(0 0 100 1) .01 '(0 OW 50 E 100 ER) '(.8 .15 .05)
'((1 1 2 .5 3 .1 4 .01) (1 1 4 .1) (1 1 2 .1 4 .05))))
| ||||
pqw (beg dur freq spacing-freq carrier-freq amplitude ampfun indexfun partials ...)
| ||||
pqw is a simple phase-quadrature waveshaping instrument which produces asymmetric spectra.
The trailing parameters just set the usual degree, distance, and reverb values.
(with-sound () (pqw 0 .5 200 1000 .2 '(0 0 25 1 100 0) '(0 1 100 0) '(2 .1 3 .3 6 .5)))
To see the asymmetric spectrum most clearly, set the index function above to '(0 1 100 1).
| ||||
resflt (beg dur driver ...)
| ||||
resflt, developed by Richard Karpen and Xavier Serra, sets up three resonators (two-pole filters),
then drives them with either white noise or a sum-of-cosines pulse train.
Both can be used for vocal effects:
(with-sound ()
(resflt 0 1.0 0 0 0 #f .1 200 230 10 '(0 0 50 1 100 0) '(0 0 100 1)
500 .995 .1 1000 .995 .1 2000 .995 .1)
(resflt 1 1.0 1 10000 .01 '(0 0 50 1 100 0) 0 0 0 0 #f #f
500 .995 .1 1000 .995 .1 2000 .995 .1))
The trailing parameters are:
ranfreq noiamp noifun cosamp cosfreq1 cosfreq0 cosnum ampcosfun freqcosfun
frq1 r1 g1 frq2 r2 g2 frq3 r3 g3
:key (degree 0.0) (distance 1.0)(reverb-amount 0.005)
Set 'driver' to 0 to get the pulse train, or to 1 to get white noise.
In the latter case, 'ranfreq' is the random number generator frequency, 'noiamp' is its amplitude,
and 'noifun' is an amplitude envelope on its output (filter input)
In the pulse case, 'cosamp' is the pulse train amplitude, 'ampcosfun' the amplitude envelope,
'cosfreq0' and 'cosfreq1' set the frequency limits of 'freqcosfun',
and 'cosnum' sets the number of cosines in the pulse.
The three resonators are centered at 'frq1', 'frq2', 'frq3',
with pole-radius 'r1', 'r2', and 'r3' respectively, and
with gains of 'g1', 'g2', and 'g3'.
| ||||
reson (beg dur freq amp ...)
| ||||
reson is a vocal simulator developed by John Chowning. Its trailing parameters are:
numformants indxfun skewfun pcskew skewat skewdc vibfreq vibpc ranvibfreq ranvibpc
degree distance reverb-amount data
'data' is a list of lists of form
'(ampf resonfrq resonamp ampat ampdc dev0 dev1 indxat indxdc)
Needless to say, no one has ever written out these parameters by hand, so here's an all-time first:
(with-sound ()
(reson 0.0 1.0 440 .1 2 '(0 0 100 1) '(0 0 100 1) .1 .1 .1 5 .01 5 .01 0 1.0 0.01
'(((0 0 100 1) 1200 .5 .1 .1 0 1.0 .1 .1) ((0 1 100 0) 2400 .5 .1 .1 0 1.0 .1 .1))))
But JC got very nice vocal sounds from this — I must have mistyped somewhere...
Here's another stab at it:
(with-sound () (reson 0.0 1.0 440 .1 2 '(0 1 100 0) '(0 0 100 1) .01 .1 .1 5 .01 5 .01 0 1.0 0.01 '(((0 1 100 1) 1000 .65 .1 .1 0 1.0 .1 .1) ((0 0 100 1) 2400 .15 .1 .1 0 1.0 .1 .1))))If you find a good example, please send me it! | ||||
rhodey (beg dur freq amp :optional (base .5))
| ||||
rhodey is another of Perry Cook's instruments (an electric piano), based on a pair of FM generators.
(with-sound () (rhodey 0 1 440 .2))
One of the oscillators is set to a frequency 15 times the requested 'freq', so for higher notes, you'll need to set the srate higher:
(with-sound (:srate 44100) (rhodey 0 1 880 .2))
| ||||
rms gen sigbalance gen sig comparisongain gen sig rsmvalmake-rmsgain :optional (hp 10.0)
| ||||
rms, balance, and gain are an implementation of the balance generators of CLM (based
on CSound originals, Scheme versions originally provided by Fabio Furlanete).
This section is a paraphrase of balance.html in the CLM tarball which was
written by Sam Hiesz.
balance, rms, and gain are used to track the RMS value of a signal and use
that information to scale some other signal. rms returns the RMS value;
gain takes a signal and an RMS value and modifies the signal to track the RMS
value; balance packages gain and rms into one function call.
make-rmsgain returns the generator used by rms, gain, and balance.
The 'hp' parameter sets the speed with which the balance process
tracks the RMS signal. An example is worth a zillion words:
| ||||
scratch (beg file src-ratio turnlist)
| ||||
scratch moves back and forth in a sound file according to
a list of turn times much like env-sound-interp.
With voice input, we can create a "Remembrance of Bugs Bunny":
Scheme: (with-sound () (scratch 0.0 "now.snd" 1.5 '(0.0 .5 .25 1.0)))
Ruby: with_sound() do scratch(0, "now.snd", 1.5, [0.0, 0.5, 0.25, 1.0]) end
I translate this as: "go forward from 0.0 to 0.5 secs, backwards to 0.25 secs, then forward to 1.0 secs".
| ||||
spectra (beg dur freq amp ...)
| ||||
spectra is an additive-synthesis instrument with vibrato and an amplitude envelope. It was intended originally
to be used with the spectra in spectra.scm (information laboriously gathered at the dawn of the computer era
by James A Moorer). One such spectrum is labelled "p-a4", so we can hear it via:
(load "spectr.scm")
(with-sound ()
(spectra 0 1 440.0 .1 p-a4 '(0.0 0.0 1.0 1.0 5.0 0.9 12.0 0.5 25.0 0.25 100.0 0.0)))
The trailing parameters are:
:optional (partials '(1 1 2 0.5))
(amp-envelope '(0 0 50 1 100 0))
(vibrato-amplitude 0.005)
(vibrato-speed 5.0)
(degree 0.0)
(distance 1.0)
(reverb-amount 0.005)
We can pass our own partials:
(with-sound ()
(spectra 0 1 440.0 .1 '(1.0 .4 2.0 .2 3.0 .2 4.0 .1 6.0 .1)
'(0.0 0.0 1.0 1.0 5.0 0.9 12.0 0.5 25.0 0.25 100.0 0.0)))
| ||||
ssb-fm gen modsigmake-ssb-fm freq
| ||||
| These two functions implement a sort of asymmetric FM using ideas similar to those used in ssb-am. | ||||
stereo-flute (beg dur freq flow ...)
| ||||
This is a physical model of a flute developed by Nicky Hind.
Scheme:
(with-sound (:channels 2)
(stereo-flute 0 1 440 0.55 :flow-envelope '(0 0 1 1 2 1 3 0))
(stereo-flute 1 3 220 0.55 :flow-envelope '(0 0 1 1 2 1 3 0)))
Ruby:
with_sound() do stereo_flute(0, 2, 440, 0.55, :flow_envelope, [0, 0, 1, 1, 2, 1, 3, 0]) end
The trailing parameters are:
:key (flow-envelope '(0 1 100 1))
(decay 0.01) ; additional time for instrument to decay
(noise 0.0356)
(embouchure-size 0.5)
(fbk-scl1 0.5) ; these two are crucial for good results
(fbk-scl2 0.55)
(offset-pos 0.764264) ; from 0.0 to 1.0 along the bore
(out-scl 1.0)
(a0 0.7) (b1 -0.3) ; filter coefficients
(vib-rate 5)
(vib-amount 0.03)
(ran-rate 5)
(ran-amount 0.03)
As with physical models in general, you may need to experiment a bit to find
parameters that work.
| ||||
touch-tone (beg number)
| ||||
This instrument produces telephone tones:
Scheme: (with-sound () (touch-tone 0.0 '(7 2 3 4 9 7 1)))
Ruby: with_sound() do touch_tone(0, [7, 2, 3, 4, 9, 7, 1]) end
It is just two sine waves whose frequencies are chosen based on the number pressed.
1 2 3 697 Hz
4 5 6 770 Hz
7 8 9 852 Hz
0 941 Hz
1209 1336 1477 Hz
For more than you really want to know about other such sounds, see
Telephone Tone Frequencies.
| ||||
tubebell beg dur freq amp :optional (base 32.0)
| ||||
Perry Cook's tubular bell:
(with-sound ()
(tubebell 0 2 440 .1 32.0)
(tubebell 2 2 220 .1 64.0)
(tubebell 4 2 660 .1 .032))
'base' is the envelope base:
(with-sound ()
(tubebell 0 2 440 .1 32.0)
(tubebell 2 2 220 .1 2048.0)
(tubebell 4 3 660 .1 .032))
| ||||
two-tab beg dur freq amp ...
| ||||
two-tab interpolates between two spectra.
(with-sound () (two-tab 0 2 440 .1 '(1.0 1.0) '(3.0 1.0)))
;; go from harmonic 1 to harmonic 3
The trailing parameters are:
:optional (partial-1 '(1.0 1.0 2.0 0.5))
(partial-2 '(1.0 0.0 3.0 1.0))
(amp-envelope '(0 0 50 1 100 0))
(interp-func '(0 1 100 0))
(vibrato-amplitude 0.005)
(vibrato-speed 5.0)
(degree 0.0)
(distance 1.0)
(reverb-amount 0.005)
'interp-func' determines how we interpolate between the two spectra. When
it is at 1.0, we get only the first, at 0.0 only the second.
(with-sound () (two-tab 0 2 440 .1 '(1.0 1.0) '(3.0 1.0) '(0 0 1 1 2 0) '(0 0 1 1)))
is the reverse of the earlier sound. To go out and back:
(with-sound () (two-tab 0 2 440 .1 '(1.0 1.0) '(3.0 1.0) '(0 0 1 1 2 0) '(0 0 1 1 2 0)))
| ||||
vox (beg dur freq amp ampfun freqfun freqscl voxfun index vibscl)
| ||||
vox is a translation of Marc LeBrun's MUS10 waveshaping voice instrument
using FM in this case.
The basic idea is that each of the three vocal formants is created by two
sets of waveshapers (or oscils producing FM), one centered on the even multiple of the base frequency closest to the desired formant frequency,
and the other on the nearest odd multiple. As the base frequency moves (due to vibrato or glissando),
these center frequencies are recalculated on each sample, and the respective amplitudes
set to reflect the distance of the current center frequency from the desired formant frequency. If a center frequency moves
enough that the previous upper member of the pair has
to become the lower member, the upper waveshaper (which has meanwhile ramped to zero amplitude), jumps
down to its new center. The male-speaker formant table was provided by Robert Poor (see the code
for the complete table of formants).
For details on waveshaping, see Le Brun, "Digital Waveshaping Synthesis", JAES 1979 April, vol 27, no 4, p250.
I used vox in the 5th movement of "Colony" and in "The New Music Liberation Army".
(with-sound () (let ((amp-env '(0 0 25 1 75 1 100 0)) (frq-env '(0 0 5 .5 10 0 100 1))) (vox 0 2 170 .4 amp-env frq-env .1 '(0 E 25 AE 35 ER 65 ER 75 I 100 UH) '(.8 .15 .05) '(.005 .0125 .025) .05 .1) (vox 2 2 110 .4 amp-env frq-env .5 '(0 UH 25 UH 35 ER 65 ER 75 UH 100 UH) '(.8 .15 .05) '(.005 .0125 .025)) (vox 4 2 300 .4 amp-env frq-env .1 '(0 I 5 OW 10 I 50 AE 100 OO) '(.8 .15 .05) '(.05 .0125 .025) .02 .1)))Or in Ruby:
with_sound() do
amp_env = [0, 0, 25, 1, 75, 1, 100, 0]
frq_env = [0, 0, 5, 0.5, 10, 0, 100, 1]
vox(0, 2, 170, 0.4, amp_env, frq_env, 0.1,
[0, :E, 25, :AE, 35, :ER, 65, :ER, 75, :I, 100, :UH], 0.05, 0.1)
vox(2, 2, 300, 0.4, amp_env, frq_env, 0.1,
[0, :I, 5, :OW, 10, :I, 50, :AE, 100, :OO], 0.02, 0.1)
vox(4, 5, 600, 0.4, amp_env, frq_env, 0.1,
[0, :I, 5, :OW, 10, :I, 50, :AE, 100, :OO], 0.01, 0.1)
end
vox can also be use for less vocal effects: (with-sound (:play #t :scaled-to .5) (vox 0 .25 500 .4 '(0 0 .1 1 1 1 2 .5 3 .25 10 0) '(0 0 5 .5 10 0 100 1) .1 '(0 E 25 OW 35 ER 105 ER) '(.13 .15 .15) '(.005 .005 .015) .05 .1)) | ||||
wurley beg dur freq amp
| ||||
Perry Cook's Wurlitzer (I assume).
(with-sound () (wurley 0 1 440 .1))
| ||||
za time dur freq amp length1 length2 feedback feedforwardzc time dur freq amp length1 length2 feedbackzn time dur freq amp length1 length2 feedforward
| ||||
The "z" instruments demonstrate "zdelay" effects — interpolating
comb, notch, and all-pass filters.
(with-sound () (zn 0 1 100 .1 20 100 .995)
(zn 1.5 1 100 .1 100 20 .995)
(zc 3 1 100 .1 20 100 .95)
(zc 4.5 1 100 .1 100 20 .95)
(za 6 1 100 .1 20 100 .95 .95)
(za 7.5 1 100 .1 100 20 .95 .95))
| ||||
snd-test.scm has examples of calling all these instruments. For more examples of instruments, there are a variety of separate files such as v.scm, and clm23.scm has a translation of the CLM test instruments. It also has some comments about the differences between the CL and Scheme instruments.
see also: bird clm dlocsig examp fade fm fmv freeverb graphEq grani jcrev maraca maxf noise piano prc95 pvoc rt-examples singer sndwarp stochastic strad ws |
|
snd-debug ; enable some command shortcuts snd-trace ; enable trace output snd-break (message #f) ; set a breakpoint
debug.scm is a package of Guile-specific debugging aids. snd-break sets a breakpoint; if it is called, you drop into the Snd debugger. You can continue from the breakpoint, optionally returning any value you like. While in the break context (while the listener prompt says "break"), these functions are available:
break-go (returned-value #f) ; continue execution from breakpoint, return 'returned-value'
break-locals (stack-location 0) ; display local variables
break-local local-var (stack-location 0) ; print a local's value ('local-var' is a symbol or string)
break-backtrace (all #f) ; display stack at breakpoint (5 levels by default, 'all' = #t: all levels)
break-help ; display some help
break-quit ; exit current break level
break-quit! ; exit all break levels, return to top level
Here is a brief session in Snd's listener:
:(define (test-break a) (let ((b (+ a (snd-break "hiho")))) b))
#<unspecified>
:(define hi 123)
#<unspecified>
:(set! hi (test-break 1))
break:("hiho")
break:hi
123
break:(break-go 32)
:#<unspecified>
:hi
33
We put a breakpoint in the midst of an expression in the 'test-break' function, asking it to type "hiho" and drop into the debugger if it is executed. Then we call 'test-break' in an expression that sets the variable 'hi'. The breakpoint is hit, "hiho" gets reported, and we're placed in the debugger. The debugger is just the Snd listener, but with some extra context to implement the break support. After poking around, we call break-go with an argument of 32. This causes the original set! to continue with 32 plugged in where the snd-break call was, setting hi to 33.
snd-debug sets up some information needed by the 'bt' and 'lv' commands:
bt ; show backtrace lv (:optional obj) ; show local var(s)
These are active outside a breakpoint, so once set up, you can use them whenever an error occurs. snd-trace activates any tracing that you may have requested and redirects its output to the Snd listener. Here's how to trace fm-violin calls in a notelist:
(trace fm-violin)
(snd-trace (with-sound () (fm-violin 0 1 440 .1)))
To turn off the trace
(untrace fm-violin)
|
dlocsig is a CLM generator developed by Fernando Lopez-Lezcano that can move sounds in two or three dimensions. Fernando's CLM/lisp-oriented documentation can be found in dlocsig.html. dlocsig.rb is Michael Scholz's translation of dlocsig to Ruby. It has lots of documentation and examples. If you load dlocsig.rb, a new menu is added named "Dlocsig". If you choose a path from this menu, you get a graphical user-interface to play with the various envelopes that drive dlocsig. Click the "With_Snd" button to apply the current path choices to the currently selected sound. Click "Gnuplot" to get a pretty picture of the path (in 3D!). A simple instrument that uses dlocsig is:
(define* (sinewave start-time duration freq amp :key (amp-env '(0 1 1 1))
(path (make-path :path '(-10 10 0 5 10 10))))
(let* ((vals (make-dlocsig :start-time start-time :duration duration :path path))
(dloc (car vals))
(beg (cadr vals))
(end (caddr vals)))
(let* ((osc (make-oscil freq))
(aenv (make-env amp-env :scaler amp :duration duration)))
(run
(lambda ()
(do ((i beg (1+ i)))
((= i end))
(dlocsig dloc i (* (env aenv) (oscil osc)))))))))
(with-sound (:channels 2) (sinewave 0 1.0 440 .5 :path (make-path '((-10 10) (0.5 0.5) (10 10)) :3d #f)))
|
|
draw.scm has examples of graphics-oriented extensions. The most useful one is make-current-window-display.
click-for-listener-help (pos)
| |||
| click-for-listener-help is intended as a listener-click-hook function. It posts help about the closest entity it can find whenever you double click in the listener. Unfortunately, the help dialog is a bit clunky for a use like this, but the minibuffer has only one line, and tooltips are irritating in their own way; perhaps it should post the help at the bottom of the listener? | |||
color-samples (color :optional beg dur snd chn)uncolor-samples (:optional snd chn)
| |||
| color-samples displays the samples from sample 'beg' for 'dur' samples in 'color' whenever they're in the current time domain view. uncolor-samples cancels this action. Here is a representative picture. To activate this, add it to after-graph-hook. | |||
display-previous-edits (snd chn)
| |||
display-previous-edits displays all the edits of the current sound, with older edits gradually fading away.
To activate this, add it to after-graph-hook:
(add-hook! after-graph-hook display-previous-edits)
| |||
make-current-window-display ()
| |||
make-current-window-display
displays in the upper right corner the overall current sound and where the current window fits in it.
This information is implicit in the x axis zoom and position sliders, but a redundant graph doesn't hurt. If you click in that graph,
the cursor is moved to the clicked point. A menu item named "Toggle inset" is added to the View menu to turn this
little graph off or on.
If you're using a line cursor (if cursor-style is cursor-line), it may
collide at times with the little graph;
to get a more polite cursor, use smart-line-cursor.
| |||
overlay-rms-env (snd chn)
| |||
overlay-rms-env displays the running rms value of the currently displayed data in red, overlayed upon the
normal graph. To activate it, add it to the after-graph-hook:
(add-hook! after-graph-hook overlay-rms-env)
| |||
overlay-sounds (:rest sounds)
| |||
overlay-sounds overlays onto its first argument (a sound index) all subsequent arguments: (overlay-sounds 1 0 3).
| |||
samples-via-colormap (snd chn)
| |||
| |||
smart-line-cursor (snd chn tracking)
| |||
| smart-line-cursor is a cursor-style function that tries not to overwrite the thumbnail graph drawn by make-current-window-display. | |||
|
|