next up previous contents index
Next: Modulation Up: Examples Previous: Additive synthesis: spectral envelope   Contents   Index


Polyphonic synthesis: sampler

We move now to an example using dynamic voice allocation as described in section 4.5. In the additive synthesis examples shown previously, all the voices are always used for a fixed purpose. In the present example, we allocate voices from a bank as needed to play notes in a control stream.

Patch D11.sampler.poly.pd(figure 4.20) shows the polyphonic sampler, which uses the abstraction sampvoice (shown in figure 4.21). The techniques for altering the pitch and other parameters in a one-shot sampler are shown in patch D10.sampler.notes.pd(not shown) which in turn is derived from the original one-shot sampler from the previous chapter (C05.sampler.oneshot.pd, shown in figure 3.14).

Figure 4.20: A polyphonic sampler demonstrating voice allocation and use of tags.
\begin{figure}\psfig{file=figs/fig04.20.ps}\end{figure}

Figure 4.21: The sampvoice abstraction used in the polyphonic sampler of figure 4.20.
\begin{figure}\psfig{file=figs/fig04.21.ps}\end{figure}

The sampvoice objects in figure 4.20 are arranged in a different kind of summing bus from the ones before, in which each one adds its own output to the signal on its inlet, and puts the sum on its outlet. At the bottom of the eight objects, the outlet therefore holds the sum of all eight. This has the advantage of being more explicit than the throw~ / catch~ busses, and is preferable if visual clutter is not at issue.

The main job of the patch, though, is to distribute the ``note" messages to the sampvoice objects. To do this we must introduce some new Pd objects:


\fbox{ \texttt{pdmod}}: Integer modulus. For instance, 17 mod 10 gives 7, and -2 mod 10 gives 8. There is also an integer division object named pddiv ; dividing 17 by 10 via pddiv gives 1, and -2 by 10 gives -1.


\fbox{ \texttt{poly}}: Polyphonic voice allocator. Creation arguments give the number of voices in the bank and a flag (1 if voice stealing is needed, 0 if not). The inlets are a numeric tag at left and a flag at right indicating whether to start or stop a voice with the given tag (nonzero numbers meaning ``start" and zero, ``stop".) The outputs are, at left, the voice number, the tag again at center, and the start/stop flag at right. In MIDI applications, the tag can be pitch and the start/stop flag can be the note's velocity.


\fbox{ \texttt{makenote}}: Supply delayed note-off messages to match note-on messages. The inlets are a tag and start/stop flag (``pitch" and ``velocity" in MIDI usage) and the desired duration in milliseconds. The tag/flag pair are repeated to the two outlets as they are received; then, after the delay, the tag is repeated with flag zero to stop the note after the desired duration.

The ``note" messages contain fields for pitch, amplitude, duration, sample number, start location in the sample, rise time, and decay time. For instance, the message,

     60 90 1000 2 500 10 20
(sent to ``note" via the r note object) means to play a note at pitch 60 (MIDI units), amplitude 90 dB, one second long, from the wavetable named ``sample2", starting at a point 500 msec into the wavetable, with rise and decay times of 10 and 20 msec.

After unpacking the message into its seven components, the first job is to create a tag for the note. To do this, first the t b f object outputs a bang after the last of the seven parameters appear separately. The combination of the +, f, and mod objects act as a counter which repeats after a million steps, essentially generating a unique number corresponding to the note.

The next step is to use the poly to determine which voice to play which note. The poly object expects separate note starts and stops as input. So the tag and duration are first fed to the makenote object, whose output contains a flag (``velocity") at right and the tag again at left. For each tag makenote receives, two pairs of numbers are output, one to start the note, and another, after a delay equal to the note duration, to stop the note.

Having treated poly to this separated input, we now have to strip the note stop output, since we really only need combined ``note" messages with duration fields. The stripnote object does this job. Finally, the voice number we have calculated is prepended to the seven parameters we started with (the pack object), so that the output of the pack object looks like this:

     4 60 90 1000 2 500 10 20
where the ``4" is the voice number output by the poly object. The voice number is used to route the note message to the desired voice using the route object. The desired voice (i.e., the sampvoice object) then gets the original list of 7 numbers shown above.

Inside the sampvoice object (figure 4.21), the message is then used to control the tabread4~ and surrounding line~ and vline~ objects. The control takes place with a delay of 5 msec as in the previous sampler example. Here, however, we must store the seven parameters of the note (whereas, earlier, there were no parameters.) This is done using the six f objects, plus the right inlet of the rightmost delay object. These values are used after the delay of 5 msec. This works in tandem with the ``mute" mechanism described earlier, via the line~ object.

After the 5 msec are up, the vline~ object in charge of generating the wavetable index is given its marching orders (and, simultaneously, the wavetable number is set for the tabread4~ object and the amplitude envelope generator is set on its attack.) The wavetable index must be set discontinuously to the starting index, then ramped to an ending index over an appropriate time duration to obtain the needed transposition. The starting index in samples is just 44.1 times the starting location in milliseconds, plus one to allow for four-point table interpolation. This becomes the third number in a packed list generated by the pack object at the center of the voice patch.

We arbitrarily decide that the ramp will last ten thousand seconds (this is the ``1e+07" appearing in the message box sent to the wavetable index generator), assuming that this is at least as long as any note we will play. Then the ending index is the starting index plus the number of samples to ramp through. At a transposition factor of one, we should move by 441 million samples in the 10 million milliseconds; and proportionally more or less depending on the transposition factor. This transposition factor is computed by the mtof object, dividing by 261.62 (the frequency corresponding to MIDI note 60) so that inputting 60 results in a transposition factor of one.

This and all the other necessary parameters are combined in one message via the pack object so that the following message boxes can easily generate the needed control messages. The only unfamiliar treatment is by the makefilename object, which converts numbers such as ``2" to symbols such as ``sample2" so that the tabread4~ object's wavetable may be set.

At the bottom of the voice patch we see how the summing bus is treated from the inside; an inlet~ object picks up the sum of all the preceding voices, the output of the current voice is added in, and the result is sent on to the next voice via the outlet~ object.


next up previous contents index
Next: Modulation Up: Examples Previous: Additive synthesis: spectral envelope   Contents   Index
Miller Puckette 2005-04-01