Score Synth: Copy-Synth-Patch Scheme
Score Synth: Copy-Synth-Patch Scheme
This preliminary section demonstrates how to interface the synth part of PWGL with music notation. This protocol assumes typically some Lisp programming.
Copy-Synth-Patch Scheme
In the following examples a box called 'copy-synth-patch' is used to copy the contents of an abstraction box count times (the abstraction contains a patch consisting of DSP-modules that are used to realize the instrument). Thus the user has to define the abstraction only once and the system automatically copies this model as many times as required. In order to distinguish between different duplicated patch instances 'copy-synth-patch' generates automatically symbolic references to specific user defined entry points. These entry points are defined by connecting a 'synth-plug' box at the leafs of a synthesis patch. The entry-points are used afterwards to control the synthesis process. The symbolic references are pathnames, such as 'guitar1/1/freq' or 'guitar2/6/lfgain'. This scheme allows to associate an instrument to a score by adding to the 'synth-plug' boxes information about accessors. These accessors are defined as short methods that are used to access information from a note object, such midi, velocity, start-time, and duration. Furthermore, the user gives control labels (D, T, or C) to the synth-plug boxes whether they are used for discrete, trigger or continuous control purposes. From this information (i.e. entry point pathnames, accessors and control labels) the system creates automatically both discrete and continuous control methods for the instrument in question.
Score Interface
The score/music notation interface is realized in 3 main steps. First, a Lisp-code box contains in textual form an instrument definition and accessor (or control) methods, which will be used later in the visual instrument definition (see below the second step). Second, the main synthesis patch contains the visual instrument definition consisting of an abstraction which is duplicated by a 'copy-synth-patch' box count times. In the abstraction the accessor methods are used by the 'synth-plug' boxes to define the interface between the score information and the instrument definition. The 'synth-ctrl-mapping' box generates automatically control method definitions that are called by the notes when the score is translated to control information. Note that in order to play scores with the synth, you must set the second input of the synth-box to ':score'. The third step involves the preparation of a score where the instrument classes and instrument-instances are given for the parts that should be realized by the
synthesis patch. To listen a score in real-time select it and press space. This will automatically search for the associated synthesis patch, calculate the score, and start the current synth box. Now you should hear the score and see the text 'synth/score: CPU: xx time xx' in the information area of the PWGL output window. Non-real-time mode can be evoked by changing the third ':dac' input of the synth-box to ':file'. To start the non-real-time calculation select the synth-box and press 'v'.
syne
This simple patch demonstrates how to control a sine module from a monophonic score. The score/music notation interface is realized here in 3 main steps. First, a Lisp-code box (1) contains an instrument definition and an accessor method called 'ctrl-freq'. Second, the top-elevel patch in (2) contains an abstraction, 'Sine', which is duplicated by a 'copy-synth-patch' box count times (here count = 1). In the abstraction the accessor method, 'ctrl-freq', is used by a discrete 'synth-plug' box (note the label 'D') to define the frequency of the 'sine' module (see the third input of the 'synth-plug' box). The third step, (3), is a score, where the instrument class, 'mysimplesine', and instrument-instance, 'sine1', are given for a part that contains a melodic line. To listen to a score select it and press space (now you should hear the result). Non-real-time mode that creates a sound file can be used by changing the ':dac' input (4) to ':file'. If you want simply to listen to the score again press space. Note that if you make changes in the score you must recalculate the score with ctrl-space in order to hear the change. If you make changes in the instrument definition you should first reevaluate the synth-ctrl-mapping box and then recalculate the score (with ctrl-space).
(in-package :ccl) ;======================================== ; instrument (create-enp-instrument mysimplesine (synth-instrument) () (:default-initargs :name "mysimplesine" :instrument-group :synth-instruments :instrument-order 45000)) ;======================================== ; accessors (defmethod ctrl-freq ((self mysimplesine) note name) (pw::m->f (midi note)))
polysine
This patch is similar to the previous one except here we demonstrate how to control a sine module with an amplitude envelope. This patch can handle polyphonic scores. First, a Lisp-code box (1) contains an instrument definition and two main accessor methods called 'ctrl-freq' and 'ctrl-envelope'. Second, the top-elevel patch in (2) contains an abstraction, 'Sine', which is duplicated by a 'copy-synth-patch' box count times (count = 3). In the abstraction the discrete accessor methods, 'ctrl-freq' and 'ctrl-envelope' are used by the 'synth-plug' boxes (with the label 'D') to define the frequency and the amplitude of the 'sine' module. A third trigger 'synth-plug' box (with the label 'T') is used to trigger the envelope. The third step, (3), a 'Multi-Score-Editor' box, contains two scores, where the instrument class, 'mypolysine', and instrument-instance, 'sine1', are given for parts. To listen to a score select a score (using the arrow up and down keys) and press space (now you should hear the result). Non-real-time mode that creates a sound file can be used by changing the ':dac' input (4) to ':file'.
(in-package :ccl) ;======================================== ; instrument (create-enp-instrument mypolysine (synth-instrument) () (:default-initargs :name "mysimplesine" :instrument-group :synth-instruments :instrument-order 45000)) ;======================================== ; accessors (defmethod ctrl-freq ((self mypolysine) note name) (pw::m->f (midi note))) (defmethod ctrl-amp ((self mypolysine) note name) (* 0.2 (/ (vel note) 127))) (defmethod ctrl-envelope ((self mypolysine) note name) (let ((dur (read-key note :enp-dur)) (amp (ctrl-amp self note name))) (convert-to-synth-env (mk-bpf (list 0 0.05 (- dur 0.05) dur) (list 0 amp (* 0.7 amp) 0)))))
sine-w-envelope
This is a more advanced patch that shows how expression markings can be used to control a synthesis instrument. The Lisp-code box (1) contains now 2 accessor or control methods, 'ctrl-amp' and 'ctrl-freq', which are used to calculate envelope information according to expression information found in the score. The 'ctrl-amp' method, for instance, takes into account whether the current note belongs to slurred group or not. Also the duration of a note is modified if the note has a staccato expression. The top-level patch (2) contains an abstraction, 'Sine', which contains now several 'synth-plug' boxes that both trigger and feed envelope information (calculated by the 2 control methods) to the synthesis modules. This patch step contains 2 scores, (3) and (4), which are prepared in advance. To listen to either of the scores select it and press space. (in-package :ccl) ;======================================== ; instrument (create-enp-instrument mysine (synth-instrument) () (:default-initargs :name "mysine" :instrument-group :synth-instruments :instrument-order 45000)) ;======================================== ; accessors (defparameter *sine-point-cnt* 10) (defmethod ctrl-amp ((self mysine) note name) (let* ((vel (cond ((e note :accent) 120) (T 40))); check whether the note is accented or not (amp (/ vel 127.0)) (start-level (if (and (e (prev-item note) :slur) (e note :slur)) 0.15 0.0)) (end-level (if (and (e (next-item note) :slur) (e note :slur)) 0.15 0.0)) (dur (* (if (e note :staccato) 0.85 1.0) (read-key note :enp-dur)))) (let* ((ys (g* amp (cons start-level (loop for i from 1 upto (- *sine-point-cnt* 1) collect (if (= i 9) end-level (g-random 0.15 0.3))))))) (convert-to-synth-env (mk-bpf (pw::interpol *sine-point-cnt* 0.0 1.0 t) ys) dur)))) (defmethod ctrl-freq ((self mysine) note name) 6
(let* ((fund (pw::m->f (midi note))) (ys (loop for i from 1 upto *sine-point-cnt* collect (* fund (g-random 1.0 1.02)))) (dur (* (if (e note :staccato) 0.75 1.0) (read-key note :enp-dur)))) (convert-to-synth-env (mk-bpf (pw::interpol *sine-point-cnt* 0.0 1.0 t) ys) dur)))
sine-vector
This patch demonstrates how to control the amplitude and frequency envelopes of a 'sine-vector' box using relatively complex accessor methods. This patch demonstrates also the use of vectored boxes (see the previous tutorial sections 'Vector' and 'Copy-synth-patch'). Each note has 10 independent envelopes for amplitude and frequency. These envelopes are calculated by the 'ctrl-amp' and 'ctrl-freq' accessors (1). Of special interest is here the 'ctrl-pan' accessor that allows to control the panning of each note in the score either based on midi-channel information or on a panning breakpoint function that is found in the score. The 'sines' abstraction (2) contains now vectored boxes ('envelope-trigger' and 'sine-vector'). We have also a 'stereo-pan' module which gets its pan position from the 'ctrl-pan' accessor. The top-level patch (3) contains a 'accum-vector' box that mixes down the signals from the 'copy-synth-patch' box to a stereo signal. We have also a global reverb box (the reverb is as well a synthesis abstraction). The reverb output is mixed with the original dry signals by the 'add-vector' box. The example contains 2 scores, (4) and (5). In (4) the pan is controlled by the midi-channel information of the notes. In (5), however, we have a break-point function expression (6) in the score, which allows the user to specify the pan parameter visually.
(in-package :ccl) ;======================================== ; instrument (create-enp-instrument mysines (synth-instrument) () (:default-initargs :name "mysines" :instrument-group :synth-instruments :instrument-order 45000)) ;======================================== ; accessors (defparameter *sines-env-cnt* 10) (defparameter *sines-point-cnt* 10) (defmethod ctrl-pan ((self mysines) note name)
(let ((bpf-pan (e note :bpf :sample :at note))) (or bpf-pan (case (chan note) (1 0.0) (2 -0.5) (3 0.5))))) (defmethod ctrl-amp ((self mysines) note name) (let ((amp (/ (vel note) 127)) (start-level (if (and (e (prev-item note) :slur) (e note :slur)) 0.05 0.0)) (end-level (if (and (e (next-item note) :slur) (e note :slur)) 0.05 0.0))) (flat (loop for b from 1 upto *sines-env-cnt* collect (let* ((ys (g* amp (cons start-level (loop for i from 1 upto (- *sines-point-cnt* 1) collect (if (= i 9) end-level (g-random 0.05 0.1))))))) ;(print ys *so*) (convert-to-synth-env (mk-bpf (pw::interpol *sines-point-cnt* 0.0 1.0 t) ys) (read-key note :enp-dur))))))) (defmethod ctrl-freq ((self mysines) note name) (flat (loop for b from 1 upto *sines-env-cnt* collect (let* ((fund (pw::m->f (midi note))) (ys (loop for i from 1 upto *sines-point-cnt* collect (* fund (g-random 1.0 1.02))))) ;(print ys *so*) (convert-to-synth-env (mk-bpf (pw::interpol *sines-point-cnt* 0.0 1.0 t) ys) (read-key note :enp-dur)))))) (defun mk-init-sines-envs () (flat (loop for b from 1 upto *sines-env-cnt* collect (convert-to-synth-env (mk-bpf (pw::interpol *sines-point-cnt* 0.0 1.0 t) (pw::make-list2 *sines-point-cnt* 0.0))))))
10