Moving samples around in a plugin


#1

Hello there,

 

My current project is an audio analysis plugin - it takes the input buffer, crunches numbers in order to get audio features such as loudness, fundamental frequency estimation, etc. and display it.

 

Currently I do as follows, which seems kind of nasty:

- At each call to ProcessBlock the AudioProcessor makes a copy of the last buffer.

- It also sends a Change Message to all its listeners

- The listeners are UI components requiring this audio data; in their changeListenerCallback they get a const reference to the AudioProcessor last buffer, do their own work of computation and display it.

 

The question is: what is the cleanest way to pass samples (AudioSampleBuffers actually) around?

Most of the JUCE examples rely on a standalone application and thus classes requiring audio data derive from an AudioIODevice, using its callback. How can I mimic this behaviour in a plugin?


Thanks!


#2

I have three classes: 

AudioStreamFifo

AudioStreamSourceManager

AudioStreamListenerManager

I create an AudioStreamFifo for any point in the AudioProcessor where I want to get a stream of data. It manages a lock free FIFO in a buffer with the data. 

The AudioProcessor registers the Fifo's with the AudioStreamSourceManager.

When the Editor starts up it starts an AudioStreamListenerManager which picks up the Fifos from the AudioStreamSource manager.  It looks like this:

class AudioStreamListenerManager {

public:

    AudioStreamListenerManager(AudioStreamSourceManager & src) :

    Thread("AudioStreamListenerManager"),

    source(src)

    {}

    ~AudioStreamListenerManager() {

        stopThread(500);

    }

    void callListeners();

    

    class StreamListener {

    public:

        virtual ~StreamListener() {}

        virtual void processBlock(const String & name,

                                  AudioSampleBuffer * b) = 0;

    };

    

    void registerStreamListener(const String & name,

                                StreamListener & listener);

    

private:

    struct Stream {

        AudioStreamFifo * fifo;

        std::vector<StreamListener *> listeners;

        String name;

    };

    std::vector<Stream> streams;

    AudioStreamSourceManager & source;

};

It all seems to be working quite well ... functions can implement StreamListener and register with AudioStreamListenerManager and as long as I call the callListeners() function regularly all is well:)


#3

Thanks for your answer! Indeed this would be what I'll be looking for.

How do you manager the calls to callListeners() though - within a timer callback, with the timer period updated according the plugin sample rate?


#4

Timer callback usually.  It doesn't have to be synchronised though.   Just rapid enough for the GUI to be responsive and have recent data.

The FIFO can cope with a few blocks in it, and if it has a few parcels of data in it then one call to callListeners() will just call the listeners a few times.

 


#5

Oh, ignore that bit with Thread in it.  I thought briefly about running a separate thread for farming the audio out to the GUI widgets  and then dropped it as an unnecessary headache!