A newer version of Max is available. Click here to access the latest documentation.
Tutorial 20: MIDI Sampler Control
Open the tutorial.
Basic sampler features
In this chapter we demonstrate a design for playing pre-recorded samples from a MIDI keyboard. This design implements some of the main features of a basic sampler keyboard: assigning samples to regions of the keyboard, specifying a base (untransposed) key location for each sample, playing samples back with the proper transposition depending on which key is played, and making polyphonic voice assignments. For the sake of simplicity, this patch does not implement control from the pitchbend wheel or mod wheel, but the method for doing so would not be much different from that demonstrated in the previous two chapters.
In this patch we use the groove~ object to play samples back at various speeds, in some cases using looped samples. As was noted in Tutorial 19, if we want a polyphonic instrument we need as many sound-generating objects as we want separate simultaneous notes. In this tutorial patch, we use four copies of a subpatch called samplervoice~ to supply four-voice polyphony. As in Tutorial 19— we use a poly object to assign a voice number to each MIDI note, and we use route to send the note information to the correct samplervoice~ subpatch.
poly assigns a voice number to each MIDI note, to send information to the correct subpatch
Before we examine the workings of the samplervoice~ subpatch, it will help to review what information is needed to play a sample correctly.
• 1. The sound samples must be read into memory (in buffer~ objects), and a list of the memory locations (buffer~ names) must be kept.
• 2. Each sample must be assigned to a region of the keyboard, and a list of the key assignments must be kept.
• 3. A list of the base key for each region -- the key at which the sample should play back untransposed -- must be kept.
• 4. A list of the loop points for each sample (and whether looping should be on or off) must be kept.
• 5. When a MIDI note message is received, and is routed to a samplervoice~ subpatch, the groove~ object in that subpatch must first be told which buffer~ to read (based on the key region being played), how fast to play the sample (based on the ratio between the frequency being played and the base key frequency for that region), what loop points to use for that sample, whether looping is on or off, and what amplitude scaling factor to use based on the note- on velocity.
In this patch, the samples are all read into memory when the patch is first loaded.
• Double-click on the p samplebuffers subpatch to open its Patcher window.
You can see that six samples have been loaded into buffer~ objects named sample1, sample2, etc. If, in a performance situation, you need to have access to more samples than you can store at once in RAM, you can use read messages with filename arguments to load new samples into buffer~ objects as needed.
• Close the subpatch window. Click on the message box marked ‘keyboard sample assignments’.

This stores a set of numbered key regions in the funbuff object. (This information could have been embedded in the funbuff and saved with the patch, but we left it in the message box here so that you can see the contents of the funbuff.) MIDI key numbers 0 to 40 are key region 1, keys 41 to 47 are key region 2, etc. When a note-on message is received, the key number goes into funbuff, and funbuff reports the key region number for that key. The key region number is used to look up other vital information in the coll.
Note-on key number finds region number in funbuff, which looks up sample info in coll
• Double-click on the coll object to see its contents.
1, 24 sample1 0 0 0;
2, 33 sample2 0 0 0;
3, 50 sample3 0.136054 373.106537 1;
4, 67 sample4 60.204079 70.476189 1;
5, 84 sample5 0 0 0;
6, 108 sample6 0 0 0;
coll contains sample information for each key region
The key region number is used to index the information in coll. For example, whenever a key from 48 to 52 is pressed, funbuff sends out the number 3, and the information for key region 3 is recalled and sent to the appropriate samplervoice~ subpatch. The data for each key region is: base key, buffer~ name, loop start time, loop end time, and loop on/off flag.
The voice number from poly opens the correct outlet of gate so that the information from coll goes to the right subpatch.

Playing a sample: the samplervoice~ subpatch
• Close the coll window, and double-click on one of the samplervoice~ subpatch objects to open its Patcher window.
The samplervoice~ subpatch
You can see that the information from coll is unpacked in the subpatch and is sent to the proper places to prepare the groove~ object for the note that is about to be played. This tells groove~ what buffer~ to read, what loop times to use, and whether looping should be on or off. Then, when the note information comes in the left inlet, the velocity is used to send an amplitude value to the *~ object, and the note-on key number is used (along with the base key number received from the right inlet) to calculate the proper playback speed for groove~ and to trigger groove~ to begin playback from time 0.
MSP sample rate vs. audio file sample rate
• Close the subpatch window.
You're almost ready to begin playing samples, but there is one more detail to attend to first. To save storage space, the samples used in this patch are mono AIFF files with a sample rate of 22,050 Hz. To hear them play properly you should set the sample rate of MSP to that rate.
• Double-click on the dac~ object to open the DSP Status window. Set the Sampling Rate to 22.050 kHz, then close the DSP Status window.
• Note: Resetting the sampling rate may not be possible, depending on your hardware.
The difference between the sample rate of an audio file and the sample rate being used in MSP is a potential problem when playing samples. This method of resolving the difference suffices in this situation because the audio files are all at the same sample rate and because these samples are the only sounds we will be playing in MSP. In other situations, however, you're likely to want to play samples (perhaps with different sampling rates) combined with other sounds in MSP, and you'll want to use the optimum sampling rate.
For such situations, you would be best advised to use the ratio between the audio file sample rate and the MSP sample rate as an additional factor in determining the correct playback speed for groove~. For example, if the sample rate of the audio file is half the sample rate being used by MSP, then groove~ should play the sample half as fast.
You can use the objects info~ and dspstate~ to find out the sampling rate of the sample and of MSP respectively, as demonstrated in the following example.
Calculate playback speed based on the sampling rates of the audio file and of MSP
The note-on key number is used first to recall the information for the sample to be played. The name of a buffer~ is sent to groove~ and info~. Next, a bang is sent to dspstate~ and info~. Upon receiving a bang, dspstate~ reports the sampling rate of MSP and info~ reports the sampling rate of the AIFF file stored in the buffer~. In the lower left part of the example, you can see how this sampling rate information is used as a factor in determining the correct playback speed for groove~.
Playing samples with MIDI
• Turn audio on and set the ‘Output Level’ number box to a comfortable listening level. Play a slow chromatic scale on the MIDI keyboard to hear the different samples and their arrangement on the keyboard.
To arrange a unified single instrument sound across the whole keyboard, each key region should contain a sample of a note from the same source. In this case, though, the samples are arranged on the keyboard in such a way as to make available a full ‘band’ consisting of drums, bass, and keyboard. This sort of multi-timbral keyboard layout is useful for simple keyboard splits (such as bass in the left hand and piano in the right hand) or, as in this case, for accessing several different sounds on a single MIDI channel with a sequencer.
• For an example of how a multi-timbral sample layout can be used by a sequencer, click on the toggle marked ‘Play Sequence’. Click on it again when you want to stop the sequence. Turn audio off. Double-click on the p sequence object to open the Patcher window of the subpatch.
The p sequence subpatch
The seq sampleseq.midi object contains a pre-recorded MIDI file. The midiparse object sends the MIDI key number and velocity to poly in the main patch. Each time the sequence finishes playing, a bang is sent out the right outlet of seq; the bang is used to restart the seq immediately, to play the sequence as a continuous loop. When the sequence is stopped by the user, a bang is sent to midiflush to turn off any notes currently being played.
• When you have finished with this patch, don't forget to open the DSP Status window and restore the Sampling Rate to its original setting.
Summary
To play samples from the MIDI keyboard, load each sample into a buffer~ and play the samples with groove~. For polyphonic sample playback, you will need one groove~ object per voice of polyphony. You can route MIDI notes to different groove~ objects using voice assignments from the poly object.
To assign each sample to a region of the MIDI keyboard, you will need to keep a list of key regions, and for each key region you will need to keep information about which buffer~ to use, what transposition to use, what loop points to use, etc. A funbuff object is good for storing keyboard region assignments. The various items of information about each sample can be best stored together as lists in a coll, indexed by the key region number. When a note is played, the key region is looked up in the funbuff, and that number is used to look up the sample information in coll.
The proper transposition for each note can be calculated by dividing the played frequency (obtained with the mtof object) by the base frequency of the sample. The result is used as the playback speed for groove~. If the sampling rate of the recorded samples differs from the sampling rate being used in MSP, that fact must be accounted for when playing the samples with groove~. Dividing the audio file sampling rate by the MSP sampling rate provides the correct factor by which to multiply the playback speed of groove~. The sampling rate of MSP can be obtained with the dspstate~ object. The sampling rate of the AIFF file in a buffer~ can be obtained with info~ (Remember -- resetting the sampling rate may not be possible on your hardware).
Note-on velocity can be used to control the amplitude of the samples. An exponential mapping of velocity to amplitude is usually best. Multi-timbral sample layouts on the keyboard can be useful for playing many different sounds, especially from a sequencer. The end-of-file bang from the right outlet of seq can be used to restart the seq to play it in a continuous loop. If the MIDI data goes through a midiflush object, any notes that are on when the seq is stopped can be turned off by sending a bang to midiflush.

See Also

Name Description
buffer~ Store audio samples
dspstate~ Report current DSP settings
groove~ Variable-rate looping sample playback
poly~ Polyphony/DSP manager for patchers