diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 764ade2661..73596f39c7 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -62,8 +62,10 @@ void Sound::downloadFinished(const QByteArray& data) { QByteArray outputAudioByteArray; - interpretAsWav(rawAudioByteArray, outputAudioByteArray); - downSample(outputAudioByteArray); + int sampleRate = interpretAsWav(rawAudioByteArray, outputAudioByteArray); + if (sampleRate != 0) { + downSample(outputAudioByteArray, sampleRate); + } } else if (fileName.endsWith(RAW_EXTENSION)) { // check if this was a stereo raw file // since it's raw the only way for us to know that is if the file was called .stereo.raw @@ -73,7 +75,7 @@ void Sound::downloadFinished(const QByteArray& data) { } // Process as RAW file - downSample(rawAudioByteArray); + downSample(rawAudioByteArray, 48000); } else { qCDebug(audio) << "Unknown sound file type"; } @@ -84,29 +86,35 @@ void Sound::downloadFinished(const QByteArray& data) { emit ready(); } -void Sound::downSample(const QByteArray& rawAudioByteArray) { - // assume that this was a RAW file and is now an array of samples that are - // signed, 16-bit, 48Khz +void Sound::downSample(const QByteArray& rawAudioByteArray, int sampleRate) { // we want to convert it to the format that the audio-mixer wants // which is signed, 16-bit, 24Khz - int numChannels = _isStereo ? 2 : 1; - AudioSRC resampler(48000, AudioConstants::SAMPLE_RATE, numChannels); + if (sampleRate == AudioConstants::SAMPLE_RATE) { - // resize to max possible output - int numSourceFrames = rawAudioByteArray.size() / (numChannels * sizeof(AudioConstants::AudioSample)); - int maxDestinationFrames = resampler.getMaxOutput(numSourceFrames); - int maxDestinationBytes = maxDestinationFrames * numChannels * sizeof(AudioConstants::AudioSample); - _byteArray.resize(maxDestinationBytes); + // no resampling needed + _byteArray = rawAudioByteArray; - int numDestinationFrames = resampler.render((int16_t*)rawAudioByteArray.data(), - (int16_t*)_byteArray.data(), - numSourceFrames); + } else { - // truncate to actual output - int numDestinationBytes = numDestinationFrames * numChannels * sizeof(AudioConstants::AudioSample); - _byteArray.resize(numDestinationBytes); + int numChannels = _isStereo ? 2 : 1; + AudioSRC resampler(sampleRate, AudioConstants::SAMPLE_RATE, numChannels); + + // resize to max possible output + int numSourceFrames = rawAudioByteArray.size() / (numChannels * sizeof(AudioConstants::AudioSample)); + int maxDestinationFrames = resampler.getMaxOutput(numSourceFrames); + int maxDestinationBytes = maxDestinationFrames * numChannels * sizeof(AudioConstants::AudioSample); + _byteArray.resize(maxDestinationBytes); + + int numDestinationFrames = resampler.render((int16_t*)rawAudioByteArray.data(), + (int16_t*)_byteArray.data(), + numSourceFrames); + + // truncate to actual output + int numDestinationBytes = numDestinationFrames * numChannels * sizeof(AudioConstants::AudioSample); + _byteArray.resize(numDestinationBytes); + } } // @@ -160,7 +168,8 @@ struct CombinedHeader { WAVEHeader wave; }; -void Sound::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray) { +// returns wavfile sample rate, used for resampling +int Sound::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray) { CombinedHeader fileHeader; @@ -174,36 +183,33 @@ void Sound::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& ou // descriptor.id == "RIFX" also signifies BigEndian file // waveStream.setByteOrder(QDataStream::BigEndian); qCDebug(audio) << "Currently not supporting big-endian audio files."; - return; + return 0; } if (strncmp(fileHeader.riff.type, "WAVE", 4) != 0 || strncmp(fileHeader.wave.descriptor.id, "fmt", 3) != 0) { qCDebug(audio) << "Not a WAVE Audio file."; - return; + return 0; } // added the endianess check as an extra level of security if (qFromLittleEndian(fileHeader.wave.audioFormat) != 1) { qCDebug(audio) << "Currently not supporting non PCM audio files."; - return; + return 0; } if (qFromLittleEndian(fileHeader.wave.numChannels) == 2) { _isStereo = true; } else if (qFromLittleEndian(fileHeader.wave.numChannels) > 2) { qCDebug(audio) << "Currently not support audio files with more than 2 channels."; + return 0; } if (qFromLittleEndian(fileHeader.wave.bitsPerSample) != 16) { qCDebug(audio) << "Currently not supporting non 16bit audio files."; - return; + return 0; } - if (qFromLittleEndian(fileHeader.wave.sampleRate) != 48000) { - qCDebug(audio) << "Currently not supporting non 48KHz audio files."; - return; - } - + // Skip any extra data in the WAVE chunk waveStream.skipRawData(fileHeader.wave.descriptor.size - (sizeof(WAVEHeader) - sizeof(chunk))); @@ -218,7 +224,7 @@ void Sound::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& ou waveStream.skipRawData(dataHeader.descriptor.size); } else { qCDebug(audio) << "Could not read wav audio data header."; - return; + return 0; } } @@ -227,12 +233,14 @@ void Sound::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& ou outputAudioByteArray.resize(outputAudioByteArraySize); if (waveStream.readRawData(outputAudioByteArray.data(), outputAudioByteArraySize) != (int)outputAudioByteArraySize) { qCDebug(audio) << "Error reading WAV file"; + return 0; } _duration = (float) (outputAudioByteArraySize / (fileHeader.wave.sampleRate * fileHeader.wave.numChannels * fileHeader.wave.bitsPerSample / 8.0f)); + return fileHeader.wave.sampleRate; } else { qCDebug(audio) << "Could not read wav audio file header."; - return; + return 0; } } diff --git a/libraries/audio/src/Sound.h b/libraries/audio/src/Sound.h index 23ef92aa6a..73d5ecd03d 100644 --- a/libraries/audio/src/Sound.h +++ b/libraries/audio/src/Sound.h @@ -40,8 +40,8 @@ private: bool _isReady; float _duration; // In seconds - void downSample(const QByteArray& rawAudioByteArray); - void interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray); + void downSample(const QByteArray& rawAudioByteArray, int sampleRate); + int interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray); virtual void downloadFinished(const QByteArray& data) override; };