Vinal Scratch Tutorial

This sound reminds me of the effect of dragging a needle across a vinal record.

(defun ring (dur pch scl)
  (let ((modstep1 (hz-to-step (* (step-to-hz pch) (sqrt 2.0))))
        (modstep2 (hz-to-step (* (step-to-hz pch) (sqrt 11.0)))))
    (stretch dur 
      (mult 
        (env 0.05 0.1 0.2 1 0.6 0.24 1)
        (fmosc pch (mult 
	             (pwl 0.07 1800 0.15 1000 0.4 680 0.8 240 1 100 1) 
		     scl 
                     (osc modstep1)
                     (osc modstep2) ))) )))

The following plays an example sound from this function:

(play (ring 7.1 (hz-to-step 1) 1.2))

Here is a brief description of how this function works: The sound is created by an FM oscillator. The modulation comes from two sinusoids operating at low frequencies multiplied together. The sinusoids are not harmonically related, so an irregular pulse is generated by their product. This is scaled further by a piece-wise linear envelope that adds more variation. To make the sinusoids inharmonically related, their frequencies are scaled by the square root of 2 and the square root of 11. The variables modstep1 and modstep2 are initialized to these computed frequencies.

The following example combines several instances of ring with different parameters:

(play (sim (scale 0.15 (at 2.9 (ring 7.1 (hz-to-step 1) 1.2)))
	   (scale 0.175 (at 4.9 (ring 5.1 (hz-to-step 2) 1.414)))
	   (scale 0.2 (at 6.9 (ring 3.1 (hz-to-step 4) 1.8)))))

Other Sounds Using Ring

The same ring function can be used to achieve other sounds. Listen to these examples:

(play (sim 
        (scale 0.35 (at 1.5 (ring 4 1 1)))
	(scale 0.325 (at 4 (ring 4 4 2)))
	(scale 0.3 (at 7.5 (ring 4 10 4))) ))

These instances use a higher pitch parameter than the previous ones.

Another Related Sound

The following creates a sound using FM and a wave table derived from a vocal sound.

(defun vocrap (&optional (pch 16) (dur 1))
  (if (not (boundp '*voc-table1*)) (mk-voc1-table))
  (fmosc pch (stretch dur (pwl 0 3 0.1 -20 0.2 20 0.3 30
                               0.4 -10 0.5 15 0.6 0
                               0.8 -30 1 60 1)) *voc-table1*))

This function uses a special test to make sure that *voc-table1* is initialized. If it is not, it would be an error to read or test it, but you can query to find out if the variable is bound or unbound. (Global variables become bound when you assign a value to them.) The boundp function takes an atom (note the use of the single quote to denote an atom, the name of the variable, rather then the variable's value) and returns true if the variable is bound.

Here is the definition for mk-voc1-table. You might have to replace the filename depending upon how your system is configured.:

(defun mk-voc1-table ()
    (if (not (boundp 'voc-snd1))
        (setf voc-snd1 (s-read "./test/voc1.snd")))
    (setf *voc-table1* (list voc-snd1 16 T)))

The following shows one way to invoke vocrap:

(play (seqrep (i 4) (vocrap)))