Sampler implementation


I’m looking for some advice for the design of an audio plugin, essentially some indications on what classes would be most appropriate for the few things I have left to implement.

I’m building a plugin that loads an audio clip and shows it in an AudioThumbnail, where I can save subsections within the audio clip. I have the start and end times of every subsection stored in a ValueTree. Here are the main objects, taken from tutorials, that I currently use for audio file loading, visualization and playback:

SamplerAudioProcessor& processor;
AudioDeviceManager& deviceManager;
AudioFormatManager formatManager;
TimeSliceThread thread { "audio file preview" };
File currentAudioFile;
AudioSourcePlayer& sourcePlayer;
AudioTransportSource& transportSource;
std::unique_ptr<AudioFormatReaderSource> currentAudioFileSource;
std::unique_ptr<SamplerThumbnail> thumbnail;

Here’s some of the things I want to have:

  • playback of subsections triggered by MIDI notes. For now, I have a “triggerNote” field in the subsection struct, but I haven’t found a way to map it to play the subsection on MIDI events.
  • two playback modes: one shot playback, where a noteOn event triggers the playback of the whole subsection; ADSR playback, where, for example, a noteOn event starts the playback of a subsection and a noteOff event cuts it off.
  • two interference modes: cut the playback of subsections when subsections are played (exclusive playback), and having subsections play over each other (non exclusive playback).
  • use the plugin as an instrument, in order to arrange sequences of subsections in a DAW’s playlist.

I have thought of using an AudioSubsectionReader per subsection. I would have the one AudioFormatReader that holds the initial audio clip, and as many AudioSubsectionReaders reading from the AudioFormatReader as there are subsections. I haven’t thought of the exclusive playback mode yet, but I think a MixerAudioSource that takes a number of AudioTransportSources might be feasible for non exclusive playback. I believe this idea leads to having the subsection & its AudioSubsectionReader being read by an AudioFormatReaderSource being read by an AudioTransportSource being managed by a MixerAudioSource, which seems a little complex.

Start here :slight_smile:

True! I came across the SamplerSound and SamplerVoice classes.
I guess I can make SamplerSounds from AudioFormatReaders: AudioSubsectionReaders in this case. I suppose that means no need for a MixerAudioSource or multiple AudioTransportSources?

Note that the Juce provided SamplerSound and SamplerVoice classes are very simple, not easily customizable and load the sample to be played in memory. You will instead probably want to make your own classes derived from SynthesiserSound and SynthesiserVoice.

Is loading the sample in memory a big drawback? I think SamplerSound and SamplerVoice may be all I need, given that all I want to do is grab a subsection from a source and play it with a Synthesizer

It’s of course up to you to decide if having the samples in memory is a significant drawback or not. Each subsection needs to be stored fully by the SamplerSounds in memory even if there would be shared sections in the audio. (The SamplerSound just reads the given AudioFormatReader into an AudioBuffer as 32 bit floating point samples, there is no smart sharing or anything like that going on.)

It depends. It’s not a big issue anymore on 64 bit systems if you don’t load huge or a lot of different samples. I would keep it in memory for samples that are only a few megabytes in size.
Keep in mind that the memory usage doubles if you load a 16bit wav file into a 32 bit float array.

Otherwise you maybe have long loading times and you use too much RAM.

Sorry if reviving this thread is a problem, but would using SynthesiserSound and SynthesiserVoice allow me to make use of the smart sharing you mentioned? If so, how?