diff --git a/AudioFile.h b/AudioFile.h index e554196..9869b89 100644 --- a/AudioFile.h +++ b/AudioFile.h @@ -90,18 +90,18 @@ 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 */ @@ -137,26 +137,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 */ @@ -189,8 +189,8 @@ class AudioFile 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(); @@ -217,7 +217,7 @@ class AudioFile static inline bool writeDataToFile (const std::vector& fileData, std::string filePath); //============================================================= - void reportError (std::string errorMessage); + void reportError (const std::string& errorMessage); //============================================================= AudioFileFormat audioFileFormat; @@ -329,7 +329,7 @@ AudioFile::AudioFile() //============================================================= template -AudioFile::AudioFile (std::string filePath) +AudioFile::AudioFile (const std::string& filePath) : AudioFile() { load (filePath); @@ -391,18 +391,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(); @@ -500,7 +500,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); @@ -919,7 +919,7 @@ void AudioFile::addSampleRateToAiffData (std::vector& fileData, uint //============================================================= template -bool AudioFile::save (std::string filePath, AudioFileFormat format) +bool AudioFile::save (const std::string& filePath, AudioFileFormat format) { if (format == AudioFileFormat::Wave) { @@ -935,7 +935,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; @@ -1052,7 +1052,7 @@ bool AudioFile::saveToWaveFile (std::string filePath) //============================================================= template -bool AudioFile::saveToAiffFile (std::string filePath) +bool AudioFile::saveToAiffFile (const std::string& filePath) { std::vector fileData; @@ -1160,21 +1160,15 @@ template 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; } //============================================================= @@ -1247,6 +1241,9 @@ void AudioFile::clearAudioBuffer() template 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") @@ -1293,27 +1290,6 @@ int16_t AudioFile::twoBytesToInt (const std::vector& source, int sta return result; } -//============================================================= -template -int AudioFile::getIndexOfString (const 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 (const std::vector& source, const std::string& chunkHeaderID, int startIndex, Endianness endianness) @@ -1340,7 +1316,13 @@ int AudioFile::getIndexOfChunk (const std::vector& source, const std 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); } @@ -1349,10 +1331,10 @@ int AudioFile::getIndexOfChunk (const std::vector& source, const std //============================================================= 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; } //============================================================= 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/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); + } }