Table of Contents
This tutorial introduces the display of audio waveforms using the AudioThumbnail class. This provides an easy way of drawing any number of waveforms within your audio applications.
Platforms: Windows, Mac OS X, Linux
- This tutorial leads on from Tutorial: Playing sound files, which you should have read and understood first. It also assumes that you are familiar with the Graphics class and the Component::paint() function for performing drawing within a component (see Tutorial: The Graphics class).
Download the demo project for this tutorial here: tutorial_audio_thumbnail.zip. Unzip the project and open it in your IDE.
If you need help with this step, see Tutorial: Getting started with the Projucer.
The demo project presents three buttons in the same way as Tutorial: Playing sound files (for opening, playing, and stopping a sound file).
There is also a rectangular area where the waveform from the sound file can be drawn. In its default state (with no sound file loaded) the application looks like this:
Once a sound file is loaded, the application looks like this:
Drawing an audio waveform, especially for long files, generally involves storing a low resolution version of the audio data in a format that makes drawing the waveform efficient and also clear to the user. The AudioThumbnail class handles this low resolution version for you and it is created and updated when needed.
The first important point is that the AudioThumbnail class is not a subclass of the Component class. The AudioThumbnail class is used to perform the drawing of the audio waveform within the
paint() function of another Component object. The code below shows how to add this functionality based on the demo project from Tutorial: Playing sound files.
MainContentComponent class we need to add two members: an AudioThumbnailCache object and an AudioThumbnail object. The AudioThumbnailCache class is used to cache the necessary low resolution version of one or more audio files. This means, for example, if we close a file, open a new file, then return to open the first file, the AudioThumbnailCache will still contain the low resolution version of the first file and won't need to rescan and recalculate the data. Another useful feature is that AudioThumbnailCache objects can be shared between different instances of the AudioThumbnail class
If statically allocated objects like this are used, it is important that the AudioThumbnailCache object  is listed before the AudioThumbnail object  since it is passed as an argument to the AudioThumbnail constructor. It is also important that the AudioFormatManager object  is listed before the AudioThumbnail object for the same reason.
In the initialiser list for the
MainContentComponent constructor we set up these objects:
- : The AudioThumbnailCache objects must be constructed with the number of thumbnails to store.
- : The AudioThumbnail object itself needs to be constructed by telling it how many source samples will be used to create a single thumbnail sample. This governs the resolution of the low resolution version. The other two arguments are the AudioFormatManager and AudioThumbnailCache objects, as discussed above.
The AudioThumbnail class is also a type of ChangeBroadcaster class. We can register as a listener for changes  (in our
MainContentComponent constructor). These changes will be when the AudioThumbnail object has changed such that we need to update our drawing of the waveform.
transportSourceChanged() function just contains our original code for responding to changes in the AudioTransportSource object:
paint() function, first we calculate the rectangle into which we will draw. Then we check how many channels the AudioThumbnail object contains, which tells us whether we have a file loaded or not:
If we have no file loaded then we display the message No File Loaded by passing our
paintIfNoFileLoaded() function the Graphics object and the bounds rectangle:
The important part is next. If we do have a file loaded we can draw the waveform:
- : Notice that we set the current colour for the Graphics object. This will govern the colour used by the AudioThumbnail object to draw the waveform.
- : We call the AudioThumbnail::drawChannels() function passing it the Graphics object with which to draw, the rectangle into which it should draw, the start and end times (in seconds), and the vertical zoom factor. Here we use the AudioThumbnail::getTotalLength() function to get the file duration so that we can draw the whole file. (We could have obtained the length from our AudioTransportSource object using the AudioTransportSource::getLengthInSeconds() function to get the same result.)
This covers all the basic points for using an AudioThumbnail object.
- In practice you will commonly want to display only certain regions of the sound files. It should be clear from the AudioThumbnail::drawChannels() function how simple this is to implement using JUCE. Try modifying the code to display only a specific region of the file.
In this section we will walk you through adding a vertical line to the display that will show the current time position of the file playback.
First of all we need to add the Timer class to our list of base classes :
Then we need to make the timer callback repaint our component. Make sure this code is added to the
private section as you will notice we inherited privately from the Timer class:
MainContentComponent constructor we need to start the timer  — every 40ms should be sufficient:
In fact you could delay starting the timer, by starting it once the file is successfully opened.
Finally, to draw the line we need to calculate the position of the line and draw it after drawing the thumbnail:
- : Store the length of the file in a variable since we need to use this value twice.
- : The position is calculated as a proportion of the total length of the audio file. The position to draw the line needs to be based on the same proportion of the width of the rectangle that the thumbnail is drawn within. We need to offset the drawing position based on the x-coordinate of the rectangle.
- : Here we draw a line that is 2 pixels wide between the top (y) and bottom of the rectangle.
And that's it: you should be able to build and run the application now.
- The source code for this modified version of the application can be found in the
MainComponent_02.cppfile in the
Sourcedirectory of the demo project.
- The problem with this example is that we force the component to be repainted every 40ms. While this may be acceptable for a simple example you will probably hit performance problems in more complex cases. Take a look at the exercise, below, for more on this.
Separate the drawing into a separate child components (see Tutorial: The Component class, parents, and children). You should have three components:
- A component that draws the audio waveform.
- A component that draws the playback position as a vertical line.
- The main parent component that contains these two child components (laid on top of each other).
Not only will this make the code easier to follow but, if done correctly, it will be much more efficient since we can avoid redrawing the waveform every frame. You could also add functionality to change the playback position if the user clicks on the waveform (see Tutorial: Responding to mouse events).
- The source code for possible implementations of this exercise can be found in the
MainComponent_04.cppfiles in the
Sourcedirectory of the demo project.
This tutorial has introduced the AudioThumbnail class and how it can be integrated into an audio application. In particular we have covered:
- Initialising AudioThumbnail and AudioThumbnailCache objects.
- Using the AudioThumbnail class within a component.
- Structuring components so that content that is complex to draw isn't forced to be redrawn unnecessarily.
Generated on Fri Jan 12 2018 09:51:15 for JUCE by 1.8.13