long long readInternal()

in pedalboard/io/ReadableAudioFile.h [315:416]


  long long readInternal(const long long numChannels,
                         const long long numSamplesToFill,
                         float *outputPointer) {
    // Note: We take a "write" lock here as calling readInternal will
    // advance internal state:
    ScopedTryWriteLock scopedTryWriteLock(objectLock);
    if (!scopedTryWriteLock.isLocked()) {
      throw std::runtime_error(
          "Another thread is currently reading from this AudioFile. Note "
          "that using multiple concurrent readers on the same AudioFile "
          "object will produce nondeterministic results.");
    }

    // If the file being read does not have enough content, it _should_ pad
    // the rest of the array with zeroes. Unfortunately, this does not seem to
    // be true in practice, so we pre-zero the array to be returned here:
    std::fill_n(outputPointer, numChannels * numSamplesToFill, 0);

    long long numSamples = std::min(
        numSamplesToFill,
        (reader->lengthInSamples + (lengthCorrection ? *lengthCorrection : 0)) -
            currentPosition);

    long long numSamplesToKeep = numSamples;

    float **channelPointers = (float **)alloca(numChannels * sizeof(float *));
    for (long long c = 0; c < numChannels; c++) {
      channelPointers[c] = ((float *)outputPointer) + (numSamples * c);
    }

    if (reader->usesFloatingPointData || reader->bitsPerSample == 32) {
      auto readResult = reader->read(channelPointers, numChannels,
                                     currentPosition, numSamples);

      juce::int64 samplesRead = numSamples;
      if (juce::AudioFormatReaderWithPosition *positionAware =
              dynamic_cast<juce::AudioFormatReaderWithPosition *>(
                  reader.get())) {
        samplesRead = positionAware->getCurrentPosition() - currentPosition;
      }

      bool hitEndOfFile =
          (samplesRead + currentPosition) == reader->lengthInSamples;

      // We read some data, but not as much as we asked for!
      // This will only happen for lossy, header-optional formats
      // like MP3.
      if (samplesRead < numSamples || hitEndOfFile) {
        lengthCorrection =
            (samplesRead + currentPosition) - reader->lengthInSamples;
      } else if (!readResult) {
        PythonException::raise();
        throwReadError(currentPosition, numSamples, samplesRead);
      }
      numSamplesToKeep = samplesRead;
    } else {
      // If the audio is stored in an integral format, read it as integers
      // and do the floating-point conversion ourselves to work around
      // floating-point imprecision in JUCE when reading formats smaller than
      // 32-bit (i.e.: 16-bit audio is off by about 0.003%)
      auto readResult =
          reader->readSamples((int **)channelPointers, numChannels, 0,
                              currentPosition, numSamplesToKeep);
      if (!readResult) {
        PythonException::raise();
        throwReadError(currentPosition, numSamples);
      }

      // When converting 24-bit, 16-bit, or 8-bit data from int to float,
      // the values provided by the above read() call are shifted left
      // (such that the least significant bits are all zero)
      // JUCE will then divide these values by 0x7FFFFFFF, even though
      // the least significant bits are zero, effectively losing precision.
      // Instead, here we set the scale factor appropriately.
      int maxValueAsInt;
      switch (reader->bitsPerSample) {
      case 24:
        maxValueAsInt = 0x7FFFFF00;
        break;
      case 16:
        maxValueAsInt = 0x7FFF0000;
        break;
      case 8:
        maxValueAsInt = 0x7F000000;
        break;
      default:
        throw std::runtime_error("Not sure how to convert data from " +
                                 std::to_string(reader->bitsPerSample) +
                                 " bits per sample to floating point!");
      }
      float scaleFactor = 1.0f / static_cast<float>(maxValueAsInt);

      for (long long c = 0; c < numChannels; c++) {
        juce::FloatVectorOperations::convertFixedToFloat(
            channelPointers[c], (const int *)channelPointers[c], scaleFactor,
            static_cast<int>(numSamples));
      }
    }

    currentPosition += numSamplesToKeep;
    return numSamplesToKeep;
  }