virtual void prepare()

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;
    }
  }