diff --git a/AudioFile.h b/AudioFile.h index 21ba6d2..3c53243 100644 --- a/AudioFile.h +++ b/AudioFile.h @@ -47,6 +47,9 @@ #include #include #include +#include +#include +#include // disable some warnings on Windows #if defined (_MSC_VER) @@ -90,22 +93,22 @@ class AudioFile AudioFile(); /** Constructor, using a given file path to load a file */ - AudioFile (std::string filePath); + AudioFile (const std::string& filePath); //============================================================= /** Loads an audio file from a given file path. * @Returns true if the file was successfully loaded */ - bool load (std::string filePath); + bool load (const std::string& filePath); /** Saves an audio file to a given file path. * @Returns true if the file was successfully saved */ - bool save (std::string filePath, AudioFileFormat format = AudioFileFormat::Wave); + bool save (const std::string& filePath, AudioFileFormat format = AudioFileFormat::Wave); //============================================================= /** Loads an audio file from data in memory */ - bool loadFromMemory (std::vector& fileData); + bool loadFromMemory (const std::vector& fileData); //============================================================= /** @Returns the sample rate */ @@ -137,26 +140,26 @@ class AudioFile /** Set the audio buffer for this AudioFile by copying samples from another buffer. * @Returns true if the buffer was copied successfully. */ - bool setAudioBuffer (AudioBuffer& newBuffer); + bool setAudioBuffer (const AudioBuffer& newBuffer); /** Sets the audio buffer to a given number of channels and number of samples per channel. This will try to preserve * the existing audio, adding zeros to any new channels or new samples in a given channel. */ - void setAudioBufferSize (int numChannels, int numSamples); + void setAudioBufferSize (const int numChannels, const int numSamples); /** Sets the number of samples per channel in the audio buffer. This will try to preserve * the existing audio, adding zeros to new samples in a given channel if the number of samples is increased. */ - void setNumSamplesPerChannel (int numSamples); + void setNumSamplesPerChannel (const int numSamples); /** Sets the number of channels. New channels will have the correct number of samples and be initialised to zero */ - void setNumChannels (int numChannels); + void setNumChannels (const int numChannels); /** Sets the bit depth for the audio file. If you use the save() function, this bit depth rate will be used */ - void setBitDepth (int numBitsPerSample); + void setBitDepth (const int numBitsPerSample); /** Sets the sample rate for the audio file. If you use the save() function, this sample rate will be used */ - void setSampleRate (uint32_t newSampleRate); + void setSampleRate (const uint32_t newSampleRate); //============================================================= /** Sets whether the library should log error messages to the console. By default this is true */ @@ -185,38 +188,38 @@ class AudioFile }; //============================================================= - AudioFileFormat determineAudioFileFormat (std::vector& fileData); - bool decodeWaveFile (std::vector& fileData); - bool decodeAiffFile (std::vector& fileData); + bool decodeWaveFile (const std::vector& fileData); + bool decodeAiffFile (const std::vector& fileData); //============================================================= - bool saveToWaveFile (std::string filePath); - bool saveToAiffFile (std::string filePath); + bool saveToWaveFile (const std::string& filePath); + bool saveToAiffFile (const std::string& filePath); //============================================================= void clearAudioBuffer(); //============================================================= - int32_t fourBytesToInt (std::vector& source, int startIndex, Endianness endianness = Endianness::LittleEndian); - int16_t twoBytesToInt (std::vector& source, int startIndex, Endianness endianness = Endianness::LittleEndian); - int getIndexOfString (std::vector& source, std::string s); - int getIndexOfChunk (std::vector& source, const std::string& chunkHeaderID, int startIndex, Endianness endianness = Endianness::LittleEndian); + static inline AudioFileFormat determineAudioFileFormat (const std::vector& fileData); + + static inline int32_t fourBytesToInt (const std::vector& source, int startIndex, Endianness endianness = Endianness::LittleEndian); + static inline int16_t twoBytesToInt (const std::vector& source, int startIndex, Endianness endianness = Endianness::LittleEndian); + static inline int getIndexOfString (const std::vector& source, std::string s); + static inline int getIndexOfChunk (const std::vector& source, const std::string& chunkHeaderID, int startIndex, Endianness endianness = Endianness::LittleEndian); //============================================================= - uint32_t getAiffSampleRate (std::vector& fileData, int sampleRateStartIndex); - bool tenByteMatch (std::vector& v1, int startIndex1, std::vector& v2, int startIndex2); - void addSampleRateToAiffData (std::vector& fileData, uint32_t sampleRate); + static inline uint32_t getAiffSampleRate (const std::vector& fileData, int sampleRateStartIndex); + static inline void addSampleRateToAiffData (std::vector& fileData, uint32_t sampleRate); //============================================================= - void addStringToFileData (std::vector& fileData, std::string s); - void addInt32ToFileData (std::vector& fileData, int32_t i, Endianness endianness = Endianness::LittleEndian); - void addInt16ToFileData (std::vector& fileData, int16_t i, Endianness endianness = Endianness::LittleEndian); + static inline void addStringToFileData (std::vector& fileData, std::string s); + static inline void addInt32ToFileData (std::vector& fileData, int32_t i, Endianness endianness = Endianness::LittleEndian); + static inline void addInt16ToFileData (std::vector& fileData, int16_t i, Endianness endianness = Endianness::LittleEndian); //============================================================= - bool writeDataToFile (std::vector& fileData, std::string filePath); + static inline bool writeDataToFile (const std::vector& fileData, std::string filePath); //============================================================= - void reportError (std::string errorMessage); + void reportError (const std::string& errorMessage); //============================================================= AudioFileFormat audioFileFormat; @@ -270,27 +273,14 @@ struct AudioSampleConverter }; //============================================================= -// Pre-defined 10-byte representations of common sample rates -static std::unordered_map > aiffSampleRateTable = { - {8000, {64, 11, 250, 0, 0, 0, 0, 0, 0, 0}}, - {11025, {64, 12, 172, 68, 0, 0, 0, 0, 0, 0}}, - {16000, {64, 12, 250, 0, 0, 0, 0, 0, 0, 0}}, - {22050, {64, 13, 172, 68, 0, 0, 0, 0, 0, 0}}, - {32000, {64, 13, 250, 0, 0, 0, 0, 0, 0, 0}}, - {37800, {64, 14, 147, 168, 0, 0, 0, 0, 0, 0}}, - {44056, {64, 14, 172, 24, 0, 0, 0, 0, 0, 0}}, - {44100, {64, 14, 172, 68, 0, 0, 0, 0, 0, 0}}, - {47250, {64, 14, 184, 146, 0, 0, 0, 0, 0, 0}}, - {48000, {64, 14, 187, 128, 0, 0, 0, 0, 0, 0}}, - {50000, {64, 14, 195, 80, 0, 0, 0, 0, 0, 0}}, - {50400, {64, 14, 196, 224, 0, 0, 0, 0, 0, 0}}, - {88200, {64, 15, 172, 68, 0, 0, 0, 0, 0, 0}}, - {96000, {64, 15, 187, 128, 0, 0, 0, 0, 0, 0}}, - {176400, {64, 16, 172, 68, 0, 0, 0, 0, 0, 0}}, - {192000, {64, 16, 187, 128, 0, 0, 0, 0, 0, 0}}, - {352800, {64, 17, 172, 68, 0, 0, 0, 0, 0, 0}}, - {2822400, {64, 20, 172, 68, 0, 0, 0, 0, 0, 0}}, - {5644800, {64, 21, 172, 68, 0, 0, 0, 0, 0, 0}} +struct AiffUtilities +{ + //============================================================= + /** Decode an 80-bit (10 byte) sample rate to a double */ + static inline double decodeAiffSampleRate (const uint8_t* bytes); + + /** Encode a double as an 80-bit (10-byte) sample rate */ + static inline void encodeAiffSampleRate (double sampleRate, uint8_t* bytes); }; //============================================================= @@ -328,7 +318,7 @@ AudioFile::AudioFile() //============================================================= template -AudioFile::AudioFile (std::string filePath) +AudioFile::AudioFile (const std::string& filePath) : AudioFile() { load (filePath); @@ -390,18 +380,18 @@ double AudioFile::getLengthInSeconds() const template void AudioFile::printSummary() const { - std::cout << "|======================================|" << std::endl; - std::cout << "Num Channels: " << getNumChannels() << std::endl; - std::cout << "Num Samples Per Channel: " << getNumSamplesPerChannel() << std::endl; - std::cout << "Sample Rate: " << sampleRate << std::endl; - std::cout << "Bit Depth: " << bitDepth << std::endl; - std::cout << "Length in Seconds: " << getLengthInSeconds() << std::endl; - std::cout << "|======================================|" << std::endl; + std::cerr << "|======================================|" << std::endl; + std::cerr << "Num Channels: " << getNumChannels() << std::endl; + std::cerr << "Num Samples Per Channel: " << getNumSamplesPerChannel() << std::endl; + std::cerr << "Sample Rate: " << sampleRate << std::endl; + std::cerr << "Bit Depth: " << bitDepth << std::endl; + std::cerr << "Length in Seconds: " << getLengthInSeconds() << std::endl; + std::cerr << "|======================================|" << std::endl; } //============================================================= template -bool AudioFile::setAudioBuffer (AudioBuffer& newBuffer) +bool AudioFile::setAudioBuffer (const AudioBuffer& newBuffer) { int numChannels = (int)newBuffer.size(); @@ -499,7 +489,7 @@ void AudioFile::shouldLogErrorsToConsole (bool logErrors) //============================================================= template -bool AudioFile::load (std::string filePath) +bool AudioFile::load (const std::string& filePath) { std::ifstream file (filePath, std::ios::binary); @@ -545,7 +535,7 @@ bool AudioFile::load (std::string filePath) //============================================================= template -bool AudioFile::loadFromMemory (std::vector& fileData) +bool AudioFile::loadFromMemory (const std::vector& fileData) { // get audio file format audioFileFormat = determineAudioFileFormat (fileData); @@ -567,7 +557,7 @@ bool AudioFile::loadFromMemory (std::vector& fileData) //============================================================= template -bool AudioFile::decodeWaveFile (std::vector& fileData) +bool AudioFile::decodeWaveFile (const std::vector& fileData) { // ----------------------------------------------------------- // HEADER CHUNK @@ -726,7 +716,7 @@ bool AudioFile::decodeWaveFile (std::vector& fileData) //============================================================= template -bool AudioFile::decodeAiffFile (std::vector& fileData) +bool AudioFile::decodeAiffFile (const std::vector& fileData) { // ----------------------------------------------------------- // HEADER CHUNK @@ -881,44 +871,24 @@ bool AudioFile::decodeAiffFile (std::vector& fileData) //============================================================= template -uint32_t AudioFile::getAiffSampleRate (std::vector& fileData, int sampleRateStartIndex) +uint32_t AudioFile::getAiffSampleRate (const std::vector& fileData, int sampleRateStartIndex) { - for (auto it : aiffSampleRateTable) - { - if (tenByteMatch (fileData, sampleRateStartIndex, it.second, 0)) - return it.first; - } - - return 0; + double sampleRate = AiffUtilities::decodeAiffSampleRate (&fileData[sampleRateStartIndex]); + return static_cast (sampleRate); } //============================================================= template -bool AudioFile::tenByteMatch (std::vector& v1, int startIndex1, std::vector& v2, int startIndex2) +void AudioFile::addSampleRateToAiffData (std::vector& fileData, uint32_t sampleRateToAdd) { - for (int i = 0; i < 10; i++) - { - if (v1[startIndex1 + i] != v2[startIndex2 + i]) - return false; - } - - return true; + std::array sampleRateData; + AiffUtilities::encodeAiffSampleRate (static_cast (sampleRateToAdd), sampleRateData.data()); + fileData.insert (fileData.end(), sampleRateData.begin(), sampleRateData.end()); } //============================================================= template -void AudioFile::addSampleRateToAiffData (std::vector& fileData, uint32_t sampleRate_) -{ - if (aiffSampleRateTable.count (sampleRate_) > 0) - { - for (int i = 0; i < 10; i++) - fileData.push_back (aiffSampleRateTable[sampleRate_][i]); - } -} - -//============================================================= -template -bool AudioFile::save (std::string filePath, AudioFileFormat format) +bool AudioFile::save (const std::string& filePath, AudioFileFormat format) { if (format == AudioFileFormat::Wave) { @@ -934,7 +904,7 @@ bool AudioFile::save (std::string filePath, AudioFileFormat format) //============================================================= template -bool AudioFile::saveToWaveFile (std::string filePath) +bool AudioFile::saveToWaveFile (const std::string& filePath) { std::vector fileData; @@ -1051,7 +1021,7 @@ bool AudioFile::saveToWaveFile (std::string filePath) //============================================================= template -bool AudioFile::saveToAiffFile (std::string filePath) +bool AudioFile::saveToAiffFile (const std::string& filePath) { std::vector fileData; @@ -1156,24 +1126,18 @@ bool AudioFile::saveToAiffFile (std::string filePath) //============================================================= template -bool AudioFile::writeDataToFile (std::vector& fileData, std::string filePath) +bool AudioFile::writeDataToFile (const std::vector& fileData, std::string filePath) { std::ofstream outputFile (filePath, std::ios::binary); - - if (outputFile.is_open()) + + if (!outputFile.is_open()) { - for (size_t i = 0; i < fileData.size(); i++) - { - char value = (char) fileData[i]; - outputFile.write (&value, sizeof (char)); - } - - outputFile.close(); - - return true; + return false; } - - return false; + + outputFile.write ((const char*)fileData.data(), fileData.size()); + outputFile.close(); + return true; } //============================================================= @@ -1244,8 +1208,11 @@ void AudioFile::clearAudioBuffer() //============================================================= template -AudioFileFormat AudioFile::determineAudioFileFormat (std::vector& fileData) +AudioFileFormat AudioFile::determineAudioFileFormat (const std::vector& fileData) { + if (fileData.size() < 4) + return AudioFileFormat::Error; + std::string header (fileData.begin(), fileData.begin() + 4); if (header == "RIFF") @@ -1258,7 +1225,7 @@ AudioFileFormat AudioFile::determineAudioFileFormat (std::vector& fi //============================================================= template -int32_t AudioFile::fourBytesToInt (std::vector& source, int startIndex, Endianness endianness) +int32_t AudioFile::fourBytesToInt (const std::vector& source, int startIndex, Endianness endianness) { if (source.size() >= (startIndex + 4)) { @@ -1280,7 +1247,7 @@ int32_t AudioFile::fourBytesToInt (std::vector& source, int startInd //============================================================= template -int16_t AudioFile::twoBytesToInt (std::vector& source, int startIndex, Endianness endianness) +int16_t AudioFile::twoBytesToInt (const std::vector& source, int startIndex, Endianness endianness) { int16_t result; @@ -1294,28 +1261,7 @@ int16_t AudioFile::twoBytesToInt (std::vector& source, int startInde //============================================================= template -int AudioFile::getIndexOfString (std::vector& source, std::string stringToSearchFor) -{ - int index = -1; - int stringLength = (int)stringToSearchFor.length(); - - for (size_t i = 0; i < source.size() - stringLength;i++) - { - std::string section (source.begin() + i, source.begin() + i + stringLength); - - if (section == stringToSearchFor) - { - index = static_cast (i); - break; - } - } - - return index; -} - -//============================================================= -template -int AudioFile::getIndexOfChunk (std::vector& source, const std::string& chunkHeaderID, int startIndex, Endianness endianness) +int AudioFile::getIndexOfChunk (const std::vector& source, const std::string& chunkHeaderID, int startIndex, Endianness endianness) { constexpr int dataLen = 4; @@ -1339,7 +1285,13 @@ int AudioFile::getIndexOfChunk (std::vector& source, const std::stri if ((i + 4) >= source.size()) return -1; - auto chunkSize = fourBytesToInt (source, i, endianness); + int32_t chunkSize = fourBytesToInt (source, i, endianness); + // Assume chunk size is invalid if it's greater than the number of bytes remaining in source + if (chunkSize > (source.size() - i - dataLen) || (chunkSize < 0)) + { + assert (false && "Invalid chunk size"); + return -1; + } i += (dataLen + chunkSize); } @@ -1348,10 +1300,10 @@ int AudioFile::getIndexOfChunk (std::vector& source, const std::stri //============================================================= template -void AudioFile::reportError (std::string errorMessage) +void AudioFile::reportError (const std::string& errorMessage) { if (logErrorsToConsole) - std::cout << errorMessage << std::endl; + std::cerr << errorMessage << std::endl; } //============================================================= @@ -1578,6 +1530,82 @@ T AudioSampleConverter::clamp (T value, T minValue, T maxValue) return value; } +//============================================================= +inline double AiffUtilities::decodeAiffSampleRate (const uint8_t* bytes) +{ + // Note: Sample rate is 80 bits made up of + // * 1 sign bit + // * 15 exponent bits + // * 64 mantissa bits + + // ---------------------------------------------- + // Sign + + // Extract the sign (most significant bit of byte 0) + int sign = (bytes[0] & 0x80) ? -1 : 1; + + // ---------------------------------------------- + // Exponent + + // byte 0: ignore the sign and shift the most significant bits to the left by one byte + uint16_t msbShifted = (static_cast (bytes[0] & 0x7F) << 8); + + // calculate exponent by combining byte 0 and byte 1 and subtract bias + uint16_t exponent = (msbShifted | static_cast (bytes[1])) - 16383; + + // ---------------------------------------------- + // Mantissa + + // Extract the mantissa (remaining 64 bits) by looping over the remaining + // bytes and combining them while shifting the result to the left by + // 8 bits each time + uint64_t mantissa = 0; + + for (int i = 2; i < 10; ++i) + mantissa = (mantissa << 8) | bytes[i]; + + // Normalize the mantissa (implicit leading 1 for normalized values) + double normalisedMantissa = static_cast (mantissa) / (1ULL << 63); + + // ---------------------------------------------- + // Combine sign, exponent, and mantissa into a double + + return sign * std::ldexp (normalisedMantissa, exponent); +} + +//============================================================= +inline void AiffUtilities::encodeAiffSampleRate (double sampleRate, uint8_t* bytes) +{ + // Determine the sign + int sign = (sampleRate < 0) ? -1 : 1; + + if (sign == -1) + sampleRate = -sampleRate; + + // Set most significant bit of byte 0 for the sign + bytes[0] = (sign == -1) ? 0x80 : 0x00; + + // Calculate the exponent using logarithm (log base 2) + int exponent = (log (sampleRate) / log (2.0)); + + // Add bias to exponent for AIFF + uint16_t biasedExponent = static_cast (exponent + 16383); + + // Normalize the sample rate + double normalizedSampleRate = sampleRate / pow (2.0, exponent); + + // Calculate the mantissa + uint64_t mantissa = static_cast (normalizedSampleRate * (1ULL << 63)); + + // Pack the exponent into first two bytes of 10-byte AIFF format + bytes[0] |= (biasedExponent >> 8) & 0x7F; // Upper 7 bits of exponent + bytes[1] = biasedExponent & 0xFF; // Lower 8 bits of exponent + + // Put the mantissa into byte array + for (int i = 0; i < 8; ++i) + bytes[2 + i] = (mantissa >> (8 * (7 - i))) & 0xFF; +} + #if defined (_MSC_VER) __pragma(warning (pop)) #elif defined (__GNUC__) diff --git a/README.md b/README.md index f6eec4e..4c08937 100644 --- a/README.md +++ b/README.md @@ -131,9 +131,9 @@ Please see the `examples` folder for some examples on library usage. A Note On Types ----------------- -AudioFile is a template class and so it can be instantiated using floating point precision: +AudioFile is a template class and so it can be instantiated using different types to represent the audio samples. -For example +For example, we can use floating point precision... AudioFile audioFile; @@ -147,13 +147,15 @@ For example This simply reflects the data type you would like to use to store the underlying audio samples. -When you use an integer type to store the samples (e.g. `int` or `int8_t` or `int16_t` or `uint32_t`), the library will read in the integer sample values directly from the audio file. A couple of notes on integer types: +When you use an integer type to store the samples (e.g. `int` or `int8_t` or `int16_t` or `uint32_t`), the library will read in the integer sample values directly from the audio file. + +A couple of notes on integer types: * The range of samples is designed to be symmetric. This means that for (e.g.) an signed 8-bit integer (`int8_t`) we will use the range `[-127, 127]` for storing samples representing the `[-1., 1.]` range. The value `-128` is possible here given the `int8_t` type, but this is interpreted as a value slightly lower than `-1` (specifically `-1.007874015748`). * In the case of unsigned types, we obviously can't store samples as negative values. Therefore, we used the equivalent range of the unsigned type in use. E.g. if with a 8-bit signed integer (`int8_t`) the range would be `[-127, 127]`, for an 8-bit unsigned integer we would use the range `[1, 255]`. Note that we don't use `-128` for `int8_t` or `0` in `uint8_t`. -* If you try to read an audio file with a larger bit-depth than the type you are using to store samples, the attempt to read the file will fail. Put more simply, you can't read a 16-bit audio file into an 8-bit integer. +* If you try to read an audio file with a larger bit-depth than the type you are using to store samples, the attempt to read the file will fail. Put more simply, you can't read a 16-bit audio sample into an 8-bit integer. * If you are writing audio samples in integer formats, you should use the correct sample range for both a) the type you are using to store samples; and b) the bit depth of the audio you want to write. @@ -245,9 +247,11 @@ Many thanks to the following people for their contributions to this library: * [Abhinav1997](https://github.com/Abhinav1997) * [alxarsenault](https://github.com/alxarsenault) * [BenjaminHinchliff](https://github.com/BenjaminHinchliff) +* [BesselJ](https://github.com/BesselJ) * [emiro85](https://github.com/emiro85) * [heartofrain](https://github.com/heartofrain) * [helloimmatt](https://github.com/helloimmatt/) +* [leocstone](https://github.com/leocstone) * [MatthieuHernandez](https://github.com/MatthieuHernandez) * [mrpossoms](https://github.com/mrpossoms) * [mynameisjohn](https://github.com/mynameisjohn) diff --git a/examples/examples.cpp b/examples/examples.cpp index 6662839..7823520 100644 --- a/examples/examples.cpp +++ b/examples/examples.cpp @@ -141,6 +141,6 @@ namespace examples // 4. Write audio file to disk std::string outputFilePath = "quieter-audio-file.wav"; // change this to somewhere useful for you - a.save (outputFilePath, AudioFileFormat::Aiff); + a.save (outputFilePath, AudioFileFormat::Wave); } } diff --git a/tests/AiffLoadingTests.cpp b/tests/AiffLoadingTests.cpp index 90f71c9..493dba3 100644 --- a/tests/AiffLoadingTests.cpp +++ b/tests/AiffLoadingTests.cpp @@ -350,3 +350,66 @@ TEST_SUITE ("AiffLoadingTests - Integer Types - 24-bit File") test24Bit44100WithInteger (true); } } + +//============================================================= +TEST_SUITE ("AiffLoadingTests - Sample Rates") +{ + //============================================================= + TEST_CASE ("AiffLoadingTests - Sample Rates - Common Sample Rates") + { + // Pre-defined 10-byte representations of common sample rates + std::unordered_map > aiffSampleRateTable = { + {8000, {64, 11, 250, 0, 0, 0, 0, 0, 0, 0}}, + {11025, {64, 12, 172, 68, 0, 0, 0, 0, 0, 0}}, + {16000, {64, 12, 250, 0, 0, 0, 0, 0, 0, 0}}, + {22050, {64, 13, 172, 68, 0, 0, 0, 0, 0, 0}}, + {32000, {64, 13, 250, 0, 0, 0, 0, 0, 0, 0}}, + {37800, {64, 14, 147, 168, 0, 0, 0, 0, 0, 0}}, + {44056, {64, 14, 172, 24, 0, 0, 0, 0, 0, 0}}, + {44100, {64, 14, 172, 68, 0, 0, 0, 0, 0, 0}}, + {47250, {64, 14, 184, 146, 0, 0, 0, 0, 0, 0}}, + {48000, {64, 14, 187, 128, 0, 0, 0, 0, 0, 0}}, + {50000, {64, 14, 195, 80, 0, 0, 0, 0, 0, 0}}, + {50400, {64, 14, 196, 224, 0, 0, 0, 0, 0, 0}}, + {88200, {64, 15, 172, 68, 0, 0, 0, 0, 0, 0}}, + {96000, {64, 15, 187, 128, 0, 0, 0, 0, 0, 0}}, + {176400, {64, 16, 172, 68, 0, 0, 0, 0, 0, 0}}, + {192000, {64, 16, 187, 128, 0, 0, 0, 0, 0, 0}}, + {352800, {64, 17, 172, 68, 0, 0, 0, 0, 0, 0}}, + {2822400, {64, 20, 172, 68, 0, 0, 0, 0, 0, 0}}, + {5644800, {64, 21, 172, 68, 0, 0, 0, 0, 0, 0}} + }; + + std::vector sampleRateBytes (10); + + for (const auto& pair : aiffSampleRateTable) + { + uint32_t sampleRate = pair.first; + double inputSampleRate = sampleRate; + + // encode into bytes + AiffUtilities::encodeAiffSampleRate (static_cast (sampleRate), sampleRateBytes.data()); + + for (int i = 0; i < 10; i++) + CHECK_EQ (sampleRateBytes[i], aiffSampleRateTable[sampleRate][i]); + + double outputSampleRate = AiffUtilities::decodeAiffSampleRate (aiffSampleRateTable[sampleRate].data()); + + CHECK_EQ (inputSampleRate, outputSampleRate); + } + } + + //============================================================= + TEST_CASE ("AiffLoadingTests - Sample Rates - Round Trip Encode/Decode Tests") + { + std::vector sampleRateBytes (10); + + for (int i = -100000; i < 100000; i += 237) // + odd number to reduce cycles but check odd and even values + { + double input = static_cast (i); + AiffUtilities::encodeAiffSampleRate (input, sampleRateBytes.data()); + double output = AiffUtilities::decodeAiffSampleRate (sampleRateBytes.data()); + CHECK_EQ (input, output); + } + } +} diff --git a/tests/GeneralTests.cpp b/tests/GeneralTests.cpp index cc0ff39..705a5b5 100644 --- a/tests/GeneralTests.cpp +++ b/tests/GeneralTests.cpp @@ -107,4 +107,13 @@ TEST_SUITE ("General Tests") checkFilesAreExactlyTheSame (a, b); } + + //============================================================= + TEST_CASE ("GeneralTests::Empty Data") + { + AudioFile a; + a.shouldLogErrorsToConsole (false); + bool result = a.loadFromMemory (std::vector()); + CHECK_EQ (result, false); + } }