Each note has a set of samples, and as long as the key is pressed, the samples must be played randomly one after the other without stopping. That is, it looks like a cycle of several samples.
// Check if the sample is over:
if (playingSample->isPlaying() == false
|| playingSample->getCurrentPosition() >= playingSample->getLengthInSeconds()
|| playingSample->hasStreamFinished())
{
PlaySample(playedNoteNumber);
}
// Randomly pick a new sample and play it:
void NewProjectAudioProcessor::PlaySample(int noteNumber)
{
int countOfNoteSamples = notesAndSamples.getReference(noteNumber).size();
if (countOfNoteSamples > 0)
{
int randomSampleNumber = Random::getSystemRandom().nextInt(countOfNoteSamples);
playingSample = notesAndSamples.getReference(noteNumber).getReference(randomSampleNumber);
playingSampleExist = true;
playingSample->setPosition(0.0);
playingSample->start();
}
}
// Selected sample plays:
void NewProjectAudioProcessor::GetNextAudioBlockForPlayingSamples(AudioBuffer<float>& buffer)
{
if(playingSampleExist)
{
playingSample->getNextAudioBlock(juce::AudioSourceChannelInfo(buffer));
}
}
Unfortunately, I get small pauses between samples:
I debugged processBlock and got some interesting results:
// Plays the end of the first sample
playingSample->isPlaying(): true
playingSample->getCurrentPosition(): 0.0914966
playingSample->getLengthInSeconds(): 0.1
playingSample seconds left: 0.0085034
// First sample ended and silence lasted 0.00165533 seconds
playingSample->isPlaying(): false
playingSample->getCurrentPosition(): 0.101655
playingSample->getLengthInSeconds(): 0.1
playingSample seconds left: -0.00165533
// Second sample started playing
playingSample->isPlaying(): true
playingSample->getCurrentPosition(): 0.0102268
playingSample->getLengthInSeconds(): 0.1
playingSample seconds left: 0.0897732
I think the sample ends before the new cycle of processBlock has time to start. How to make a note play without pauses?
The TransportAudioSource pulls from an AudioFormatReaderSource. Maybe you even have some buffering going on (you should!). So that takes some time to rewind which could be the one block silence. When I implemented such a loop I added a spare Buffered AudioFormatReaderSource on standby that would kick in when the end was reached and the exhausted source can be rewound while the spare is polaying. However I still added a crossfade just in case.
Well you cannot, but like I keep saying I wouldn’t use AudioTransportSource at all. You cannot start a note accurately to a sample, which is a requirement for a virtual instrument.
AudioTransportSource is if you create a player with a GUI controlled start/stop button. Because there you need the asynchronous action. But it is not suited for anything else.
What then do you propose? I tried Synthesiser, but it is too multifunctional, has its own logic for handling midi messages, sources, and so on, I will have to completely rewrite it. I just need to load the sound file into some object, store those objects into an array, and start playing any of them aloud with something like play() and stop(). If it doesn’t work perfectly with AudioTransportSource then what solution can you suggest?
I am afraid it’s the juce::Synthesizer class and juce::SynthesizerVoice.
The reason is, the plugin processing is continuously running. There is no start here and end there you could implement, all is happening at the same time and in blocks. So you need some kind of context when you enter for the next block.
The synthesizer classes take care of this context. You are free to implement it yourself, but you will likely end up with something similar. And my advice if you want to write your own, still try to understand how they did it, so you avoid mistakes others already made and corrected.
I found a way! I need to use AudioSampleBuffer. The looping audio tutorial describes how to loop a file. In order for several files to play sequentially, i just need to insert the choice of a new fileBuffer into this condition.
if (position == fileBuffer.getNumSamples())
position = 0;
I read all the files into the AudioSampleBuffer array beforehand and simply select the next element from the array. Works great with no pauses or stutters. Writing your own solution from scratch is much easier than completely rewriting the Synthesizer class.
The only problem is that the sample rate of audio files must match the sample rate of the DAW. I think i can use a GenericInterpolator or something similar to bring the file’s samplerate into line with the DAW if they don’t match.