You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

874 lines
30KB

  1. //=======================================================================
  2. /** @file AudioFile.cpp
  3. * @author Adam Stark
  4. * @copyright Copyright (C) 2017 Adam Stark
  5. *
  6. * This file is part of the 'AudioFile' library
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. //=======================================================================
  22. #include "AudioFile.h"
  23. #include <fstream>
  24. #include <unordered_map>
  25. #include <iterator>
  26. //=============================================================
  27. // Pre-defined 10-byte representations of common sample rates
  28. std::unordered_map <uint32_t, std::vector<uint8_t>> aiffSampleRateTable = {
  29. {8000, {64, 11, 250, 0, 0, 0, 0, 0, 0, 0}},
  30. {11025, {64, 12, 172, 68, 0, 0, 0, 0, 0, 0}},
  31. {16000, {64, 12, 250, 0, 0, 0, 0, 0, 0, 0}},
  32. {22050, {64, 13, 172, 68, 0, 0, 0, 0, 0, 0}},
  33. {32000, {64, 13, 250, 0, 0, 0, 0, 0, 0, 0}},
  34. {37800, {64, 14, 147, 168, 0, 0, 0, 0, 0, 0}},
  35. {44056, {64, 14, 172, 24, 0, 0, 0, 0, 0, 0}},
  36. {44100, {64, 14, 172, 68, 0, 0, 0, 0, 0, 0}},
  37. {47250, {64, 14, 184, 146, 0, 0, 0, 0, 0, 0}},
  38. {48000, {64, 14, 187, 128, 0, 0, 0, 0, 0, 0}},
  39. {50000, {64, 14, 195, 80, 0, 0, 0, 0, 0, 0}},
  40. {50400, {64, 14, 196, 224, 0, 0, 0, 0, 0, 0}},
  41. {88200, {64, 15, 172, 68, 0, 0, 0, 0, 0, 0}},
  42. {96000, {64, 15, 187, 128, 0, 0, 0, 0, 0, 0}},
  43. {176400, {64, 16, 172, 68, 0, 0, 0, 0, 0, 0}},
  44. {192000, {64, 16, 187, 128, 0, 0, 0, 0, 0, 0}},
  45. {352800, {64, 17, 172, 68, 0, 0, 0, 0, 0, 0}},
  46. {2822400, {64, 20, 172, 68, 0, 0, 0, 0, 0, 0}},
  47. {5644800, {64, 21, 172, 68, 0, 0, 0, 0, 0, 0}}
  48. };
  49. //=============================================================
  50. template <class T>
  51. AudioFile<T>::AudioFile()
  52. {
  53. bitDepth = 16;
  54. sampleRate = 44100;
  55. samples.resize (1);
  56. samples[0].resize (0);
  57. audioFileFormat = AudioFileFormat::NotLoaded;
  58. }
  59. //=============================================================
  60. template <class T>
  61. uint32_t AudioFile<T>::getSampleRate() const
  62. {
  63. return sampleRate;
  64. }
  65. //=============================================================
  66. template <class T>
  67. int AudioFile<T>::getNumChannels() const
  68. {
  69. return (int)samples.size();
  70. }
  71. //=============================================================
  72. template <class T>
  73. bool AudioFile<T>::isMono() const
  74. {
  75. return getNumChannels() == 1;
  76. }
  77. //=============================================================
  78. template <class T>
  79. bool AudioFile<T>::isStereo() const
  80. {
  81. return getNumChannels() == 2;
  82. }
  83. //=============================================================
  84. template <class T>
  85. int AudioFile<T>::getBitDepth() const
  86. {
  87. return bitDepth;
  88. }
  89. //=============================================================
  90. template <class T>
  91. int AudioFile<T>::getNumSamplesPerChannel() const
  92. {
  93. if (samples.size() > 0)
  94. return (int) samples[0].size();
  95. else
  96. return 0;
  97. }
  98. //=============================================================
  99. template <class T>
  100. double AudioFile<T>::getLengthInSeconds() const
  101. {
  102. return (double)getNumSamplesPerChannel() / (double)sampleRate;
  103. }
  104. //=============================================================
  105. template <class T>
  106. void AudioFile<T>::printSummary() const
  107. {
  108. std::cout << "|======================================|" << std::endl;
  109. std::cout << "Num Channels: " << getNumChannels() << std::endl;
  110. std::cout << "Num Samples Per Channel: " << getNumSamplesPerChannel() << std::endl;
  111. std::cout << "Sample Rate: " << sampleRate << std::endl;
  112. std::cout << "Bit Depth: " << bitDepth << std::endl;
  113. std::cout << "Length in Seconds: " << getLengthInSeconds() << std::endl;
  114. std::cout << "|======================================|" << std::endl;
  115. }
  116. //=============================================================
  117. template <class T>
  118. bool AudioFile<T>::setAudioBuffer (AudioBuffer& newBuffer)
  119. {
  120. int numChannels = (int)newBuffer.size();
  121. if (numChannels <= 0)
  122. {
  123. assert (false && "The buffer your are trying to use has no channels");
  124. return false;
  125. }
  126. int numSamples = (int)newBuffer[0].size();
  127. // set the number of channels
  128. samples.resize (newBuffer.size());
  129. for (int k = 0; k < getNumChannels(); k++)
  130. {
  131. assert (newBuffer[k].size() == numSamples);
  132. samples[k].resize (numSamples);
  133. for (int i = 0; i < numSamples; i++)
  134. {
  135. samples[k][i] = newBuffer[k][i];
  136. }
  137. }
  138. return true;
  139. }
  140. //=============================================================
  141. template <class T>
  142. void AudioFile<T>::setAudioBufferSize (int numChannels, int numSamples)
  143. {
  144. samples.resize (numChannels);
  145. setNumSamplesPerChannel (numSamples);
  146. }
  147. //=============================================================
  148. template <class T>
  149. void AudioFile<T>::setNumSamplesPerChannel (int numSamples)
  150. {
  151. int originalSize = getNumSamplesPerChannel();
  152. for (int i = 0; i < getNumChannels();i++)
  153. {
  154. samples[i].resize (numSamples);
  155. // set any new samples to zero
  156. if (numSamples > originalSize)
  157. std::fill (samples[i].begin() + originalSize, samples[i].end(), (T)0.);
  158. }
  159. }
  160. //=============================================================
  161. template <class T>
  162. void AudioFile<T>::setNumChannels (int numChannels)
  163. {
  164. int originalNumChannels = getNumChannels();
  165. int originalNumSamplesPerChannel = getNumSamplesPerChannel();
  166. samples.resize (numChannels);
  167. // make sure any new channels are set to the right size
  168. // and filled with zeros
  169. if (numChannels > originalNumChannels)
  170. {
  171. for (int i = originalNumChannels; i < numChannels; i++)
  172. {
  173. samples[i].resize (originalNumSamplesPerChannel);
  174. std::fill (samples[i].begin(), samples[i].end(), (T)0.);
  175. }
  176. }
  177. }
  178. //=============================================================
  179. template <class T>
  180. void AudioFile<T>::setBitDepth (int numBitsPerSample)
  181. {
  182. bitDepth = numBitsPerSample;
  183. }
  184. //=============================================================
  185. template <class T>
  186. void AudioFile<T>::setSampleRate (uint32_t newSampleRate)
  187. {
  188. sampleRate = newSampleRate;
  189. }
  190. //=============================================================
  191. template <class T>
  192. bool AudioFile<T>::load (std::string filePath)
  193. {
  194. std::ifstream file (filePath, std::ios::binary);
  195. // check the file exists
  196. if (! file.good())
  197. {
  198. std::cout << "ERROR: File doesn't exist or otherwise can't load file" << std::endl;
  199. std::cout << filePath << std::endl;
  200. return false;
  201. }
  202. file.unsetf (std::ios::skipws);
  203. std::istream_iterator<uint8_t> begin (file), end;
  204. std::vector<uint8_t> fileData (begin, end);
  205. // get audio file format
  206. audioFileFormat = determineAudioFileFormat (fileData);
  207. if (audioFileFormat == AudioFileFormat::Wave)
  208. {
  209. return decodeWaveFile (fileData);
  210. }
  211. else if (audioFileFormat == AudioFileFormat::Aiff)
  212. {
  213. return decodeAiffFile (fileData);
  214. }
  215. else
  216. {
  217. std::cout << "Audio File Type: " << "Error" << std::endl;
  218. return false;
  219. }
  220. }
  221. //=============================================================
  222. template <class T>
  223. bool AudioFile<T>::decodeWaveFile (std::vector<uint8_t>& fileData)
  224. {
  225. // -----------------------------------------------------------
  226. // HEADER CHUNK
  227. std::string headerChunkID (fileData.begin(), fileData.begin() + 4);
  228. //int32_t fileSizeInBytes = fourBytesToInt (fileData, 4) + 8;
  229. std::string format (fileData.begin() + 8, fileData.begin() + 12);
  230. // -----------------------------------------------------------
  231. // try and find the start points of key chunks
  232. int indexOfDataChunk = getIndexOfString (fileData, "data");
  233. int indexOfFormatChunk = getIndexOfString (fileData, "fmt");
  234. // if we can't find the data or format chunks, or the IDs/formats don't seem to be as expected
  235. // then it is unlikely we'll able to read this file, so abort
  236. if (indexOfDataChunk == -1 || indexOfFormatChunk == -1 || headerChunkID != "RIFF" || format != "WAVE")
  237. {
  238. std::cout << "ERROR: this doesn't seem to be a valid .WAV file" << std::endl;
  239. return false;
  240. }
  241. // -----------------------------------------------------------
  242. // FORMAT CHUNK
  243. int f = indexOfFormatChunk;
  244. std::string formatChunkID (fileData.begin() + f, fileData.begin() + f + 4);
  245. //int32_t formatChunkSize = fourBytesToInt (fileData, f + 4);
  246. int16_t audioFormat = twoBytesToInt (fileData, f + 8);
  247. int16_t numChannels = twoBytesToInt (fileData, f + 10);
  248. sampleRate = (uint32_t) fourBytesToInt (fileData, f + 12);
  249. int32_t numBytesPerSecond = fourBytesToInt (fileData, f + 16);
  250. int16_t numBytesPerBlock = twoBytesToInt (fileData, f + 20);
  251. bitDepth = (int) twoBytesToInt (fileData, f + 22);
  252. int numBytesPerSample = bitDepth / 8;
  253. // check that the audio format is PCM
  254. if (audioFormat != 1)
  255. {
  256. std::cout << "ERROR: this is a compressed .WAV file and this library does not support decoding them at present" << std::endl;
  257. return false;
  258. }
  259. // check the number of channels is mono or stereo
  260. if (numChannels < 1 ||numChannels > 2)
  261. {
  262. std::cout << "ERROR: this WAV file seems to be neither mono nor stereo (perhaps multi-track, or corrupted?)" << std::endl;
  263. return false;
  264. }
  265. // check header data is consistent
  266. if ((numBytesPerSecond != (numChannels * sampleRate * bitDepth) / 8) || (numBytesPerBlock != (numChannels * numBytesPerSample)))
  267. {
  268. std::cout << "ERROR: the header data in this WAV file seems to be inconsistent" << std::endl;
  269. return false;
  270. }
  271. // check bit depth is either 8, 16 or 24 bit
  272. if (bitDepth != 8 && bitDepth != 16 && bitDepth != 24)
  273. {
  274. std::cout << "ERROR: this file has a bit depth that is not 8, 16 or 24 bits" << std::endl;
  275. return false;
  276. }
  277. // -----------------------------------------------------------
  278. // DATA CHUNK
  279. int d = indexOfDataChunk;
  280. std::string dataChunkID (fileData.begin() + d, fileData.begin() + d + 4);
  281. int32_t dataChunkSize = fourBytesToInt (fileData, d + 4);
  282. int numSamples = dataChunkSize / (numChannels * bitDepth / 8);
  283. int samplesStartIndex = indexOfDataChunk + 8;
  284. clearAudioBuffer();
  285. samples.resize (numChannels);
  286. for (int i = 0; i < numSamples; i++)
  287. {
  288. for (int channel = 0; channel < numChannels; channel++)
  289. {
  290. int sampleIndex = samplesStartIndex + (numBytesPerBlock * i) + channel * numBytesPerSample;
  291. if (bitDepth == 8)
  292. {
  293. int32_t sampleAsInt = (int32_t) fileData[sampleIndex];
  294. T sample = (T)(sampleAsInt - 128) / (T)128.;
  295. samples[channel].push_back (sample);
  296. }
  297. else if (bitDepth == 16)
  298. {
  299. int16_t sampleAsInt = twoBytesToInt (fileData, sampleIndex);
  300. T sample = sixteenBitIntToSample (sampleAsInt);
  301. samples[channel].push_back (sample);
  302. }
  303. else if (bitDepth == 24)
  304. {
  305. int32_t sampleAsInt = 0;
  306. sampleAsInt = (fileData[sampleIndex + 2] << 16) | (fileData[sampleIndex + 1] << 8) | fileData[sampleIndex];
  307. if (sampleAsInt & 0x800000) // if the 24th bit is set, this is a negative number in 24-bit world
  308. sampleAsInt = sampleAsInt | ~0xFFFFFF; // so make sure sign is extended to the 32 bit float
  309. T sample = (T)sampleAsInt / (T)8388608.;
  310. samples[channel].push_back (sample);
  311. }
  312. else
  313. {
  314. assert (false);
  315. }
  316. }
  317. }
  318. return true;
  319. }
  320. //=============================================================
  321. template <class T>
  322. bool AudioFile<T>::decodeAiffFile (std::vector<uint8_t>& fileData)
  323. {
  324. // -----------------------------------------------------------
  325. // HEADER CHUNK
  326. std::string headerChunkID (fileData.begin(), fileData.begin() + 4);
  327. //int32_t fileSizeInBytes = fourBytesToInt (fileData, 4, Endianness::BigEndian) + 8;
  328. std::string format (fileData.begin() + 8, fileData.begin() + 12);
  329. // -----------------------------------------------------------
  330. // try and find the start points of key chunks
  331. int indexOfCommChunk = getIndexOfString (fileData, "COMM");
  332. int indexOfSoundDataChunk = getIndexOfString (fileData, "SSND");
  333. // if we can't find the data or format chunks, or the IDs/formats don't seem to be as expected
  334. // then it is unlikely we'll able to read this file, so abort
  335. if (indexOfSoundDataChunk == -1 || indexOfCommChunk == -1 || headerChunkID != "FORM" || format != "AIFF")
  336. {
  337. std::cout << "ERROR: this doesn't seem to be a valid AIFF file" << std::endl;
  338. return false;
  339. }
  340. // -----------------------------------------------------------
  341. // COMM CHUNK
  342. int p = indexOfCommChunk;
  343. std::string commChunkID (fileData.begin() + p, fileData.begin() + p + 4);
  344. //int32_t commChunkSize = fourBytesToInt (fileData, p + 4, Endianness::BigEndian);
  345. int16_t numChannels = twoBytesToInt (fileData, p + 8, Endianness::BigEndian);
  346. int32_t numSamplesPerChannel = fourBytesToInt (fileData, p + 10, Endianness::BigEndian);
  347. bitDepth = (int) twoBytesToInt (fileData, p + 14, Endianness::BigEndian);
  348. sampleRate = getAiffSampleRate (fileData, p + 16);
  349. // check the sample rate was properly decoded
  350. if (sampleRate == -1)
  351. {
  352. std::cout << "ERROR: this AIFF file has an unsupported sample rate" << std::endl;
  353. return false;
  354. }
  355. // check the number of channels is mono or stereo
  356. if (numChannels < 1 ||numChannels > 2)
  357. {
  358. std::cout << "ERROR: this AIFF file seems to be neither mono nor stereo (perhaps multi-track, or corrupted?)" << std::endl;
  359. return false;
  360. }
  361. // check bit depth is either 8, 16 or 24 bit
  362. if (bitDepth != 8 && bitDepth != 16 && bitDepth != 24)
  363. {
  364. std::cout << "ERROR: this file has a bit depth that is not 8, 16 or 24 bits" << std::endl;
  365. return false;
  366. }
  367. // -----------------------------------------------------------
  368. // SSND CHUNK
  369. int s = indexOfSoundDataChunk;
  370. std::string soundDataChunkID (fileData.begin() + s, fileData.begin() + s + 4);
  371. int32_t soundDataChunkSize = fourBytesToInt (fileData, s + 4, Endianness::BigEndian);
  372. int32_t offset = fourBytesToInt (fileData, s + 8, Endianness::BigEndian);
  373. //int32_t blockSize = fourBytesToInt (fileData, s + 12, Endianness::BigEndian);
  374. int numBytesPerSample = bitDepth / 8;
  375. int numBytesPerFrame = numBytesPerSample * numChannels;
  376. int totalNumAudioSampleBytes = numSamplesPerChannel * numBytesPerFrame;
  377. int samplesStartIndex = s + 16 + (int)offset;
  378. // sanity check the data
  379. if ((soundDataChunkSize - 8) != totalNumAudioSampleBytes || totalNumAudioSampleBytes > (fileData.size() - samplesStartIndex))
  380. {
  381. std::cout << "ERROR: the metadatafor this file doesn't seem right" << std::endl;
  382. return false;
  383. }
  384. clearAudioBuffer();
  385. samples.resize (numChannels);
  386. for (int i = 0; i < numSamplesPerChannel; i++)
  387. {
  388. for (int channel = 0; channel < numChannels; channel++)
  389. {
  390. int sampleIndex = samplesStartIndex + (numBytesPerFrame * i) + channel * numBytesPerSample;
  391. if (bitDepth == 8)
  392. {
  393. int8_t sampleAsSigned8Bit = (int8_t)fileData[sampleIndex];
  394. T sample = (T)sampleAsSigned8Bit / (T)128.;
  395. samples[channel].push_back (sample);
  396. }
  397. else if (bitDepth == 16)
  398. {
  399. int16_t sampleAsInt = twoBytesToInt (fileData, sampleIndex, Endianness::BigEndian);
  400. T sample = sixteenBitIntToSample (sampleAsInt);
  401. samples[channel].push_back (sample);
  402. }
  403. else if (bitDepth == 24)
  404. {
  405. int32_t sampleAsInt = 0;
  406. sampleAsInt = (fileData[sampleIndex] << 16) | (fileData[sampleIndex + 1] << 8) | fileData[sampleIndex + 2];
  407. if (sampleAsInt & 0x800000) // if the 24th bit is set, this is a negative number in 24-bit world
  408. sampleAsInt = sampleAsInt | ~0xFFFFFF; // so make sure sign is extended to the 32 bit float
  409. T sample = (T)sampleAsInt / (T)8388608.;
  410. samples[channel].push_back (sample);
  411. }
  412. else
  413. {
  414. assert (false);
  415. }
  416. }
  417. }
  418. return true;
  419. }
  420. //=============================================================
  421. template <class T>
  422. uint32_t AudioFile<T>::getAiffSampleRate (std::vector<uint8_t>& fileData, int sampleRateStartIndex)
  423. {
  424. for (auto it : aiffSampleRateTable)
  425. {
  426. if (tenByteMatch (fileData, sampleRateStartIndex, it.second, 0))
  427. return it.first;
  428. }
  429. return -1;
  430. }
  431. //=============================================================
  432. template <class T>
  433. bool AudioFile<T>::tenByteMatch (std::vector<uint8_t>& v1, int startIndex1, std::vector<uint8_t>& v2, int startIndex2)
  434. {
  435. for (int i = 0; i < 10; i++)
  436. {
  437. if (v1[startIndex1 + i] != v2[startIndex2 + i])
  438. return false;
  439. }
  440. return true;
  441. }
  442. //=============================================================
  443. template <class T>
  444. void AudioFile<T>::addSampleRateToAiffData (std::vector<uint8_t>& fileData, uint32_t sampleRate)
  445. {
  446. if (aiffSampleRateTable.count (sampleRate) > 0)
  447. {
  448. for (int i = 0; i < 10; i++)
  449. fileData.push_back (aiffSampleRateTable[sampleRate][i]);
  450. }
  451. }
  452. //=============================================================
  453. template <class T>
  454. bool AudioFile<T>::save (std::string filePath, AudioFileFormat format)
  455. {
  456. if (format == AudioFileFormat::Wave)
  457. {
  458. return saveToWaveFile (filePath);
  459. }
  460. else if (format == AudioFileFormat::Aiff)
  461. {
  462. return saveToAiffFile (filePath);
  463. }
  464. return false;
  465. }
  466. //=============================================================
  467. template <class T>
  468. bool AudioFile<T>::saveToWaveFile (std::string filePath)
  469. {
  470. std::vector<uint8_t> fileData;
  471. int32_t dataChunkSize = getNumSamplesPerChannel() * (getNumChannels() * bitDepth / 8);
  472. // -----------------------------------------------------------
  473. // HEADER CHUNK
  474. addStringToFileData (fileData, "RIFF");
  475. // The file size in bytes is the header chunk size (4, not counting RIFF and WAVE) + the format
  476. // chunk size (24) + the metadata part of the data chunk plus the actual data chunk size
  477. int32_t fileSizeInBytes = 4 + 24 + 8 + dataChunkSize;
  478. addInt32ToFileData (fileData, fileSizeInBytes);
  479. addStringToFileData (fileData, "WAVE");
  480. // -----------------------------------------------------------
  481. // FORMAT CHUNK
  482. addStringToFileData (fileData, "fmt ");
  483. addInt32ToFileData (fileData, 16); // format chunk size (16 for PCM)
  484. addInt16ToFileData (fileData, 1); // audio format = 1
  485. addInt16ToFileData (fileData, (int16_t)getNumChannels()); // num channels
  486. addInt32ToFileData (fileData, (int32_t)sampleRate); // sample rate
  487. int32_t numBytesPerSecond = (int32_t) ((getNumChannels() * sampleRate * bitDepth) / 8);
  488. addInt32ToFileData (fileData, numBytesPerSecond);
  489. int16_t numBytesPerBlock = getNumChannels() * (bitDepth / 8);
  490. addInt16ToFileData (fileData, numBytesPerBlock);
  491. addInt16ToFileData (fileData, (int16_t)bitDepth);
  492. // -----------------------------------------------------------
  493. // DATA CHUNK
  494. addStringToFileData (fileData, "data");
  495. addInt32ToFileData (fileData, dataChunkSize);
  496. for (int i = 0; i < getNumSamplesPerChannel(); i++)
  497. {
  498. for (int channel = 0; channel < getNumChannels(); channel++)
  499. {
  500. if (bitDepth == 8)
  501. {
  502. int32_t sampleAsInt = ((samples[channel][i] * (T)128.) + 128.);
  503. uint8_t byte = (uint8_t)sampleAsInt;
  504. fileData.push_back (byte);
  505. }
  506. else if (bitDepth == 16)
  507. {
  508. int16_t sampleAsInt = (int16_t) (samples[channel][i] * (T)32768.);
  509. addInt16ToFileData (fileData, sampleAsInt);
  510. }
  511. else if (bitDepth == 24)
  512. {
  513. int32_t sampleAsIntAgain = (int32_t) (samples[channel][i] * (T)8388608.);
  514. uint8_t bytes[3];
  515. bytes[2] = (uint8_t) (sampleAsIntAgain >> 16) & 0xFF;
  516. bytes[1] = (uint8_t) (sampleAsIntAgain >> 8) & 0xFF;
  517. bytes[0] = (uint8_t) sampleAsIntAgain & 0xFF;
  518. fileData.push_back (bytes[0]);
  519. fileData.push_back (bytes[1]);
  520. fileData.push_back (bytes[2]);
  521. }
  522. else
  523. {
  524. assert (false && "Trying to write a file with unsupported bit depth");
  525. return false;
  526. }
  527. }
  528. }
  529. // check that the various sizes we put in the metadata are correct
  530. if (fileSizeInBytes != (fileData.size() - 8) || dataChunkSize != (getNumSamplesPerChannel() * getNumChannels() * (bitDepth / 8)))
  531. {
  532. std::cout << "ERROR: couldn't save file to " << filePath << std::endl;
  533. return false;
  534. }
  535. // try to write the file
  536. return writeDataToFile (fileData, filePath);
  537. }
  538. //=============================================================
  539. template <class T>
  540. bool AudioFile<T>::saveToAiffFile (std::string filePath)
  541. {
  542. std::vector<uint8_t> fileData;
  543. int32_t numBytesPerSample = bitDepth / 8;
  544. int32_t numBytesPerFrame = numBytesPerSample * getNumChannels();
  545. int32_t totalNumAudioSampleBytes = getNumSamplesPerChannel() * numBytesPerFrame;
  546. int32_t soundDataChunkSize = totalNumAudioSampleBytes + 8;
  547. // -----------------------------------------------------------
  548. // HEADER CHUNK
  549. addStringToFileData (fileData, "FORM");
  550. // The file size in bytes is the header chunk size (4, not counting FORM and AIFF) + the COMM
  551. // chunk size (26) + the metadata part of the SSND chunk plus the actual data chunk size
  552. int32_t fileSizeInBytes = 4 + 26 + 16 + totalNumAudioSampleBytes;
  553. addInt32ToFileData (fileData, fileSizeInBytes, Endianness::BigEndian);
  554. addStringToFileData (fileData, "AIFF");
  555. // -----------------------------------------------------------
  556. // COMM CHUNK
  557. addStringToFileData (fileData, "COMM");
  558. addInt32ToFileData (fileData, 18, Endianness::BigEndian); // commChunkSize
  559. addInt16ToFileData (fileData, getNumChannels(), Endianness::BigEndian); // num channels
  560. addInt32ToFileData (fileData, getNumSamplesPerChannel(), Endianness::BigEndian); // num samples per channel
  561. addInt16ToFileData (fileData, bitDepth, Endianness::BigEndian); // bit depth
  562. addSampleRateToAiffData (fileData, sampleRate);
  563. // -----------------------------------------------------------
  564. // SSND CHUNK
  565. addStringToFileData (fileData, "SSND");
  566. addInt32ToFileData (fileData, soundDataChunkSize, Endianness::BigEndian);
  567. addInt32ToFileData (fileData, 0, Endianness::BigEndian); // offset
  568. addInt32ToFileData (fileData, 0, Endianness::BigEndian); // block size
  569. for (int i = 0; i < getNumSamplesPerChannel(); i++)
  570. {
  571. for (int channel = 0; channel < getNumChannels(); channel++)
  572. {
  573. if (bitDepth == 8)
  574. {
  575. int32_t sampleAsInt = (int32_t)(samples[channel][i] * (T)128.);
  576. uint8_t byte = (uint8_t)sampleAsInt;
  577. fileData.push_back (byte);
  578. }
  579. else if (bitDepth == 16)
  580. {
  581. int16_t sampleAsInt = (int16_t) (samples[channel][i] * (T)32768.);
  582. addInt16ToFileData (fileData, sampleAsInt, Endianness::BigEndian);
  583. }
  584. else if (bitDepth == 24)
  585. {
  586. int32_t sampleAsIntAgain = (int32_t) (samples[channel][i] * (T)8388608.);
  587. uint8_t bytes[3];
  588. bytes[0] = (uint8_t) (sampleAsIntAgain >> 16) & 0xFF;
  589. bytes[1] = (uint8_t) (sampleAsIntAgain >> 8) & 0xFF;
  590. bytes[2] = (uint8_t) sampleAsIntAgain & 0xFF;
  591. fileData.push_back (bytes[0]);
  592. fileData.push_back (bytes[1]);
  593. fileData.push_back (bytes[2]);
  594. }
  595. else
  596. {
  597. assert (false && "Trying to write a file with unsupported bit depth");
  598. return false;
  599. }
  600. }
  601. }
  602. // check that the various sizes we put in the metadata are correct
  603. if (fileSizeInBytes != (fileData.size() - 8) || soundDataChunkSize != getNumSamplesPerChannel() * numBytesPerFrame + 8)
  604. {
  605. std::cout << "ERROR: couldn't save file to " << filePath << std::endl;
  606. return false;
  607. }
  608. // try to write the file
  609. return writeDataToFile (fileData, filePath);
  610. }
  611. //=============================================================
  612. template <class T>
  613. bool AudioFile<T>::writeDataToFile (std::vector<uint8_t>& fileData, std::string filePath)
  614. {
  615. std::ofstream outputFile (filePath, std::ios::binary);
  616. if (outputFile.is_open())
  617. {
  618. for (int i = 0; i < fileData.size(); i++)
  619. {
  620. char value = (char) fileData[i];
  621. outputFile.write (&value, sizeof (char));
  622. }
  623. outputFile.close();
  624. return true;
  625. }
  626. return false;
  627. }
  628. //=============================================================
  629. template <class T>
  630. void AudioFile<T>::addStringToFileData (std::vector<uint8_t>& fileData, std::string s)
  631. {
  632. for (int i = 0; i < s.length();i++)
  633. fileData.push_back ((uint8_t) s[i]);
  634. }
  635. //=============================================================
  636. template <class T>
  637. void AudioFile<T>::addInt32ToFileData (std::vector<uint8_t>& fileData, int32_t i, Endianness endianness)
  638. {
  639. uint8_t bytes[4];
  640. if (endianness == Endianness::LittleEndian)
  641. {
  642. bytes[3] = (i >> 24) & 0xFF;
  643. bytes[2] = (i >> 16) & 0xFF;
  644. bytes[1] = (i >> 8) & 0xFF;
  645. bytes[0] = i & 0xFF;
  646. }
  647. else
  648. {
  649. bytes[0] = (i >> 24) & 0xFF;
  650. bytes[1] = (i >> 16) & 0xFF;
  651. bytes[2] = (i >> 8) & 0xFF;
  652. bytes[3] = i & 0xFF;
  653. }
  654. for (int i = 0; i < 4; i++)
  655. fileData.push_back (bytes[i]);
  656. }
  657. //=============================================================
  658. template <class T>
  659. void AudioFile<T>::addInt16ToFileData (std::vector<uint8_t>& fileData, int16_t i, Endianness endianness)
  660. {
  661. uint8_t bytes[2];
  662. if (endianness == Endianness::LittleEndian)
  663. {
  664. bytes[1] = (i >> 8) & 0xFF;
  665. bytes[0] = i & 0xFF;
  666. }
  667. else
  668. {
  669. bytes[0] = (i >> 8) & 0xFF;
  670. bytes[1] = i & 0xFF;
  671. }
  672. fileData.push_back (bytes[0]);
  673. fileData.push_back (bytes[1]);
  674. }
  675. //=============================================================
  676. template <class T>
  677. void AudioFile<T>::clearAudioBuffer()
  678. {
  679. for (int i = 0; i < samples.size();i++)
  680. {
  681. samples[i].clear();
  682. }
  683. samples.clear();
  684. }
  685. //=============================================================
  686. template <class T>
  687. AudioFileFormat AudioFile<T>::determineAudioFileFormat (std::vector<uint8_t>& fileData)
  688. {
  689. std::string header (fileData.begin(), fileData.begin() + 4);
  690. if (header == "RIFF")
  691. return AudioFileFormat::Wave;
  692. else if (header == "FORM")
  693. return AudioFileFormat::Aiff;
  694. else
  695. return AudioFileFormat::Error;
  696. }
  697. //=============================================================
  698. template <class T>
  699. int32_t AudioFile<T>::fourBytesToInt (std::vector<uint8_t>& source, int startIndex, Endianness endianness)
  700. {
  701. int32_t result;
  702. if (endianness == Endianness::LittleEndian)
  703. result = (source[startIndex + 3] << 24) | (source[startIndex + 2] << 16) | (source[startIndex + 1] << 8) | source[startIndex];
  704. else
  705. result = (source[startIndex] << 24) | (source[startIndex + 1] << 16) | (source[startIndex + 2] << 8) | source[startIndex + 3];
  706. return result;
  707. }
  708. //=============================================================
  709. template <class T>
  710. int16_t AudioFile<T>::twoBytesToInt (std::vector<uint8_t>& source, int startIndex, Endianness endianness)
  711. {
  712. int16_t result;
  713. if (endianness == Endianness::LittleEndian)
  714. result = (source[startIndex + 1] << 8) | source[startIndex];
  715. else
  716. result = (source[startIndex] << 8) | source[startIndex + 1];
  717. return result;
  718. }
  719. //=============================================================
  720. template <class T>
  721. int AudioFile<T>::getIndexOfString (std::vector<uint8_t>& source, std::string stringToSearchFor)
  722. {
  723. int index = -1;
  724. int stringLength = (int)stringToSearchFor.length();
  725. for (int i = 0; i < source.size() - stringLength;i++)
  726. {
  727. std::string section (source.begin() + i, source.begin() + i + stringLength);
  728. if (section == stringToSearchFor)
  729. {
  730. index = i;
  731. break;
  732. }
  733. }
  734. return index;
  735. }
  736. //=============================================================
  737. template <class T>
  738. T AudioFile<T>::sixteenBitIntToSample (int16_t sample)
  739. {
  740. return (T)sample / (T)32768.;
  741. }
  742. //===========================================================
  743. template class AudioFile<float>;
  744. template class AudioFile<double>;