void write()

in pedalboard/io/WriteableAudioFile.h [477:617]


  void write(py::array_t<SampleType, py::array::c_style> inputArray) {
    const juce::ScopedReadLock scopedReadLock(objectLock);

    if (!writer)
      throw std::runtime_error("I/O operation on a closed file.");

    py::buffer_info inputInfo = inputArray.request();

    unsigned int numChannels = 0;
    unsigned int numSamples = 0;

    if (lastChannelLayout) {
      try {
        lastChannelLayout = detectChannelLayout(inputArray, {getNumChannels()});
      } catch (...) {
        // Use the last cached layout.
      }
    } else {
      // We have no cached layout; detect it now and raise if necessary:
      try {
        lastChannelLayout = detectChannelLayout(inputArray, {getNumChannels()});
      } catch (const std::exception &e) {
        throw std::runtime_error(
            std::string(e.what()) +
            " Provide a non-square array first to allow Pedalboard to "
            "determine which dimension corresponds with the number of channels "
            "and which dimension corresponds with the number of samples.");
      }
    }

    // Release the GIL when we do the writing, after we
    // already have a reference to the input array:
    pybind11::gil_scoped_release release;

    if (inputInfo.ndim == 1) {
      numSamples = inputInfo.shape[0];
      numChannels = 1;
    } else if (inputInfo.ndim == 2) {
      switch (*lastChannelLayout) {
      case ChannelLayout::Interleaved:
        numSamples = inputInfo.shape[0];
        numChannels = inputInfo.shape[1];
        break;
      case ChannelLayout::NotInterleaved:
        numSamples = inputInfo.shape[1];
        numChannels = inputInfo.shape[0];
        break;
      }
    } else {
      throw std::runtime_error(
          "Number of input dimensions must be 1 or 2 (got " +
          std::to_string(inputInfo.ndim) + ").");
    }

    if (numChannels == 0) {
      // No work to do.
      return;
    } else if (numChannels != getNumChannels()) {
      throw std::runtime_error(
          "WriteableAudioFile was opened with num_channels=" +
          std::to_string(getNumChannels()) +
          ", but was passed an array containing " +
          std::to_string(numChannels) + "-channel audio!");
    }

    // Depending on the input channel layout, we need to copy data
    // differently. This loop is duplicated here to move the if statement
    // outside of the tight loop, as we don't need to re-check that the input
    // channel is still the same on every iteration of the loop.
    switch (*lastChannelLayout) {
    case ChannelLayout::Interleaved: {
      std::vector<std::vector<SampleType>> deinterleaveBuffers;

      // Use a temporary buffer to chunk the audio input
      // and pass it into the writer, chunk by chunk, rather
      // than de-interleaving the entire buffer at once:
      deinterleaveBuffers.resize(numChannels);

      const SampleType **channelPointers =
          (const SampleType **)alloca(numChannels * sizeof(SampleType *));
      for (int startSample = 0; startSample < numSamples;
           startSample += DEFAULT_AUDIO_BUFFER_SIZE_FRAMES) {
        int samplesToWrite = std::min(numSamples - startSample,
                                      DEFAULT_AUDIO_BUFFER_SIZE_FRAMES);

        for (int c = 0; c < numChannels; c++) {
          deinterleaveBuffers[c].resize(samplesToWrite);
          channelPointers[c] = deinterleaveBuffers[c].data();

          // We're de-interleaving the data here, so we can't use copyFrom.
          for (unsigned int i = 0; i < samplesToWrite; i++) {
            deinterleaveBuffers[c][i] =
                ((SampleType
                      *)(inputInfo.ptr))[((i + startSample) * numChannels) + c];
          }
        }

        bool writeSuccessful =
            write(channelPointers, numChannels, samplesToWrite);

        if (!writeSuccessful) {
          PythonException::raise();
          throw std::runtime_error("Unable to write data to audio file.");
        }
      }

      break;
    }
    case ChannelLayout::NotInterleaved: {
      // We can just pass all the data to write:
      const SampleType **channelPointers =
          (const SampleType **)alloca(numChannels * sizeof(SampleType *));
      for (int c = 0; c < numChannels; c++) {
        channelPointers[c] = ((SampleType *)inputInfo.ptr) + (numSamples * c);
      }

      bool writeSuccessful = write(channelPointers, numChannels, numSamples);

      if (!writeSuccessful) {
        PythonException::raise();
        throw std::runtime_error("Unable to write data to audio file.");
      }
      break;
    }
    default:
      throw std::runtime_error(
          "Internal error: got unexpected channel layout.");
    }

    {
      ScopedTryWriteLock scopedTryWriteLock(objectLock);
      if (!scopedTryWriteLock.isLocked()) {
        throw std::runtime_error(
            "Another thread is currently writing to this AudioFile. Note "
            "that using multiple concurrent writers on the same AudioFile "
            "object will produce nondeterministic results.");
      }

      framesWritten += numSamples;
    }
  }