• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //=======================================================================
2 /** @file AudioFile.h
3  *  @author Adam Stark
4  *  @copyright Copyright (C) 2017  Adam Stark
5  *
6  * This file is part of the 'AudioFile' library
7  *
8  * MIT License
9  *
10  * Copyright (c) 2017 Adam Stark
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a copy
13  * of this software and associated documentation files (the "Software"), to deal
14  * in the Software without restriction, including without limitation the rights
15  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
16  * of the Software, and to permit persons to whom the Software is furnished to do so,
17  * subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included in all
20  * copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
23  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
24  * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28  */
29 //=======================================================================
30 
31 #ifndef _AS_AudioFile_h
32 #define _AS_AudioFile_h
33 
34 #if defined (_MSC_VER)
35 #undef max
36 #undef min
37 #define NOMINMAX
38 #endif
39 
40 #include <iostream>
41 #include <vector>
42 #include <cassert>
43 #include <string>
44 #include <cstring>
45 #include <fstream>
46 #include <unordered_map>
47 #include <iterator>
48 #include <algorithm>
49 #include <limits>
50 #include <cstdint>
51 #include <cmath>
52 #include <array>
53 
54 // disable some warnings on Windows
55 #if defined (_MSC_VER)
56     __pragma(warning (push))
57     __pragma(warning (disable : 4244))
58     __pragma(warning (disable : 4457))
59     __pragma(warning (disable : 4458))
60     __pragma(warning (disable : 4389))
61     __pragma(warning (disable : 4996))
62 #elif defined (__GNUC__)
63     _Pragma("GCC diagnostic push")
64     _Pragma("GCC diagnostic ignored \"-Wconversion\"")
65     _Pragma("GCC diagnostic ignored \"-Wsign-compare\"")
66     _Pragma("GCC diagnostic ignored \"-Wshadow\"")
67 #endif
68 
69 //=============================================================
70 /** The different types of audio file, plus some other types to
71  * indicate a failure to load a file, or that one hasn't been
72  * loaded yet
73  */
74 enum class AudioFileFormat
75 {
76     Error,
77     NotLoaded,
78     Wave,
79     Aiff
80 };
81 
82 //=============================================================
83 template <class T>
84 class AudioFile
85 {
86 public:
87 
88     //=============================================================
89     typedef std::vector<std::vector<T> > AudioBuffer;
90 
91     //=============================================================
92     /** Constructor */
93     AudioFile();
94 
95     /** Constructor, using a given file path to load a file */
96     AudioFile (const std::string& filePath);
97 
98     //=============================================================
99     /** Loads an audio file from a given file path.
100      * @Returns true if the file was successfully loaded
101      */
102     bool load (const std::string& filePath);
103 
104     /** Saves an audio file to a given file path.
105      * @Returns true if the file was successfully saved
106      */
107     bool save (const std::string& filePath, AudioFileFormat format = AudioFileFormat::Wave);
108 
109     //=============================================================
110     /** Loads an audio file from data in memory */
111     bool loadFromMemory (const std::vector<uint8_t>& fileData);
112 
113     //=============================================================
114     /** @Returns the sample rate */
115     uint32_t getSampleRate() const;
116 
117     /** @Returns the number of audio channels in the buffer */
118     int getNumChannels() const;
119 
120     /** @Returns true if the audio file is mono */
121     bool isMono() const;
122 
123     /** @Returns true if the audio file is stereo */
124     bool isStereo() const;
125 
126     /** @Returns the bit depth of each sample */
127     int getBitDepth() const;
128 
129     /** @Returns the number of samples per channel */
130     int getNumSamplesPerChannel() const;
131 
132     /** @Returns the length in seconds of the audio file based on the number of samples and sample rate */
133     double getLengthInSeconds() const;
134 
135     /** Prints a summary of the audio file to the console */
136     void printSummary() const;
137 
138     //=============================================================
139 
140     /** Set the audio buffer for this AudioFile by copying samples from another buffer.
141      * @Returns true if the buffer was copied successfully.
142      */
143     bool setAudioBuffer (const AudioBuffer& newBuffer);
144 
145     /** Sets the audio buffer to a given number of channels and number of samples per channel. This will try to preserve
146      * the existing audio, adding zeros to any new channels or new samples in a given channel.
147      */
148     void setAudioBufferSize (const int numChannels, const int numSamples);
149 
150     /** Sets the number of samples per channel in the audio buffer. This will try to preserve
151      * the existing audio, adding zeros to new samples in a given channel if the number of samples is increased.
152      */
153     void setNumSamplesPerChannel (const int numSamples);
154 
155     /** Sets the number of channels. New channels will have the correct number of samples and be initialised to zero */
156     void setNumChannels (const int numChannels);
157 
158     /** Sets the bit depth for the audio file. If you use the save() function, this bit depth rate will be used */
159     void setBitDepth (const int numBitsPerSample);
160 
161     /** Sets the sample rate for the audio file. If you use the save() function, this sample rate will be used */
162     void setSampleRate (const uint32_t newSampleRate);
163 
164     //=============================================================
165     /** Sets whether the library should log error messages to the console. By default this is true */
166     void shouldLogErrorsToConsole (bool logErrors);
167 
168     //=============================================================
169     /** A vector of vectors holding the audio samples for the AudioFile. You can
170      * access the samples by channel and then by sample index, i.e:
171      *
172      *      samples[channel][sampleIndex]
173      */
174     AudioBuffer samples;
175 
176     //=============================================================
177     /** An optional iXML chunk that can be added to the AudioFile.
178      */
179     std::string iXMLChunk;
180 
181 private:
182 
183     //=============================================================
184     enum class Endianness
185     {
186         LittleEndian,
187         BigEndian
188     };
189 
190     //=============================================================
191     bool decodeWaveFile (const std::vector<uint8_t>& fileData);
192     bool decodeAiffFile (const std::vector<uint8_t>& fileData);
193 
194     //=============================================================
195     bool saveToWaveFile (const std::string& filePath);
196     bool saveToAiffFile (const std::string& filePath);
197 
198     //=============================================================
199     void clearAudioBuffer();
200 
201     //=============================================================
202     static inline AudioFileFormat determineAudioFileFormat (const std::vector<uint8_t>& fileData);
203 
204     static inline int32_t fourBytesToInt (const std::vector<uint8_t>& source, int startIndex, Endianness endianness = Endianness::LittleEndian);
205     static inline int16_t twoBytesToInt (const std::vector<uint8_t>& source, int startIndex, Endianness endianness = Endianness::LittleEndian);
206     static inline int getIndexOfString (const std::vector<uint8_t>& source, std::string s);
207     static inline int getIndexOfChunk (const std::vector<uint8_t>& source, const std::string& chunkHeaderID, int startIndex, Endianness endianness = Endianness::LittleEndian);
208 
209     //=============================================================
210     static inline uint32_t getAiffSampleRate (const std::vector<uint8_t>& fileData, int sampleRateStartIndex);
211     static inline void addSampleRateToAiffData (std::vector<uint8_t>& fileData, uint32_t sampleRate);
212 
213     //=============================================================
214     static inline void addStringToFileData (std::vector<uint8_t>& fileData, std::string s);
215     static inline void addInt32ToFileData (std::vector<uint8_t>& fileData, int32_t i, Endianness endianness = Endianness::LittleEndian);
216     static inline void addInt16ToFileData (std::vector<uint8_t>& fileData, int16_t i, Endianness endianness = Endianness::LittleEndian);
217 
218     //=============================================================
219     static inline bool writeDataToFile (const std::vector<uint8_t>& fileData, std::string filePath);
220 
221     //=============================================================
222     void reportError (const std::string& errorMessage);
223 
224     //=============================================================
225     AudioFileFormat audioFileFormat;
226     uint32_t sampleRate;
227     int bitDepth;
228     bool logErrorsToConsole {true};
229 };
230 
231 //=============================================================
232 template <typename T>
233 struct AudioSampleConverter
234 {
235     //=============================================================
236     /** Convert a signed 8-bit integer to an audio sample */
237     static T signedByteToSample (int8_t sample);
238 
239     /** Convert an audio sample to an signed 8-bit representation */
240     static int8_t sampleToSignedByte (T sample);
241 
242     //=============================================================
243     /** Convert an unsigned 8-bit integer to an audio sample */
244     static T unsignedByteToSample (uint8_t sample);
245 
246     /** Convert an audio sample to an unsigned 8-bit representation */
247     static uint8_t sampleToUnsignedByte (T sample);
248 
249     //=============================================================
250     /** Convert a 16-bit integer to an audio sample */
251     static T sixteenBitIntToSample (int16_t sample);
252 
253     /** Convert a an audio sample to a 16-bit integer */
254     static int16_t sampleToSixteenBitInt (T sample);
255 
256     //=============================================================
257     /** Convert a 24-bit value (int a 32-bit int) to an audio sample */
258     static T twentyFourBitIntToSample (int32_t sample);
259 
260     /** Convert a an audio sample to a 24-bit value (in a 32-bit integer) */
261     static int32_t sampleToTwentyFourBitInt (T sample);
262 
263     //=============================================================
264     /** Convert a 32-bit signed integer to an audio sample */
265     static T thirtyTwoBitIntToSample (int32_t sample);
266 
267     /** Convert a an audio sample to a 32-bit signed integer */
268     static int32_t sampleToThirtyTwoBitInt (T sample);
269 
270     //=============================================================
271     /** Helper clamp function to enforce ranges */
272     static T clamp (T v1, T minValue, T maxValue);
273 };
274 
275 //=============================================================
276 struct AiffUtilities
277 {
278     //=============================================================
279     /** Decode an 80-bit (10 byte) sample rate to a double */
280     static inline double decodeAiffSampleRate (const uint8_t* bytes);
281 
282     /** Encode a double as an 80-bit (10-byte) sample rate */
283     static inline void encodeAiffSampleRate (double sampleRate, uint8_t* bytes);
284 };
285 
286 //=============================================================
287 enum WavAudioFormat
288 {
289     PCM = 0x0001,
290     IEEEFloat = 0x0003,
291     ALaw = 0x0006,
292     MULaw = 0x0007,
293     Extensible = 0xFFFE
294 };
295 
296 //=============================================================
297 enum AIFFAudioFormat
298 {
299     Uncompressed,
300     Compressed,
301     Error
302 };
303 
304 //=============================================================
305 /* IMPLEMENTATION */
306 //=============================================================
307 
308 //=============================================================
309 template <class T>
AudioFile()310 AudioFile<T>::AudioFile()
311 {
312     bitDepth = 16;
313     sampleRate = 44100;
314     samples.resize (1);
315     samples[0].resize (0);
316     audioFileFormat = AudioFileFormat::NotLoaded;
317 }
318 
319 //=============================================================
320 template <class T>
AudioFile(const std::string & filePath)321 AudioFile<T>::AudioFile (const std::string& filePath)
322  :  AudioFile<T>()
323 {
324     load (filePath);
325 }
326 
327 //=============================================================
328 template <class T>
getSampleRate()329 uint32_t AudioFile<T>::getSampleRate() const
330 {
331     return sampleRate;
332 }
333 
334 //=============================================================
335 template <class T>
getNumChannels()336 int AudioFile<T>::getNumChannels() const
337 {
338     return (int)samples.size();
339 }
340 
341 //=============================================================
342 template <class T>
isMono()343 bool AudioFile<T>::isMono() const
344 {
345     return getNumChannels() == 1;
346 }
347 
348 //=============================================================
349 template <class T>
isStereo()350 bool AudioFile<T>::isStereo() const
351 {
352     return getNumChannels() == 2;
353 }
354 
355 //=============================================================
356 template <class T>
getBitDepth()357 int AudioFile<T>::getBitDepth() const
358 {
359     return bitDepth;
360 }
361 
362 //=============================================================
363 template <class T>
getNumSamplesPerChannel()364 int AudioFile<T>::getNumSamplesPerChannel() const
365 {
366     if (samples.size() > 0)
367         return (int) samples[0].size();
368     else
369         return 0;
370 }
371 
372 //=============================================================
373 template <class T>
getLengthInSeconds()374 double AudioFile<T>::getLengthInSeconds() const
375 {
376     return (double)getNumSamplesPerChannel() / (double)sampleRate;
377 }
378 
379 //=============================================================
380 template <class T>
printSummary()381 void AudioFile<T>::printSummary() const
382 {
383     std::cerr << "|======================================|" << std::endl;
384     std::cerr << "Num Channels: " << getNumChannels() << std::endl;
385     std::cerr << "Num Samples Per Channel: " << getNumSamplesPerChannel() << std::endl;
386     std::cerr << "Sample Rate: " << sampleRate << std::endl;
387     std::cerr << "Bit Depth: " << bitDepth << std::endl;
388     std::cerr << "Length in Seconds: " << getLengthInSeconds() << std::endl;
389     std::cerr << "|======================================|" << std::endl;
390 }
391 
392 //=============================================================
393 template <class T>
setAudioBuffer(const AudioBuffer & newBuffer)394 bool AudioFile<T>::setAudioBuffer (const AudioBuffer& newBuffer)
395 {
396     int numChannels = (int)newBuffer.size();
397 
398     if (numChannels <= 0)
399     {
400         assert (false && "The buffer you are trying to use has no channels");
401         return false;
402     }
403 
404     size_t numSamples = newBuffer[0].size();
405 
406     // set the number of channels
407     samples.resize (newBuffer.size());
408 
409     for (int k = 0; k < getNumChannels(); k++)
410     {
411         assert (newBuffer[k].size() == numSamples);
412 
413         samples[k].resize (numSamples);
414 
415         for (size_t i = 0; i < numSamples; i++)
416         {
417             samples[k][i] = newBuffer[k][i];
418         }
419     }
420 
421     return true;
422 }
423 
424 //=============================================================
425 template <class T>
setAudioBufferSize(int numChannels,int numSamples)426 void AudioFile<T>::setAudioBufferSize (int numChannels, int numSamples)
427 {
428     samples.resize (numChannels);
429     setNumSamplesPerChannel (numSamples);
430 }
431 
432 //=============================================================
433 template <class T>
setNumSamplesPerChannel(int numSamples)434 void AudioFile<T>::setNumSamplesPerChannel (int numSamples)
435 {
436     int originalSize = getNumSamplesPerChannel();
437 
438     for (int i = 0; i < getNumChannels();i++)
439     {
440         samples[i].resize (numSamples);
441 
442         // set any new samples to zero
443         if (numSamples > originalSize)
444             std::fill (samples[i].begin() + originalSize, samples[i].end(), (T)0.);
445     }
446 }
447 
448 //=============================================================
449 template <class T>
setNumChannels(int numChannels)450 void AudioFile<T>::setNumChannels (int numChannels)
451 {
452     int originalNumChannels = getNumChannels();
453     int originalNumSamplesPerChannel = getNumSamplesPerChannel();
454 
455     samples.resize (numChannels);
456 
457     // make sure any new channels are set to the right size
458     // and filled with zeros
459     if (numChannels > originalNumChannels)
460     {
461         for (int i = originalNumChannels; i < numChannels; i++)
462         {
463             samples[i].resize (originalNumSamplesPerChannel);
464             std::fill (samples[i].begin(), samples[i].end(), (T)0.);
465         }
466     }
467 }
468 
469 //=============================================================
470 template <class T>
setBitDepth(int numBitsPerSample)471 void AudioFile<T>::setBitDepth (int numBitsPerSample)
472 {
473     bitDepth = numBitsPerSample;
474 }
475 
476 //=============================================================
477 template <class T>
setSampleRate(uint32_t newSampleRate)478 void AudioFile<T>::setSampleRate (uint32_t newSampleRate)
479 {
480     sampleRate = newSampleRate;
481 }
482 
483 //=============================================================
484 template <class T>
shouldLogErrorsToConsole(bool logErrors)485 void AudioFile<T>::shouldLogErrorsToConsole (bool logErrors)
486 {
487     logErrorsToConsole = logErrors;
488 }
489 
490 //=============================================================
491 template <class T>
load(const std::string & filePath)492 bool AudioFile<T>::load (const std::string& filePath)
493 {
494     std::ifstream file (filePath, std::ios::binary);
495 
496     // check the file exists
497     if (! file.good())
498     {
499         reportError ("ERROR: File doesn't exist or otherwise can't load file\n"  + filePath);
500         return false;
501     }
502 
503     std::vector<uint8_t> fileData;
504 
505 	file.unsetf (std::ios::skipws);
506 
507 	file.seekg (0, std::ios::end);
508 	size_t length = file.tellg();
509 	file.seekg (0, std::ios::beg);
510 
511 	// allocate
512 	fileData.resize (length);
513 
514 	file.read(reinterpret_cast<char*> (fileData.data()), length);
515 	file.close();
516 
517 	if (file.gcount() != length)
518 	{
519 		reportError ("ERROR: Couldn't read entire file\n" + filePath);
520 		return false;
521 	}
522 
523     // Handle very small files that will break our attempt to read the
524     // first header info from them
525     if (fileData.size() < 12)
526     {
527         reportError ("ERROR: File is not a valid audio file\n" + filePath);
528         return false;
529     }
530     else
531     {
532         return loadFromMemory (fileData);
533     }
534 }
535 
536 //=============================================================
537 template <class T>
loadFromMemory(const std::vector<uint8_t> & fileData)538 bool AudioFile<T>::loadFromMemory (const std::vector<uint8_t>& fileData)
539 {
540     // get audio file format
541     audioFileFormat = determineAudioFileFormat (fileData);
542 
543     if (audioFileFormat == AudioFileFormat::Wave)
544     {
545         return decodeWaveFile (fileData);
546     }
547     else if (audioFileFormat == AudioFileFormat::Aiff)
548     {
549         return decodeAiffFile (fileData);
550     }
551     else
552     {
553         reportError ("Audio File Type: Error");
554         return false;
555     }
556 }
557 
558 //=============================================================
559 template <class T>
decodeWaveFile(const std::vector<uint8_t> & fileData)560 bool AudioFile<T>::decodeWaveFile (const std::vector<uint8_t>& fileData)
561 {
562     // -----------------------------------------------------------
563     // HEADER CHUNK
564     std::string headerChunkID (fileData.begin(), fileData.begin() + 4);
565     //int32_t fileSizeInBytes = fourBytesToInt (fileData, 4) + 8;
566     std::string format (fileData.begin() + 8, fileData.begin() + 12);
567 
568     // -----------------------------------------------------------
569     // try and find the start points of key chunks
570     int indexOfDataChunk = getIndexOfChunk (fileData, "data", 12);
571     int indexOfFormatChunk = getIndexOfChunk (fileData, "fmt ", 12);
572     int indexOfXMLChunk = getIndexOfChunk (fileData, "iXML", 12);
573 
574     // if we can't find the data or format chunks, or the IDs/formats don't seem to be as expected
575     // then it is unlikely we'll able to read this file, so abort
576     if (indexOfDataChunk == -1 || indexOfFormatChunk == -1 || headerChunkID != "RIFF" || format != "WAVE")
577     {
578         reportError ("ERROR: this doesn't seem to be a valid .WAV file");
579         return false;
580     }
581 
582     // -----------------------------------------------------------
583     // FORMAT CHUNK
584     int f = indexOfFormatChunk;
585     std::string formatChunkID (fileData.begin() + f, fileData.begin() + f + 4);
586     //int32_t formatChunkSize = fourBytesToInt (fileData, f + 4);
587     uint16_t audioFormat = twoBytesToInt (fileData, f + 8);
588     uint16_t numChannels = twoBytesToInt (fileData, f + 10);
589     sampleRate = (uint32_t) fourBytesToInt (fileData, f + 12);
590     uint32_t numBytesPerSecond = fourBytesToInt (fileData, f + 16);
591     uint16_t numBytesPerBlock = twoBytesToInt (fileData, f + 20);
592     bitDepth = (int) twoBytesToInt (fileData, f + 22);
593 
594     if (bitDepth > sizeof (T) * 8)
595     {
596         std::string message = "ERROR: you are trying to read a ";
597         message += std::to_string (bitDepth);
598         message += "-bit file using a ";
599         message += std::to_string (sizeof (T) * 8);
600         message += "-bit sample type";
601         reportError (message);
602         return false;
603     }
604 
605     uint16_t numBytesPerSample = static_cast<uint16_t> (bitDepth) / 8;
606 
607     // check that the audio format is PCM or Float or extensible
608     if (audioFormat != WavAudioFormat::PCM && audioFormat != WavAudioFormat::IEEEFloat && audioFormat != WavAudioFormat::Extensible)
609     {
610         reportError ("ERROR: this .WAV file is encoded in a format that this library does not support at present");
611         return false;
612     }
613 
614     // check the number of channels is mono or stereo
615     if (numChannels < 1 || numChannels > 128)
616     {
617         reportError ("ERROR: this WAV file seems to be an invalid number of channels (or corrupted?)");
618         return false;
619     }
620 
621     // check header data is consistent
622     if (numBytesPerSecond != static_cast<uint32_t> ((numChannels * sampleRate * bitDepth) / 8) || numBytesPerBlock != (numChannels * numBytesPerSample))
623     {
624         reportError ("ERROR: the header data in this WAV file seems to be inconsistent");
625         return false;
626     }
627 
628     // check bit depth is either 8, 16, 24 or 32 bit
629     if (bitDepth != 8 && bitDepth != 16 && bitDepth != 24 && bitDepth != 32)
630     {
631         reportError ("ERROR: this file has a bit depth that is not 8, 16, 24 or 32 bits");
632         return false;
633     }
634 
635     // -----------------------------------------------------------
636     // DATA CHUNK
637     int d = indexOfDataChunk;
638     std::string dataChunkID (fileData.begin() + d, fileData.begin() + d + 4);
639     int32_t dataChunkSize = fourBytesToInt (fileData, d + 4);
640 
641     int numSamples = dataChunkSize / (numChannels * bitDepth / 8);
642     int samplesStartIndex = indexOfDataChunk + 8;
643 
644     clearAudioBuffer();
645     samples.resize (numChannels);
646 
647     for (int i = 0; i < numSamples; i++)
648     {
649         for (int channel = 0; channel < numChannels; channel++)
650         {
651             int sampleIndex = samplesStartIndex + (numBytesPerBlock * i) + channel * numBytesPerSample;
652 
653             if ((sampleIndex + (bitDepth / 8) - 1) >= fileData.size())
654             {
655                 reportError ("ERROR: read file error as the metadata indicates more samples than there are in the file data");
656                 return false;
657             }
658 
659             if (bitDepth == 8)
660             {
661                 T sample = AudioSampleConverter<T>::unsignedByteToSample (fileData[sampleIndex]);
662                 samples[channel].push_back (sample);
663             }
664             else if (bitDepth == 16)
665             {
666                 int16_t sampleAsInt = twoBytesToInt (fileData, sampleIndex);
667                 T sample = AudioSampleConverter<T>::sixteenBitIntToSample (sampleAsInt);
668                 samples[channel].push_back (sample);
669             }
670             else if (bitDepth == 24)
671             {
672                 int32_t sampleAsInt = 0;
673                 sampleAsInt = (fileData[sampleIndex + 2] << 16) | (fileData[sampleIndex + 1] << 8) | fileData[sampleIndex];
674 
675                 if (sampleAsInt & 0x800000) //  if the 24th bit is set, this is a negative number in 24-bit world
676                     sampleAsInt = sampleAsInt | ~0xFFFFFF; // so make sure sign is extended to the 32 bit float
677 
678                 T sample = AudioSampleConverter<T>::twentyFourBitIntToSample (sampleAsInt);
679                 samples[channel].push_back (sample);
680             }
681             else if (bitDepth == 32)
682             {
683                 int32_t sampleAsInt = fourBytesToInt (fileData, sampleIndex);
684                 T sample;
685 
686                 if (audioFormat == WavAudioFormat::IEEEFloat && std::is_floating_point_v<T>)
687                 {
688                     float f;
689                     memcpy (&f, &sampleAsInt, sizeof(int32_t));
690                     sample = (T)f;
691                 }
692                 else // assume PCM
693                 {
694                     sample = AudioSampleConverter<T>::thirtyTwoBitIntToSample (sampleAsInt);
695                 }
696 
697                 samples[channel].push_back (sample);
698             }
699             else
700             {
701                 assert (false);
702             }
703         }
704     }
705 
706     // -----------------------------------------------------------
707     // iXML CHUNK
708     if (indexOfXMLChunk != -1)
709     {
710         int32_t chunkSize = fourBytesToInt (fileData, indexOfXMLChunk + 4);
711         iXMLChunk = std::string ((const char*) &fileData[indexOfXMLChunk + 8], chunkSize);
712     }
713 
714     return true;
715 }
716 
717 //=============================================================
718 template <class T>
decodeAiffFile(const std::vector<uint8_t> & fileData)719 bool AudioFile<T>::decodeAiffFile (const std::vector<uint8_t>& fileData)
720 {
721     // -----------------------------------------------------------
722     // HEADER CHUNK
723     std::string headerChunkID (fileData.begin(), fileData.begin() + 4);
724     //int32_t fileSizeInBytes = fourBytesToInt (fileData, 4, Endianness::BigEndian) + 8;
725     std::string format (fileData.begin() + 8, fileData.begin() + 12);
726 
727     int audioFormat = format == "AIFF" ? AIFFAudioFormat::Uncompressed : format == "AIFC" ? AIFFAudioFormat::Compressed : AIFFAudioFormat::Error;
728 
729     // -----------------------------------------------------------
730     // try and find the start points of key chunks
731     int indexOfCommChunk = getIndexOfChunk (fileData, "COMM", 12, Endianness::BigEndian);
732     int indexOfSoundDataChunk = getIndexOfChunk (fileData, "SSND", 12, Endianness::BigEndian);
733     int indexOfXMLChunk = getIndexOfChunk (fileData, "iXML", 12, Endianness::BigEndian);
734 
735     // if we can't find the data or format chunks, or the IDs/formats don't seem to be as expected
736     // then it is unlikely we'll able to read this file, so abort
737     if (indexOfSoundDataChunk == -1 || indexOfCommChunk == -1 || headerChunkID != "FORM" || audioFormat == AIFFAudioFormat::Error)
738     {
739         reportError ("ERROR: this doesn't seem to be a valid AIFF file");
740         return false;
741     }
742 
743     // -----------------------------------------------------------
744     // COMM CHUNK
745     int p = indexOfCommChunk;
746     std::string commChunkID (fileData.begin() + p, fileData.begin() + p + 4);
747     //int32_t commChunkSize = fourBytesToInt (fileData, p + 4, Endianness::BigEndian);
748     int16_t numChannels = twoBytesToInt (fileData, p + 8, Endianness::BigEndian);
749     int32_t numSamplesPerChannel = fourBytesToInt (fileData, p + 10, Endianness::BigEndian);
750     bitDepth = (int) twoBytesToInt (fileData, p + 14, Endianness::BigEndian);
751     sampleRate = getAiffSampleRate (fileData, p + 16);
752 
753     if (bitDepth > sizeof (T) * 8)
754     {
755         std::string message = "ERROR: you are trying to read a ";
756         message += std::to_string (bitDepth);
757         message += "-bit file using a ";
758         message += std::to_string (sizeof (T) * 8);
759         message += "-bit sample type";
760         reportError (message);
761         return false;
762     }
763 
764     // check the sample rate was properly decoded
765     if (sampleRate == 0)
766     {
767         reportError ("ERROR: this AIFF file has an unsupported sample rate");
768         return false;
769     }
770 
771     // check the number of channels is mono or stereo
772     if (numChannels < 1 ||numChannels > 2)
773     {
774         reportError ("ERROR: this AIFF file seems to be neither mono nor stereo (perhaps multi-track, or corrupted?)");
775         return false;
776     }
777 
778     // check bit depth is either 8, 16, 24 or 32-bit
779     if (bitDepth != 8 && bitDepth != 16 && bitDepth != 24 && bitDepth != 32)
780     {
781         reportError ("ERROR: this file has a bit depth that is not 8, 16, 24 or 32 bits");
782         return false;
783     }
784 
785     // -----------------------------------------------------------
786     // SSND CHUNK
787     int s = indexOfSoundDataChunk;
788     std::string soundDataChunkID (fileData.begin() + s, fileData.begin() + s + 4);
789     int32_t soundDataChunkSize = fourBytesToInt (fileData, s + 4, Endianness::BigEndian);
790     int32_t offset = fourBytesToInt (fileData, s + 8, Endianness::BigEndian);
791     //int32_t blockSize = fourBytesToInt (fileData, s + 12, Endianness::BigEndian);
792 
793     int numBytesPerSample = bitDepth / 8;
794     int numBytesPerFrame = numBytesPerSample * numChannels;
795     int totalNumAudioSampleBytes = numSamplesPerChannel * numBytesPerFrame;
796     int samplesStartIndex = s + 16 + (int)offset;
797 
798     // sanity check the data
799     if ((soundDataChunkSize - 8) != totalNumAudioSampleBytes || totalNumAudioSampleBytes > static_cast<long>(fileData.size() - samplesStartIndex))
800     {
801         reportError ("ERROR: the metadatafor this file doesn't seem right");
802         return false;
803     }
804 
805     clearAudioBuffer();
806     samples.resize (numChannels);
807 
808     for (int i = 0; i < numSamplesPerChannel; i++)
809     {
810         for (int channel = 0; channel < numChannels; channel++)
811         {
812             int sampleIndex = samplesStartIndex + (numBytesPerFrame * i) + channel * numBytesPerSample;
813 
814             if ((sampleIndex + (bitDepth / 8) - 1) >= fileData.size())
815             {
816                 reportError ("ERROR: read file error as the metadata indicates more samples than there are in the file data");
817                 return false;
818             }
819 
820             if (bitDepth == 8)
821             {
822                 T sample = AudioSampleConverter<T>::signedByteToSample (static_cast<int8_t> (fileData[sampleIndex]));
823                 samples[channel].push_back (sample);
824             }
825             else if (bitDepth == 16)
826             {
827                 int16_t sampleAsInt = twoBytesToInt (fileData, sampleIndex, Endianness::BigEndian);
828                 T sample = AudioSampleConverter<T>::sixteenBitIntToSample (sampleAsInt);
829                 samples[channel].push_back (sample);
830             }
831             else if (bitDepth == 24)
832             {
833                 int32_t sampleAsInt = 0;
834                 sampleAsInt = (fileData[sampleIndex] << 16) | (fileData[sampleIndex + 1] << 8) | fileData[sampleIndex + 2];
835 
836                 if (sampleAsInt & 0x800000) //  if the 24th bit is set, this is a negative number in 24-bit world
837                     sampleAsInt = sampleAsInt | ~0xFFFFFF; // so make sure sign is extended to the 32 bit float
838 
839                 T sample = AudioSampleConverter<T>::twentyFourBitIntToSample (sampleAsInt);
840                 samples[channel].push_back (sample);
841             }
842             else if (bitDepth == 32)
843             {
844                 int32_t sampleAsInt = fourBytesToInt (fileData, sampleIndex, Endianness::BigEndian);
845                 T sample;
846 
847                 if (audioFormat == AIFFAudioFormat::Compressed)
848                     sample = (T)reinterpret_cast<float&> (sampleAsInt);
849                 else // assume PCM
850                     sample = AudioSampleConverter<T>::thirtyTwoBitIntToSample (sampleAsInt);
851 
852                 samples[channel].push_back (sample);
853             }
854             else
855             {
856                 assert (false);
857             }
858         }
859     }
860 
861     // -----------------------------------------------------------
862     // iXML CHUNK
863     if (indexOfXMLChunk != -1)
864     {
865         int32_t chunkSize = fourBytesToInt (fileData, indexOfXMLChunk + 4);
866         iXMLChunk = std::string ((const char*) &fileData[indexOfXMLChunk + 8], chunkSize);
867     }
868 
869     return true;
870 }
871 
872 //=============================================================
873 template <class T>
getAiffSampleRate(const std::vector<uint8_t> & fileData,int sampleRateStartIndex)874 uint32_t AudioFile<T>::getAiffSampleRate (const std::vector<uint8_t>& fileData, int sampleRateStartIndex)
875 {
876     double sampleRate = AiffUtilities::decodeAiffSampleRate (&fileData[sampleRateStartIndex]);
877     return static_cast<uint32_t> (sampleRate);
878 }
879 
880 //=============================================================
881 template <class T>
addSampleRateToAiffData(std::vector<uint8_t> & fileData,uint32_t sampleRateToAdd)882 void AudioFile<T>::addSampleRateToAiffData (std::vector<uint8_t>& fileData, uint32_t sampleRateToAdd)
883 {
884     std::array<uint8_t, 10> sampleRateData;
885     AiffUtilities::encodeAiffSampleRate (static_cast<double> (sampleRateToAdd), sampleRateData.data());
886     fileData.insert (fileData.end(), sampleRateData.begin(), sampleRateData.end());
887 }
888 
889 //=============================================================
890 template <class T>
save(const std::string & filePath,AudioFileFormat format)891 bool AudioFile<T>::save (const std::string& filePath, AudioFileFormat format)
892 {
893     if (format == AudioFileFormat::Wave)
894     {
895         return saveToWaveFile (filePath);
896     }
897     else if (format == AudioFileFormat::Aiff)
898     {
899         return saveToAiffFile (filePath);
900     }
901 
902     return false;
903 }
904 
905 //=============================================================
906 template <class T>
saveToWaveFile(const std::string & filePath)907 bool AudioFile<T>::saveToWaveFile (const std::string& filePath)
908 {
909     std::vector<uint8_t> fileData;
910 
911     int32_t dataChunkSize = getNumSamplesPerChannel() * (getNumChannels() * bitDepth / 8);
912     int16_t audioFormat = bitDepth == 32 && std::is_floating_point_v<T> ? WavAudioFormat::IEEEFloat : WavAudioFormat::PCM;
913     int32_t formatChunkSize = audioFormat == WavAudioFormat::PCM ? 16 : 18;
914     int32_t iXMLChunkSize = static_cast<int32_t> (iXMLChunk.size());
915 
916     // -----------------------------------------------------------
917     // HEADER CHUNK
918     addStringToFileData (fileData, "RIFF");
919 
920     // The file size in bytes is the header chunk size (4, not counting RIFF and WAVE) + the format
921     // chunk size (24) + the metadata part of the data chunk plus the actual data chunk size
922     int32_t fileSizeInBytes = 4 + formatChunkSize + 8 + 8 + dataChunkSize;
923     if (iXMLChunkSize > 0)
924     {
925         fileSizeInBytes += (8 + iXMLChunkSize);
926     }
927 
928     addInt32ToFileData (fileData, fileSizeInBytes);
929 
930     addStringToFileData (fileData, "WAVE");
931 
932     // -----------------------------------------------------------
933     // FORMAT CHUNK
934     addStringToFileData (fileData, "fmt ");
935     addInt32ToFileData (fileData, formatChunkSize); // format chunk size (16 for PCM)
936     addInt16ToFileData (fileData, audioFormat); // audio format
937     addInt16ToFileData (fileData, (int16_t)getNumChannels()); // num channels
938     addInt32ToFileData (fileData, (int32_t)sampleRate); // sample rate
939 
940     int32_t numBytesPerSecond = (int32_t) ((getNumChannels() * sampleRate * bitDepth) / 8);
941     addInt32ToFileData (fileData, numBytesPerSecond);
942 
943     int16_t numBytesPerBlock = getNumChannels() * (bitDepth / 8);
944     addInt16ToFileData (fileData, numBytesPerBlock);
945 
946     addInt16ToFileData (fileData, (int16_t)bitDepth);
947 
948     if (audioFormat == WavAudioFormat::IEEEFloat)
949         addInt16ToFileData (fileData, 0); // extension size
950 
951     // -----------------------------------------------------------
952     // DATA CHUNK
953     addStringToFileData (fileData, "data");
954     addInt32ToFileData (fileData, dataChunkSize);
955 
956     for (int i = 0; i < getNumSamplesPerChannel(); i++)
957     {
958         for (int channel = 0; channel < getNumChannels(); channel++)
959         {
960             if (bitDepth == 8)
961             {
962                 uint8_t byte = AudioSampleConverter<T>::sampleToUnsignedByte (samples[channel][i]);
963                 fileData.push_back (byte);
964             }
965             else if (bitDepth == 16)
966             {
967                 int16_t sampleAsInt = AudioSampleConverter<T>::sampleToSixteenBitInt (samples[channel][i]);
968                 addInt16ToFileData (fileData, sampleAsInt);
969             }
970             else if (bitDepth == 24)
971             {
972                 int32_t sampleAsIntAgain = AudioSampleConverter<T>::sampleToTwentyFourBitInt (samples[channel][i]);
973 
974                 uint8_t bytes[3];
975                 bytes[2] = (uint8_t) (sampleAsIntAgain >> 16) & 0xFF;
976                 bytes[1] = (uint8_t) (sampleAsIntAgain >>  8) & 0xFF;
977                 bytes[0] = (uint8_t) sampleAsIntAgain & 0xFF;
978 
979                 fileData.push_back (bytes[0]);
980                 fileData.push_back (bytes[1]);
981                 fileData.push_back (bytes[2]);
982             }
983             else if (bitDepth == 32)
984             {
985                 int32_t sampleAsInt;
986 
987                 if (audioFormat == WavAudioFormat::IEEEFloat)
988                     sampleAsInt = (int32_t) reinterpret_cast<int32_t&> (samples[channel][i]);
989                 else // assume PCM
990                     sampleAsInt = AudioSampleConverter<T>::sampleToThirtyTwoBitInt (samples[channel][i]);
991 
992                 addInt32ToFileData (fileData, sampleAsInt, Endianness::LittleEndian);
993             }
994             else
995             {
996                 assert (false && "Trying to write a file with unsupported bit depth");
997                 return false;
998             }
999         }
1000     }
1001 
1002     // -----------------------------------------------------------
1003     // iXML CHUNK
1004     if (iXMLChunkSize > 0)
1005     {
1006         addStringToFileData (fileData, "iXML");
1007         addInt32ToFileData (fileData, iXMLChunkSize);
1008         addStringToFileData (fileData, iXMLChunk);
1009     }
1010 
1011     // check that the various sizes we put in the metadata are correct
1012     if (fileSizeInBytes != static_cast<int32_t> (fileData.size() - 8) || dataChunkSize != (getNumSamplesPerChannel() * getNumChannels() * (bitDepth / 8)))
1013     {
1014         reportError ("ERROR: couldn't save file to " + filePath);
1015         return false;
1016     }
1017 
1018     // try to write the file
1019     return writeDataToFile (fileData, filePath);
1020 }
1021 
1022 //=============================================================
1023 template <class T>
saveToAiffFile(const std::string & filePath)1024 bool AudioFile<T>::saveToAiffFile (const std::string& filePath)
1025 {
1026     std::vector<uint8_t> fileData;
1027 
1028     int32_t numBytesPerSample = bitDepth / 8;
1029     int32_t numBytesPerFrame = numBytesPerSample * getNumChannels();
1030     int32_t totalNumAudioSampleBytes = getNumSamplesPerChannel() * numBytesPerFrame;
1031     int32_t soundDataChunkSize = totalNumAudioSampleBytes + 8;
1032     int32_t iXMLChunkSize = static_cast<int32_t> (iXMLChunk.size());
1033 
1034     // -----------------------------------------------------------
1035     // HEADER CHUNK
1036     addStringToFileData (fileData, "FORM");
1037 
1038     // The file size in bytes is the header chunk size (4, not counting FORM and AIFF) + the COMM
1039     // chunk size (26) + the metadata part of the SSND chunk plus the actual data chunk size
1040     int32_t fileSizeInBytes = 4 + 26 + 16 + totalNumAudioSampleBytes;
1041     if (iXMLChunkSize > 0)
1042     {
1043         fileSizeInBytes += (8 + iXMLChunkSize);
1044     }
1045 
1046     addInt32ToFileData (fileData, fileSizeInBytes, Endianness::BigEndian);
1047 
1048     addStringToFileData (fileData, "AIFF");
1049 
1050     // -----------------------------------------------------------
1051     // COMM CHUNK
1052     addStringToFileData (fileData, "COMM");
1053     addInt32ToFileData (fileData, 18, Endianness::BigEndian); // commChunkSize
1054     addInt16ToFileData (fileData, getNumChannels(), Endianness::BigEndian); // num channels
1055     addInt32ToFileData (fileData, getNumSamplesPerChannel(), Endianness::BigEndian); // num samples per channel
1056     addInt16ToFileData (fileData, bitDepth, Endianness::BigEndian); // bit depth
1057     addSampleRateToAiffData (fileData, sampleRate);
1058 
1059     // -----------------------------------------------------------
1060     // SSND CHUNK
1061     addStringToFileData (fileData, "SSND");
1062     addInt32ToFileData (fileData, soundDataChunkSize, Endianness::BigEndian);
1063     addInt32ToFileData (fileData, 0, Endianness::BigEndian); // offset
1064     addInt32ToFileData (fileData, 0, Endianness::BigEndian); // block size
1065 
1066     for (int i = 0; i < getNumSamplesPerChannel(); i++)
1067     {
1068         for (int channel = 0; channel < getNumChannels(); channel++)
1069         {
1070             if (bitDepth == 8)
1071             {
1072                 uint8_t byte = static_cast<uint8_t> (AudioSampleConverter<T>::sampleToSignedByte (samples[channel][i]));
1073                 fileData.push_back (byte);
1074             }
1075             else if (bitDepth == 16)
1076             {
1077                 int16_t sampleAsInt = AudioSampleConverter<T>::sampleToSixteenBitInt (samples[channel][i]);
1078                 addInt16ToFileData (fileData, sampleAsInt, Endianness::BigEndian);
1079             }
1080             else if (bitDepth == 24)
1081             {
1082                 int32_t sampleAsIntAgain = AudioSampleConverter<T>::sampleToTwentyFourBitInt (samples[channel][i]);
1083 
1084                 uint8_t bytes[3];
1085                 bytes[0] = (uint8_t) (sampleAsIntAgain >> 16) & 0xFF;
1086                 bytes[1] = (uint8_t) (sampleAsIntAgain >>  8) & 0xFF;
1087                 bytes[2] = (uint8_t) sampleAsIntAgain & 0xFF;
1088 
1089                 fileData.push_back (bytes[0]);
1090                 fileData.push_back (bytes[1]);
1091                 fileData.push_back (bytes[2]);
1092             }
1093             else if (bitDepth == 32)
1094             {
1095                 // write samples as signed integers (no implementation yet for floating point, but looking at WAV implementation should help)
1096                 int32_t sampleAsInt = AudioSampleConverter<T>::sampleToThirtyTwoBitInt (samples[channel][i]);
1097                 addInt32ToFileData (fileData, sampleAsInt, Endianness::BigEndian);
1098             }
1099             else
1100             {
1101                 assert (false && "Trying to write a file with unsupported bit depth");
1102                 return false;
1103             }
1104         }
1105     }
1106 
1107     // -----------------------------------------------------------
1108     // iXML CHUNK
1109     if (iXMLChunkSize > 0)
1110     {
1111         addStringToFileData (fileData, "iXML");
1112         addInt32ToFileData (fileData, iXMLChunkSize, Endianness::BigEndian);
1113         addStringToFileData (fileData, iXMLChunk);
1114     }
1115 
1116     // check that the various sizes we put in the metadata are correct
1117     if (fileSizeInBytes != static_cast<int32_t> (fileData.size() - 8) || soundDataChunkSize != getNumSamplesPerChannel() *  numBytesPerFrame + 8)
1118     {
1119         reportError ("ERROR: couldn't save file to " + filePath);
1120         return false;
1121     }
1122 
1123     // try to write the file
1124     return writeDataToFile (fileData, filePath);
1125 }
1126 
1127 //=============================================================
1128 template <class T>
writeDataToFile(const std::vector<uint8_t> & fileData,std::string filePath)1129 bool AudioFile<T>::writeDataToFile (const std::vector<uint8_t>& fileData, std::string filePath)
1130 {
1131     std::ofstream outputFile (filePath, std::ios::binary);
1132 
1133     if (!outputFile.is_open())
1134     {
1135         return false;
1136     }
1137 
1138     outputFile.write ((const char*)fileData.data(), fileData.size());
1139     outputFile.close();
1140     return true;
1141 }
1142 
1143 //=============================================================
1144 template <class T>
addStringToFileData(std::vector<uint8_t> & fileData,std::string s)1145 void AudioFile<T>::addStringToFileData (std::vector<uint8_t>& fileData, std::string s)
1146 {
1147     for (size_t i = 0; i < s.length();i++)
1148         fileData.push_back ((uint8_t) s[i]);
1149 }
1150 
1151 //=============================================================
1152 template <class T>
addInt32ToFileData(std::vector<uint8_t> & fileData,int32_t i,Endianness endianness)1153 void AudioFile<T>::addInt32ToFileData (std::vector<uint8_t>& fileData, int32_t i, Endianness endianness)
1154 {
1155     uint8_t bytes[4];
1156 
1157     if (endianness == Endianness::LittleEndian)
1158     {
1159         bytes[3] = (i >> 24) & 0xFF;
1160         bytes[2] = (i >> 16) & 0xFF;
1161         bytes[1] = (i >> 8) & 0xFF;
1162         bytes[0] = i & 0xFF;
1163     }
1164     else
1165     {
1166         bytes[0] = (i >> 24) & 0xFF;
1167         bytes[1] = (i >> 16) & 0xFF;
1168         bytes[2] = (i >> 8) & 0xFF;
1169         bytes[3] = i & 0xFF;
1170     }
1171 
1172     for (int j = 0; j < 4; j++)
1173         fileData.push_back (bytes[j]);
1174 }
1175 
1176 //=============================================================
1177 template <class T>
addInt16ToFileData(std::vector<uint8_t> & fileData,int16_t i,Endianness endianness)1178 void AudioFile<T>::addInt16ToFileData (std::vector<uint8_t>& fileData, int16_t i, Endianness endianness)
1179 {
1180     uint8_t bytes[2];
1181 
1182     if (endianness == Endianness::LittleEndian)
1183     {
1184         bytes[1] = (i >> 8) & 0xFF;
1185         bytes[0] = i & 0xFF;
1186     }
1187     else
1188     {
1189         bytes[0] = (i >> 8) & 0xFF;
1190         bytes[1] = i & 0xFF;
1191     }
1192 
1193     fileData.push_back (bytes[0]);
1194     fileData.push_back (bytes[1]);
1195 }
1196 
1197 //=============================================================
1198 template <class T>
clearAudioBuffer()1199 void AudioFile<T>::clearAudioBuffer()
1200 {
1201     for (size_t i = 0; i < samples.size();i++)
1202     {
1203         samples[i].clear();
1204     }
1205 
1206     samples.clear();
1207 }
1208 
1209 //=============================================================
1210 template <class T>
determineAudioFileFormat(const std::vector<uint8_t> & fileData)1211 AudioFileFormat AudioFile<T>::determineAudioFileFormat (const std::vector<uint8_t>& fileData)
1212 {
1213     if (fileData.size() < 4)
1214         return AudioFileFormat::Error;
1215 
1216     std::string header (fileData.begin(), fileData.begin() + 4);
1217 
1218     if (header == "RIFF")
1219         return AudioFileFormat::Wave;
1220     else if (header == "FORM")
1221         return AudioFileFormat::Aiff;
1222     else
1223         return AudioFileFormat::Error;
1224 }
1225 
1226 //=============================================================
1227 template <class T>
fourBytesToInt(const std::vector<uint8_t> & source,int startIndex,Endianness endianness)1228 int32_t AudioFile<T>::fourBytesToInt (const std::vector<uint8_t>& source, int startIndex, Endianness endianness)
1229 {
1230     if (source.size() >= (startIndex + 4))
1231     {
1232         int32_t result;
1233 
1234         if (endianness == Endianness::LittleEndian)
1235             result = (source[startIndex + 3] << 24) | (source[startIndex + 2] << 16) | (source[startIndex + 1] << 8) | source[startIndex];
1236         else
1237             result = (source[startIndex] << 24) | (source[startIndex + 1] << 16) | (source[startIndex + 2] << 8) | source[startIndex + 3];
1238 
1239         return result;
1240     }
1241     else
1242     {
1243         assert (false && "Attempted to read four bytes from vector at position where out of bounds access would occur");
1244         return 0; // this is a dummy value as we don't have one to return
1245     }
1246 }
1247 
1248 //=============================================================
1249 template <class T>
twoBytesToInt(const std::vector<uint8_t> & source,int startIndex,Endianness endianness)1250 int16_t AudioFile<T>::twoBytesToInt (const std::vector<uint8_t>& source, int startIndex, Endianness endianness)
1251 {
1252     int16_t result;
1253 
1254     if (endianness == Endianness::LittleEndian)
1255         result = (source[startIndex + 1] << 8) | source[startIndex];
1256     else
1257         result = (source[startIndex] << 8) | source[startIndex + 1];
1258 
1259     return result;
1260 }
1261 
1262 //=============================================================
1263 template <class T>
getIndexOfChunk(const std::vector<uint8_t> & source,const std::string & chunkHeaderID,int startIndex,Endianness endianness)1264 int AudioFile<T>::getIndexOfChunk (const std::vector<uint8_t>& source, const std::string& chunkHeaderID, int startIndex, Endianness endianness)
1265 {
1266     constexpr int dataLen = 4;
1267 
1268     if (chunkHeaderID.size() != dataLen)
1269     {
1270         assert (false && "Invalid chunk header ID string");
1271         return -1;
1272     }
1273 
1274     int i = startIndex;
1275     while (i < source.size() - dataLen)
1276     {
1277         if (memcmp (&source[i], chunkHeaderID.data(), dataLen) == 0)
1278         {
1279             return i;
1280         }
1281 
1282         i += dataLen;
1283 
1284         // If somehow we don't have 4 bytes left to read, then exit with -1
1285         if ((i + 4) >= source.size())
1286             return -1;
1287 
1288         int32_t chunkSize = fourBytesToInt (source, i, endianness);
1289         // Assume chunk size is invalid if it's greater than the number of bytes remaining in source
1290         if (chunkSize > (source.size() - i - dataLen) || (chunkSize < 0))
1291         {
1292             assert (false && "Invalid chunk size");
1293             return -1;
1294         }
1295         i += (dataLen + chunkSize);
1296     }
1297 
1298     return -1;
1299 }
1300 
1301 //=============================================================
1302 template <class T>
reportError(const std::string & errorMessage)1303 void AudioFile<T>::reportError (const std::string& errorMessage)
1304 {
1305     if (logErrorsToConsole)
1306         std::cerr << errorMessage << std::endl;
1307 }
1308 
1309 //=============================================================
1310 template <typename SignedType>
convertSignedToUnsigned(SignedType signedValue)1311 typename std::make_unsigned<SignedType>::type convertSignedToUnsigned (SignedType signedValue)
1312 {
1313     static_assert (std::is_signed<SignedType>::value, "The input value must be signed");
1314 
1315     typename std::make_unsigned<SignedType>::type unsignedValue = static_cast<typename std::make_unsigned<SignedType>::type> (1) + std::numeric_limits<SignedType>::max();
1316 
1317     unsignedValue += signedValue;
1318     return unsignedValue;
1319 }
1320 
1321 //=============================================================
1322 enum SampleLimit
1323 {
1324     SignedInt16_Min = -32768,
1325     SignedInt16_Max = 32767,
1326     UnsignedInt16_Min = 0,
1327     UnsignedInt16_Max = 65535,
1328     SignedInt24_Min = -8388608,
1329     SignedInt24_Max = 8388607,
1330     UnsignedInt24_Min = 0,
1331     UnsignedInt24_Max = 16777215
1332 };
1333 
1334 //=============================================================
1335 template <class T>
thirtyTwoBitIntToSample(int32_t sample)1336 T AudioSampleConverter<T>::thirtyTwoBitIntToSample (int32_t sample)
1337 {
1338     if constexpr (std::is_floating_point<T>::value)
1339     {
1340         return static_cast<T> (sample) / static_cast<T> (std::numeric_limits<int32_t>::max());
1341     }
1342     else if (std::numeric_limits<T>::is_integer)
1343     {
1344         if constexpr (std::is_signed_v<T>)
1345 			return static_cast<T> (sample);
1346         else
1347             return static_cast<T> (clamp (static_cast<T> (sample + 2147483648), 0, 4294967295));
1348     }
1349 }
1350 
1351 //=============================================================
1352 template <class T>
sampleToThirtyTwoBitInt(T sample)1353 int32_t AudioSampleConverter<T>::sampleToThirtyTwoBitInt (T sample)
1354 {
1355     if constexpr (std::is_floating_point<T>::value)
1356     {
1357         // multiplying a float by a the max int32_t is problematic because
1358         // of roundng errors which can cause wrong values to come out, so
1359         // we use a different implementation here compared to other types
1360         if constexpr (std::is_same_v<T, float>)
1361         {
1362             if (sample >= 1.f)
1363                 return std::numeric_limits<int32_t>::max();
1364             else if (sample <= -1.f)
1365                 return std::numeric_limits<int32_t>::lowest() + 1; // starting at 1 preserves symmetry
1366             else
1367                 return static_cast<int32_t> (sample * std::numeric_limits<int32_t>::max());
1368         }
1369         else
1370         {
1371             return static_cast<int32_t> (clamp (sample, -1., 1.) * std::numeric_limits<int32_t>::max());
1372         }
1373     }
1374     else
1375     {
1376         if constexpr (std::is_signed_v<T>)
1377             return static_cast<int32_t> (clamp (sample, -2147483648LL, 2147483647LL));
1378         else
1379             return static_cast<int32_t> (clamp (sample, 0, 4294967295) - 2147483648);
1380     }
1381 }
1382 
1383 //=============================================================
1384 template <class T>
twentyFourBitIntToSample(int32_t sample)1385 T AudioSampleConverter<T>::twentyFourBitIntToSample (int32_t sample)
1386 {
1387     if constexpr (std::is_floating_point<T>::value)
1388     {
1389         return static_cast<T> (sample) / static_cast<T> (8388607.);
1390     }
1391     else if (std::numeric_limits<T>::is_integer)
1392     {
1393         if constexpr (std::is_signed_v<T>)
1394             return static_cast<T> (clamp (sample, SignedInt24_Min, SignedInt24_Max));
1395         else
1396             return static_cast<T> (clamp (sample + 8388608, UnsignedInt24_Min, UnsignedInt24_Max));
1397     }
1398 }
1399 
1400 //=============================================================
1401 template <class T>
sampleToTwentyFourBitInt(T sample)1402 int32_t AudioSampleConverter<T>::sampleToTwentyFourBitInt (T sample)
1403 {
1404     if constexpr (std::is_floating_point<T>::value)
1405     {
1406         sample = clamp (sample, -1., 1.);
1407         return static_cast<int32_t> (sample * 8388607.);
1408     }
1409     else
1410     {
1411         if constexpr (std::is_signed_v<T>)
1412             return static_cast<int32_t> (clamp (sample, SignedInt24_Min, SignedInt24_Max));
1413         else
1414             return static_cast<int32_t> (clamp (sample, UnsignedInt24_Min, UnsignedInt24_Max) + SignedInt24_Min);
1415     }
1416 }
1417 
1418 //=============================================================
1419 template <class T>
sixteenBitIntToSample(int16_t sample)1420 T AudioSampleConverter<T>::sixteenBitIntToSample (int16_t sample)
1421 {
1422     if constexpr (std::is_floating_point<T>::value)
1423     {
1424         return static_cast<T> (sample) / static_cast<T> (32767.);
1425     }
1426     else if constexpr (std::numeric_limits<T>::is_integer)
1427     {
1428         if constexpr (std::is_signed_v<T>)
1429             return static_cast<T> (sample);
1430         else
1431             return static_cast<T> (convertSignedToUnsigned<int16_t> (sample));
1432     }
1433 }
1434 
1435 //=============================================================
1436 template <class T>
sampleToSixteenBitInt(T sample)1437 int16_t AudioSampleConverter<T>::sampleToSixteenBitInt (T sample)
1438 {
1439     if constexpr (std::is_floating_point<T>::value)
1440     {
1441         sample = clamp (sample, -1., 1.);
1442         return static_cast<int16_t> (sample * 32767.);
1443     }
1444     else
1445     {
1446         if constexpr (std::is_signed_v<T>)
1447             return static_cast<int16_t> (clamp (sample, SignedInt16_Min, SignedInt16_Max));
1448         else
1449             return static_cast<int16_t> (clamp (sample, UnsignedInt16_Min, UnsignedInt16_Max) + SignedInt16_Min);
1450     }
1451 }
1452 
1453 //=============================================================
1454 template <class T>
sampleToUnsignedByte(T sample)1455 uint8_t AudioSampleConverter<T>::sampleToUnsignedByte (T sample)
1456 {
1457     if constexpr (std::is_floating_point<T>::value)
1458     {
1459         sample = clamp (sample, -1., 1.);
1460         sample = (sample + 1.) / 2.;
1461         return static_cast<uint8_t> (1 + (sample * 254));
1462     }
1463     else
1464     {
1465         if constexpr (std::is_signed_v<T>)
1466             return static_cast<uint8_t> (clamp (sample, -128, 127) + 128);
1467         else
1468             return static_cast<uint8_t> (clamp (sample, 0, 255));
1469     }
1470 }
1471 
1472 //=============================================================
1473 template <class T>
sampleToSignedByte(T sample)1474 int8_t AudioSampleConverter<T>::sampleToSignedByte (T sample)
1475 {
1476     if constexpr (std::is_floating_point<T>::value)
1477     {
1478         sample = clamp (sample, -1., 1.);
1479         return static_cast<int8_t> (sample * (T)0x7F);
1480     }
1481     else
1482     {
1483         if constexpr (std::is_signed_v<T>)
1484             return static_cast<int8_t> (clamp (sample, -128, 127));
1485         else
1486             return static_cast<int8_t> (clamp (sample, 0, 255) - 128);
1487     }
1488 }
1489 
1490 //=============================================================
1491 template <class T>
unsignedByteToSample(uint8_t sample)1492 T AudioSampleConverter<T>::unsignedByteToSample (uint8_t sample)
1493 {
1494     if constexpr (std::is_floating_point<T>::value)
1495     {
1496         return static_cast<T> (sample - 128) / static_cast<T> (127.);
1497     }
1498     else if (std::numeric_limits<T>::is_integer)
1499     {
1500         if constexpr (std::is_unsigned_v<T>)
1501             return static_cast<T> (sample);
1502         else
1503             return static_cast<T> (sample - 128);
1504     }
1505 }
1506 
1507 //=============================================================
1508 template <class T>
signedByteToSample(int8_t sample)1509 T AudioSampleConverter<T>::signedByteToSample (int8_t sample)
1510 {
1511     if constexpr (std::is_floating_point<T>::value)
1512     {
1513         return static_cast<T> (sample) / static_cast<T> (127.);
1514     }
1515     else if constexpr (std::numeric_limits<T>::is_integer)
1516     {
1517         if constexpr (std::is_signed_v<T>)
1518             return static_cast<T> (sample);
1519         else
1520             return static_cast<T> (convertSignedToUnsigned<int8_t> (sample));
1521     }
1522 }
1523 
1524 //=============================================================
1525 template <class T>
clamp(T value,T minValue,T maxValue)1526 T AudioSampleConverter<T>::clamp (T value, T minValue, T maxValue)
1527 {
1528     value = std::min (value, maxValue);
1529     value = std::max (value, minValue);
1530     return value;
1531 }
1532 
1533 //=============================================================
decodeAiffSampleRate(const uint8_t * bytes)1534 inline double AiffUtilities::decodeAiffSampleRate (const uint8_t* bytes)
1535 {
1536     // Note: Sample rate is 80 bits made up of
1537     // * 1 sign bit
1538     // * 15 exponent bits
1539     // * 64 mantissa bits
1540 
1541     // ----------------------------------------------
1542     // Sign
1543 
1544     // Extract the sign (most significant bit of byte 0)
1545     int sign = (bytes[0] & 0x80) ? -1 : 1;
1546 
1547     // ----------------------------------------------
1548     // Exponent
1549 
1550     // byte 0: ignore the sign and shift the most significant bits to the left by one byte
1551     uint16_t msbShifted = (static_cast<uint16_t> (bytes[0] & 0x7F) << 8);
1552 
1553     // calculate exponent by combining byte 0 and byte 1 and subtract bias
1554     uint16_t exponent = (msbShifted | static_cast<uint16_t> (bytes[1])) - 16383;
1555 
1556     // ----------------------------------------------
1557     // Mantissa
1558 
1559     // Extract the mantissa (remaining 64 bits) by looping over the remaining
1560     // bytes and combining them while shifting the result to the left by
1561     // 8 bits each time
1562     uint64_t mantissa = 0;
1563 
1564     for (int i = 2; i < 10; ++i)
1565         mantissa = (mantissa << 8) | bytes[i];
1566 
1567     // Normalize the mantissa (implicit leading 1 for normalized values)
1568     double normalisedMantissa = static_cast<double> (mantissa) / (1ULL << 63);
1569 
1570     // ----------------------------------------------
1571     // Combine sign, exponent, and mantissa into a double
1572 
1573     return sign * std::ldexp (normalisedMantissa, exponent);
1574 }
1575 
1576 //=============================================================
encodeAiffSampleRate(double sampleRate,uint8_t * bytes)1577 inline void AiffUtilities::encodeAiffSampleRate (double sampleRate, uint8_t* bytes)
1578 {
1579     // Determine the sign
1580     int sign = (sampleRate < 0) ? -1 : 1;
1581 
1582     if (sign == -1)
1583         sampleRate = -sampleRate;
1584 
1585     // Set most significant bit of byte 0 for the sign
1586     bytes[0] = (sign == -1) ? 0x80 : 0x00;
1587 
1588     // Calculate the exponent using logarithm (log base 2)
1589     int exponent = (log (sampleRate) / log (2.0));
1590 
1591     // Add bias to exponent for AIFF
1592     uint16_t biasedExponent = static_cast<uint16_t> (exponent + 16383);
1593 
1594     // Normalize the sample rate
1595     double normalizedSampleRate = sampleRate / pow (2.0, exponent);
1596 
1597     // Calculate the mantissa
1598     uint64_t mantissa = static_cast<uint64_t> (normalizedSampleRate * (1ULL << 63));
1599 
1600     // Pack the exponent into first two bytes of 10-byte AIFF format
1601     bytes[0] |= (biasedExponent >> 8) & 0x7F;   // Upper 7 bits of exponent
1602     bytes[1] = biasedExponent & 0xFF;           // Lower 8 bits of exponent
1603 
1604     // Put the mantissa into byte array
1605     for (int i = 0; i < 8; ++i)
1606         bytes[2 + i] = (mantissa >> (8 * (7 - i))) & 0xFF;
1607 }
1608 
1609 #if defined (_MSC_VER)
1610     __pragma(warning (pop))
1611 #elif defined (__GNUC__)
1612     _Pragma("GCC diagnostic pop")
1613 #endif
1614 
1615 #endif /* AudioFile_h */
1616