in pedalboard/ExternalPlugin.h [1227:1314]
py::array_t<float> renderMIDIMessages(py::object midiMessages, float duration,
float sampleRate,
unsigned int numChannels,
unsigned long bufferSize, bool reset) {
// Tiny quality-of-life improvement to try to detect if people have swapped
// the duration and sample_rate arguments:
if ((duration == 48000 || duration == 44100 || duration == 22050 ||
duration == 11025) &&
sampleRate < 8000) {
throw std::invalid_argument(
"Plugin '" + pluginInstance->getName().toStdString() +
"' was called with a duration argument of " +
std::to_string(duration) + " and a sample_rate argument of " +
std::to_string(sampleRate) +
". These arguments appear to be flipped, and may cause distorted "
"audio to be rendered. Try reversing the order of the sample_rate "
"and duration arguments provided to this method.");
}
std::scoped_lock<std::mutex>(this->mutex);
py::array_t<float> outputArray;
unsigned long outputSampleCount = duration * sampleRate;
juce::MidiBuffer midiInputBuffer =
parseMidiBufferFromPython(midiMessages, sampleRate);
outputArray =
py::array_t<float>({numChannels, (unsigned int)outputSampleCount});
float *outputArrayPointer = static_cast<float *>(outputArray.request().ptr);
py::gil_scoped_release release;
if (pluginInstance) {
if (reset)
this->reset();
juce::dsp::ProcessSpec spec;
spec.sampleRate = sampleRate;
spec.maximumBlockSize = (juce::uint32)bufferSize;
spec.numChannels = (juce::uint32)numChannels;
prepare(spec);
if (!foundPluginDescription.isInstrument) {
throw std::invalid_argument(
"Plugin '" + pluginInstance->getName().toStdString() +
"' expects audio as input, but was provided MIDI messages.");
}
if ((size_t)pluginInstance->getMainBusNumOutputChannels() !=
numChannels) {
throw std::invalid_argument(
"Plugin '" + pluginInstance->getName().toStdString() +
"' produces " +
std::to_string(pluginInstance->getMainBusNumOutputChannels()) +
"-channel output, but " + std::to_string(numChannels) +
" channels of output were requested.");
}
std::memset((void *)outputArrayPointer, 0,
sizeof(float) * numChannels * outputSampleCount);
for (unsigned long i = 0; i < outputSampleCount; i += bufferSize) {
unsigned long chunkSampleCount =
std::min((unsigned long)bufferSize, outputSampleCount - i);
std::vector<float *> channelPointers(numChannels);
for (size_t c = 0; c < numChannels; c++) {
channelPointers[c] =
(outputArrayPointer + (outputSampleCount * c) + i);
}
// Create an audio buffer that doesn't actually allocate anything, but
// just points to the data in the output array.
juce::AudioBuffer<float> audioChunk(
channelPointers.data(), channelPointers.size(), chunkSampleCount);
juce::MidiBuffer midiChunk;
midiChunk.addEvents(midiInputBuffer, i, chunkSampleCount, -i);
pluginInstance->processBlock(audioChunk, midiChunk);
}
}
return outputArray;
}