This tutorial shows how to process audio input and pass it to the audio output.
Level: Beginner
Platforms: Windows , macOS , Linux
Classes: Random, BigInteger, AudioBuffer
Download the demo project for this tutorial here: PIP | ZIP . Unzip the project and open the first header file in the Projucer.
If you need help with this step, see Tutorial: Projucer Part 1: Getting started with the Projucer.
The demo project modulates an incoming signal with white noise. The level of the white noise can be changed which affects the level of the overall output (see Tutorial: Control audio levels for the technique used to generate the white noise). The result is a very "fuzzy" version of the input signal.
It's probably best to use a separate microphone and headphones. Of course, you will need some kind of audio input device for the project to work correctly.
This tutorial uses the AudioAppComponent class as the basis for the demo project application. In other tutorials we generate audio within the getNextAudioBlock()
function — see Tutorial: Build a white noise generator, Tutorial: Control audio levels, and Tutorial: Build a sine wave synthesiser. In this tutorial we read the audio input and output some audio too. In the MainContentComponent
constructor we request two audio inputs and two audio outputs:
It is important to know that the input and output buffers are not completely separate. The same buffer is used for the input and output. You can test this by temporarily commenting out all of the code in the getNextAudioBlock()
function. If you then run the application, the audio input will be passed directly to the output. In the getNextAudioBlock()
function, the number of channels in the AudioSampleBuffer object within the bufferToFill
struct may be larger than the number input channels, the number of output channels, or both. It is important to access only the data that refers to the number of input and output channels that you have requested, and that are available. In particular, if you have more input channels than output channels you must not modify the channels that should contain read-only data.
In the getNextAudioBlock()
function we obtain BigInteger objects that represent the list of active input and output channels as a bitmask (this is similar to the std::bitset class or using a std::vector<bool> object). In these BigInteger objects the channels are represented by a 0 (inactive) or 1 (active) in the bits comprising the BigInteger value.
To work out the maximum number of channels over which we need to iterate, we can inspect the bits in the BigInteger objects to find the highest numbered bit. The maximum number of channels will be one more than this.
Then obtain the desired level from our level slider and move on to process the audio by processing each output channel, one at a time. If the maximum number of input channels is zero (which could happen even though we requested two channels if our hardware has no audio inputs) then we must not try to process the audio. In this case we simply zero the output channel buffer (to output silence). Individual output channels may also be inactive, so we check the state of the channel and also output silence for that channel if it is inactive:
Then we go on to process the input data through to the output:
The code should be reasonably self-explanatory but here are a few highlights:
In this tutorial we have introduced processing audio from an audio input in a JUCE application. In particular, you should now know: