Including an Audio Waveform component into a Audio Plugin

Hello so currently I am trying to test out putting the AudioWaveformTutorial into an Audio Plugin (end goal is to be able to have the user to import their own sound library to the plugin). Currently I have the following header file included in the PluginEditor.cpp .

#ifndef H_SAMPLELIBRARY
#define H_SAMPLELIBRARY

>  class SampleLibrary :   public AudioAppComponent,
>                         private ChangeListener,
>                         private ButtonListener
> {
> public: 
>     SampleLibrary()
>         :state (Stopped),
>         thumbnailCache (5),     //AudioThumbnailCache number of thumbnails to store
>         thumbnail (512, formatManager, thumbnailCache)      //AudioThumbnail how many source samples to create 
>     {
>         formatManager.registerBasicFormats();
>         transportSource.addChangeListener(this);
>         thumbnail.addChangeListener (this);
>         
>         setAudioChannels (2,2);
>     }
>     ~SampleLibrary()
>     {
>         shutdownAudio();
>     }
>     void prepareToPlay (int samplesPerBlockExpected, double sampleRate) 
>     {
>         transportSource.prepareToPlay (samplesPerBlockExpected, sampleRate);
>     }
>     void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) 
>     {
>         if (readerSource == nullptr)
>             bufferToFill.clearActiveBufferRegion();
>         else 
>             transportSource.getNextAudioBlock(bufferToFill);
>     }
>     void releaseResources()
>     {
>         transportSource.releaseResources();
>     }
>     void changeListenerCallback (ChangeBroadcaster* source)
>     {
>         if (source == &transportSource) transportSourceChanged();
>         if (source == &thumbnail)       thumbnailChanged();
>     }
>     void buttonClicked (Button* button) override
>     {
>         if (button == &openButton)  openButtonClicked();
>         if (button == &playButton)  playButtonClicked();
>         if (button == &stopButton)  stopButtonClicked();
>     }
>     
>     private:
    >     enum TransportState
    >     {
    >         Stopped,
    >         Starting,
    >         Playing,
    >         Stopping
>     };
>     void changeState (TransportState newState)
>     {
>         if (state != newState)
>         {
>             state = newState;

>             switch (state)
>             {
>                 case Stopped:
>                     stopButton.setEnabled (false);
>                     playButton.setEnabled (true);
>                     transportSource.setPosition (0.0);
>                     break;

>                 case Starting:
>                     playButton.setEnabled (false);
>                     transportSource.start();
>                     break;

>                 case Playing:
>                     stopButton.setEnabled (true);
>                     break;

>                 case Stopping:
>                     transportSource.stop();
>                     break;

>                 default:
>                     jassertfalse;
>                     break;
>             }
>         }
>     }
>     void transportSourceChanged()
>     {
>         changeState (transportSource.isPlaying() ? Playing : Stopped);
>     }

>     void thumbnailChanged()
>     {
>         repaint();
>     }

>     void paintIfNoFileLoaded (Graphics& g, const Rectangle<int>& thumbnailBounds)
>     {
>         g.setColour (Colours::darkgrey);
>         g.fillRect (thumbnailBounds);
>         g.setColour (Colours::white);
>         g.drawFittedText ("No File Loaded", thumbnailBounds, Justification::centred, 1.0f);
>     }

>     void paintIfFileLoaded (Graphics& g, const Rectangle<int>& thumbnailBounds)
>     {
>         g.setColour (Colours::white);
>         g.fillRect (thumbnailBounds);

>         g.setColour (Colours::red);                                     // [8]

>         thumbnail.drawChannels (g,                                      // [9]
>                                 thumbnailBounds,
>                                 0.0,                                    // start time
>                                 thumbnail.getTotalLength(),             // end time
>                                 1.0f);                                  // vertical zoom
>     }

>     void openButtonClicked()
>     {
>         FileChooser chooser ("Select a Wave file to play...",
>                              File::nonexistent,
>                              "*.wav");

>         if (chooser.browseForFileToOpen())
>         {
>             File file (chooser.getResult());
>             AudioFormatReader* reader = formatManager.createReaderFor (file);

>             if (reader != nullptr)
>             {
>                 ScopedPointer<AudioFormatReaderSource> newSource = new AudioFormatReaderSource (reader, true);
>                 transportSource.setSource (newSource, 0, nullptr, reader->sampleRate);
>                 playButton.setEnabled (true);
>                 thumbnail.setSource (new FileInputSource (file));          // [7]
>                 readerSource = newSource.release();
>             }
>         }
>     }

>     void playButtonClicked()
>     {
>         changeState (Starting);
>     }

>     void stopButtonClicked()
>     {
>         changeState (Stopping);
>     }

>     //==========================================================================
>     TextButton openButton;
>     TextButton playButton;
>     TextButton stopButton;

>     AudioFormatManager formatManager;                    // [3]
>     ScopedPointer<AudioFormatReaderSource> readerSource;
>     AudioTransportSource transportSource;
>     TransportState state;
>     AudioThumbnailCache thumbnailCache;                  // [1]
>     AudioThumbnail thumbnail;                            // [2]

> };

the current errors I have are:

  • several “undeclared identifiers” (shutdownAudio, transportSource, etc.)
  • “expected class name” for the public AudioAppComponent

Sorry for that wild mess. Essentially I am having trouble using child components while developing a plugin. I need to input component to plugin to either open one wav file or to access a whole library already stored on the computer. I also want to display the waveform of the wav file. I am using the auto-generated PluginProcessor and PluginEditor components by JUCE (if this is not a good way to create a plugin using child components please do tell me). I have gotten a string of errors in my attempt including multiple “undeclared identifiers” as well as a “expected class name” error that points towards public AudioAppComponent

where is your #include JuceHeader.h ?

Also, are you including the proper modules in your ProJucer project file before you save and open in the IDE?

1 Like
  • “expected class name” for the public AudioAppComponent

This is the problem. The other errors relate to this missing class…

You don’t want to be using AudioAppComponent in a plugin. It’s not needed and likely just going to be harmful anyway because it accesses the audio IO hardware.

2 Likes

So what would be a good way to display a waveform in a plugin?

There’s the AudioVisualiserComponent class, and also the dRowAudio oscilloscope class. Rolling your own isn’t too hard either, both those classes should give you an idea of how to do it.

4 Likes

For a simple waveform to be displayed is it not possible to use the AudioThumbnail and the AudioThumbnailCache classes in a component? I constantly get “unknown type name” errors when including them in my component.

Have you added the Juce audio_utils module into your project?

1 Like

There’s the AudioVisualiserComponent class, and also the dRowAudio oscilloscope class. Rolling your own isn’t too hard either, both those classes should give you an idea of how to do it.

I am bit lost using AudioVisualiserComponent. If I use it in the PluginEditor.h, how can I access to the AudioSampleBuffer from the PluginProcessor.h.

Best regards,
Paulo

You can’t access it directly, because it is called from the audio thread, where you can’t share variables without synchronisation. I.e. you would impose the low priority of the GUI thread on the high priority audio thread, eventually bringing it to a halt.

You will have to create a FIFO, that the audio thread is pushing the samples from AudioSampleBuffer, so the low GUI can read from in it’s own time.

Make sure to allow skipping the pushing part, in case the GUI can’t keep up, or in offline bouncing.