Skip to content

Playing the Buffer

passivist edited this page Jan 16, 2017 · 5 revisions

Playing the buffer

Let's take a closer look at the processBlock function now. As discussed in the previous chapter the processBlock function takes a reference to an AudioSampleBuffer as an argument. This buffer represents the current audio block and after execution of the function is passed to the sound card. Our goal now is to fill the buffer that is passed in every block with the right samples from our sound file.

const int numSamplesInBlock = buffer.getNumSamples(); // [1]
const int numSamplesInFile  = fileBuffer.getNumSamples();

for (int i = getTotalNumInputChannels(); i < getTotalNumOutputChannels(); ++i) // [2]
    buffer.clear (i, 0, buffer.getNumSamples());

// return from the function if there's nothing in our buffer
if(fileBuffer.getNumSamples() == 0) return; // [3]

// [4.1]
for(int sample=0; sample<numSamplesInBlock; ++sample){
    for(int channel=0; channel<buffer.getNumChannels(); ++channel){
      // [4.2]
        float* channelData = buffer.getWritePointer(channel);
        const float* fileData = fileBuffer.getReadPointer(channel%fileBuffer.getNumChannels());
        // [4.3]
        channelData[sample] = fileData[filePosition];
    }
    // [4.4]
    if(filePosition < numSamplesInFile){
        ++filePosition;
    } else {
        filePosition = 0;
    }
}
  1. We define two constant integer variables: one holds the number of samples in the current audio block, the other the number of samples in the buffer holding the data from our file.

  2. This code deletes all the data from the buffer. This is necessary because we can't guarantee that the address where we store our buffer in memory won't still contain any garbage data from previous operations. Reading out this data might result in unwanted (and sometimes quite loud) noise. 1.

  3. We should take care that the program doesn't try to read the buffer before it is loaded. We check every execution of the function if there is data in our buffer. If there is none we immediately exit or 'return' from the function. We could also put test if(fileBuffer.getNumChannels != 0) and put the rest of the function into the body of that if statement but I find this solution adds a little less clutter.

  4. Here is the code that does the actual reading and copying of the samples.

  5. We first notice two nested for loops. The outer loop iterates through all the samples in our current audio-block and the inner loop iterates through all the channels in our block.

  6. Inside the loops we first make the data for the current channel in the current block and the buffer holding the samples we want to read accessible for writing and reading respectively. Even though JUCE has functions for reading and writing whole blocks of samples 'at once' we do it sample by sample because it makes a lot of things we want to do later much less complex. The methods getReadPointer and getWritePointer return pointers to the data inside a buffer, these pointers can be used in C++ (and C) like arrays. Because we are using the pointers in this way we must assure that our index never is out of bounds of the buffer. If we try to read or write a sample outside of the bounds of the buffer the program might crash or output random noise. For the fileBuffer we also take the modulo of the number of channels in the block with the number of channels in the fileBuffer so that if the fileBuffer has less channels than the audio-block (for example when the file we are loading is mono) we never go out of bounds.

  7. In this line the sample at the position filePosition in the fileBuffer is copied into the buffer buffer (which is the current audio-block) at the position sample.

  8. After this is done for all channels we check if filePosition is smaller than numSamplesInFile. If it is we increment filePosition by one, if it isn't we reset filePosition to 0. This makes the file loop.

At this point we should compile again and see if everything is working. Once we open the GUI of the plugin we expect the sample to load and play until we shut the program off.

This is still a pretty useless plugin. It only loads a sound file at a hardcoded path and plays it looping forever. In the next chapter we will extent the functionality of the plugin by implementing a way to load arbitrary sound files.

<<< last Chapter next Chapter >>>


1: Note that we don't need to put brackets ("{}") around the body of if/else/while or for statements if the body is just one line long.

Clone this wiki locally