Random access of a sound sample

Image

 

The fact that groove~ can leap to any point in the buffer~ makes it a suitable object for certain kinds of algorithmic fragmented sound playback. In this example it periodically plays a small chunk of sound chosen at random (and with a randomly chosen rate of playback).

The info~ object reports the length of the sound that has been read into the buffer~, and that number is used to set the maximum value for the random object that will be used to choose time locations. When the metro is turned on, its bangs trigger three things: an amplitude envelope, a starting point in ms, and a playback rate. The starting point is some randomly chosen millisecond time in the buffer~. The rate is calculated by choosing a random number from 0 to 200, offsetting it and scaling it so that it is in the range -1 to 1, and using that number as the exponent for a power of 2. That’s converted into a signal by the sig~ object to control the rate of groove~. (2 to the -1 power is 0.5, 2 to the 0 power is 1, and 2 to the 1 power is 2, so the rate will always be some value distributed exponentially between 0.5 and 2.)

The amplitude envelope is important to prevent clicks when groove~ leaps to a new location in the buffer~. The line~ object creates a trapezoidal envelope that is the same duration as the chunk of sound being played. It ramps up to 1 in 10 milliseconds, stays at 1 for some amount of time (the length of the note minus the time needed for the attack and release portions), and ramps back to 0 in 10 ms. Notice that whenever the interval of the metro is changed, that interval minus 20 is stored and is used as the sustain time for subsequent notes. This pretty effectively creates a trapezoidal “window” that fades the sound down to 0 just before a new start time is chosen, then fades it back up to 1 at the time the new note is played.

MSP calculates audio signals several samples at a time, in order to ensure that it always has enough audio to play without ever running out (which would cause clicks). The number of samples it calculates at a time is determined by the signal vector size, which can be set in the Audio Status window. If the sample rate is 44100 Hz, and the signal vector size is 64, this means that audio is computed in chunks of 1.45 ms at a time; a larger signal vector size such as 512 would mean that audio is computed in chunks of 11.61 ms at a time. For this reason, the millisecond scheduler of Max messages might not be in perfect synchrony with the MSP calculation interval based on the signal vector size. This causes the possibility that we will get clicks even if we are trying to make Max messages occur only at moments when the signal is at 0. For that reason, you need to be very careful to synchronize those events carefully. One thing that can help is to synchronize the Max scheduler to send messages only at the same time as the beginning of a signal vector calculation. You can do that by setting the Scheduler in Overdrive and Scheduler in Audio Interrupt options both On in the Audio Status window. Another useful technique (although not so appropriate with groove~, which can only have its starting points triggered by Max messages) is to cause everything in your MSP computations to be triggered by other signals instead of by Max messages.

Playing a sample from RAM

Image

You can use the play~ object to play the contents of a buffer~, simply by sending it a ‘start’ message. By default it starts from the beginning of the buffer~. You can specify a different starting time, in milliseconds, as an argument to the ‘start’ message, or you can specify both a starting time and a stopping time (in ms) as two arguments to the ‘start’ message. In the patch, you can see two examples of the use of starttime and stoptime arguments. The message ‘start 0 420’ reads from time 0 to time 420 ms, then stops. You can cause reverse playback just by specifying a stop time that’s less than the start time; the message ‘start 2000 420’ starts at time 2000 ms and plays backward to time 420 ms and stops. In all of these cases — start with no arguments, start with one argument, or start with two arguments — play~ plays at normal speed. If you include a third argument in the ‘start’ message, that’s the amount of time you want play~ to take to get to its destination; in that way, you can cause play~ to play at a different rate, just by specifying an amount of time that’s not the same as the absolute difference between the starttime and stoptime arguments. For example, the message ‘start 1000 2000 1200’ means “play from time 1000 ms to time 2000 ms in 1200 ms.” Since that will cause the playback to take 6/5 as long as normal playback, the rate of playback will be 5/6 of normal, and thus will sound a minor third lower than normal. (The ratio between the 5th and 6th partials of the harmonic series is a pitch interval of a minor third.) The message ‘start 2000 1000 4000’ will read backward from 2000 ms to 1000 ms in 4000 ms, thus playing backward at 1/4 the original rate, causing extreme slowing and a pitch transposition of two octaves down.

The info~ object, when it receives a bang in its inlet, provides information about the contents of a buffer~ object. Since the buffer~ object sends a bang out its right outlet when it has finished a ‘read’ or ‘replace’ operation, we can use that bang to get information about the sound that has just been loaded in. In this example, we see the length of the buffer, in milliseconds. You can use that information in other parts of your patch.

Getting a sound sample from RAM

Image

 

The buffer~ object holds audio data in RAM as an array of 32-bit floating point numbers (floats). The fact that the sound is loaded into RAM, rather than read continuously off the hard drive as the sfplay~ object does, means that it can be accessed quickly and in various ways, for diverse audio effects (including normal playback).

Every buffer~ must have a unique name as its first argument; other objects can access the contents of the buffer~ just by referring to that name. The next (optional) arguments are the size of the buffer (stated in terms of the number of milliseconds of sound to be held there) and the number of channels (1 for mono, 2 for stereo). If those optional arguments are present, they will be used to limit the amount of sound that can be stored in that buffer~ when you load sound into the buffer~ from a file with the ‘read’ message. If those arguments are absent, when you load in a sound from a file with the ‘read’ message the buffer~ will be resized to to hold the entire file. The ‘replace’ message is like ‘read’ except that it will always resize the buffer~ regardless of the arguments.

The most basic way to use a buffer is simply to access each individual sample by incrementing through the array indices at the sample rate. To do that, the best objects to use are count~, which puts out a signal that increments by one for each sample, and index~, which refers to the buffer~’s contents by the sample index received from count~ and sends that out its outlet. This is good for basic playback with no speed change, or for when you want to access a particular sample within the buffer~.

When count~ receives a ‘bang’, it begins outputting an incrementing signal, starting from 0 by default. You can cause it to start counting from some other starting sample number by sending a number in its left inlet. (You can also cause it to count in a loop by giving it both a starting and ending number. It will count from the starting number up to one less than the ending number, then will loop back to the starting number.)