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