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