in pedalboard/plugins/MP3Compressor.h [169:254]
virtual void prepare(const juce::dsp::ProcessSpec &spec) override {
bool specChanged = lastSpec.sampleRate != spec.sampleRate ||
lastSpec.maximumBlockSize < spec.maximumBlockSize ||
lastSpec.numChannels != spec.numChannels;
if (!encoder || specChanged) {
reset();
if (lame_set_in_samplerate(encoder.getContext(), spec.sampleRate) != 0 ||
lame_set_out_samplerate(encoder.getContext(), spec.sampleRate) != 0) {
// TODO: It would be possible to add a resampler here to support
// arbitrary-sample-rate audio.
throw std::domain_error(
"MP3 only supports 32kHz, 44.1kHz, and 48kHz audio. (Was passed " +
juce::String(spec.sampleRate / 1000, 1).toStdString() +
"kHz audio.)");
}
if (lame_set_num_channels(encoder.getContext(), spec.numChannels) != 0) {
// TODO: It would be possible to run multiple independent mono encoders.
throw std::domain_error(
"MP3Compressor only supports mono or stereo audio. (Was passed " +
std::to_string(spec.numChannels) + "-channel audio.)");
}
if (lame_set_VBR(encoder.getContext(), vbr_default) != 0) {
throw std::domain_error(
"MP3 encoder failed to set variable bit rate flag.");
}
if (lame_set_VBR_quality(encoder.getContext(), vbrLevel) != 0) {
throw std::domain_error(
"MP3 encoder failed to set variable bit rate quality to " +
std::to_string(vbrLevel) + "!");
}
int ret = lame_init_params(encoder.getContext());
if (ret != 0) {
throw std::runtime_error(
"MP3 encoder failed to initialize MP3 encoder! (error " +
std::to_string(ret) + ")");
}
// Why + 528 + 1? Pulled directly from the libmp3lame code.
// An explanation supposedly exists in the old mp3encoder mailing list
// archive. These values have been confirmed empirically, however.
encoderInStreamLatency =
lame_get_encoder_delay(encoder.getContext()) + 528 + 1;
// Why add this latency? Again, not 100% sure - this has just
// been empirically observed at all sample rates. Good thing we have
// tests.
if (lame_get_in_samplerate(encoder.getContext()) >= 32000) {
encoderInStreamLatency += 1152;
} else {
encoderInStreamLatency += 576;
}
// More constants copied from the LAME documentation, to ensure
// we never overrun the mp3 buffer:
mp3Buffer.ensureSize((1.25 * MAX_LAME_MP3_BUFFER_SIZE_SAMPLES) + 7200);
// Feed in some silence at the start so that LAME buffers up enough
// samples Without this, we underrun our output buffer at the end of the
// stream.
std::vector<short> silence(ADDED_SILENCE_SAMPLES_AT_START);
// Use the integer version rather than the float version for a bit of
// extra speed.
mp3BufferBytesFilled = lame_encode_buffer(
encoder.getContext(), silence.data(), silence.data(), silence.size(),
(unsigned char *)mp3Buffer.getData(), mp3Buffer.getSize());
if (mp3BufferBytesFilled < 0) {
throw std::runtime_error(
"Failed to prime MP3 encoder! This is an internal Pedalboard error "
"and should be reported.");
}
encoderInStreamLatency += silence.size();
// Allow us to buffer up to (expected latency + 1 block of audio)
// between the output of LAME and data returned back to Pedalboard.
outputBuffer.setSize(encoderInStreamLatency + spec.maximumBlockSize);
lastSpec = spec;
}
}