b3WriteWavFile.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. // b3WriteWavFile is copied from Stk::FileWvOut/FileWrite
  2. // See also https://github.com/thestk/stk
  3. // by Perry R. Cook and Gary P. Scavone, 1995--2014.
  4. #include "b3WriteWavFile.h"
  5. #include "Bullet3Common/b3AlignedObjectArray.h"
  6. #include "b3SwapUtils.h"
  7. #define B3_FLOAT32 32
  8. #define B3_FLOAT64 64
  9. // WAV header structure. See
  10. // http://www-mmsp.ece.mcgill.ca/documents/audioformats/WAVE/Docs/rfc2361.txt
  11. // for information regarding format codes.
  12. struct b3WaveHeader
  13. {
  14. char riff[4]; // "RIFF"
  15. int fileSize; // in bytes
  16. char wave[4]; // "WAVE"
  17. char fmt[4]; // "fmt "
  18. int chunkSize; // in bytes (16 for PCM)
  19. union {
  20. signed short formatCode; // 1=PCM, 2=ADPCM, 3=IEEE float, 6=A-Law, 7=Mu-Law
  21. unsigned short uformatCode;
  22. };
  23. signed short nChannels; // 1=mono, 2=stereo
  24. int sampleRate;
  25. int bytesPerSecond;
  26. signed short bytesPerSample; // 2=16-bit mono, 4=16-bit stereo
  27. signed short bitsPerSample;
  28. signed short cbSize; // size of extension
  29. signed short validBits; // valid bits per sample
  30. int channelMask; // speaker position mask
  31. char subformat[16]; // format code and GUID
  32. char fact[4]; // "fact"
  33. int factSize; // fact chunk size
  34. int frames; // sample frames
  35. };
  36. struct b3WriteWavFileInternalData
  37. {
  38. FILE *m_file;
  39. int m_numChannels;
  40. int m_sampleRate;
  41. int m_dataType; // single precision 32bit float, 64bit double
  42. bool m_byteswap;
  43. int m_frameCounter;
  44. int m_bufferIndex;
  45. int m_bufferSize;
  46. bool m_clipped;
  47. bool m_isMachineLittleEndian;
  48. b3AlignedObjectArray<float> m_floatBuffer;
  49. b3AlignedObjectArray<double> m_doubleBuffer;
  50. b3WriteWavFileInternalData()
  51. : m_file(0),
  52. m_numChannels(0),
  53. m_dataType(B3_FLOAT32),
  54. m_byteswap(false),
  55. m_frameCounter(0),
  56. m_bufferIndex(0),
  57. m_bufferSize(1024),
  58. m_clipped(false)
  59. {
  60. m_floatBuffer.reserve(m_bufferSize);
  61. m_doubleBuffer.reserve(m_bufferSize);
  62. m_isMachineLittleEndian = b3MachineIsLittleEndian();
  63. }
  64. };
  65. b3WriteWavFile::b3WriteWavFile()
  66. {
  67. m_data = new b3WriteWavFileInternalData();
  68. }
  69. b3WriteWavFile::~b3WriteWavFile()
  70. {
  71. closeWavFile();
  72. delete m_data;
  73. }
  74. bool b3WriteWavFile::setWavFile(std::string fileName, int sampleRate, int numChannels, bool useDoublePrecision)
  75. {
  76. m_data->m_numChannels = numChannels;
  77. m_data->m_sampleRate = sampleRate;
  78. if (useDoublePrecision)
  79. {
  80. m_data->m_dataType = B3_FLOAT64;
  81. }
  82. else
  83. {
  84. m_data->m_dataType = B3_FLOAT32;
  85. }
  86. if (fileName.find(".wav") == std::string::npos)
  87. fileName += ".wav";
  88. m_data->m_file = fopen(fileName.c_str(), "wb");
  89. if (!m_data->m_file)
  90. {
  91. return false;
  92. }
  93. struct b3WaveHeader hdr = {{'R', 'I', 'F', 'F'}, 44, {'W', 'A', 'V', 'E'}, {'f', 'm', 't', ' '}, 16, 1, 1, sampleRate, 0, 2, 16, 0, 0, 0, {'\x01', '\x00', '\x00', '\x00', '\x00', '\x00', '\x10', '\x00', '\x80', '\x00', '\x00', '\xAA', '\x00', '\x38', '\x9B', '\x71'}, {'f', 'a', 'c', 't'}, 4, 0};
  94. hdr.nChannels = (signed short)m_data->m_numChannels;
  95. if (m_data->m_dataType == B3_FLOAT32)
  96. {
  97. hdr.formatCode = 3;
  98. hdr.bitsPerSample = 32;
  99. }
  100. else if (m_data->m_dataType == B3_FLOAT64)
  101. {
  102. hdr.formatCode = 3;
  103. hdr.bitsPerSample = 64;
  104. }
  105. hdr.bytesPerSample = (signed short)(m_data->m_numChannels * hdr.bitsPerSample / 8);
  106. hdr.bytesPerSecond = (int)(hdr.sampleRate * hdr.bytesPerSample);
  107. unsigned int bytesToWrite = 36;
  108. if (m_data->m_numChannels > 2 || hdr.bitsPerSample > 16)
  109. { // use extensible format
  110. bytesToWrite = 72;
  111. hdr.chunkSize += 24;
  112. hdr.uformatCode = 0xFFFE;
  113. hdr.cbSize = 22;
  114. hdr.validBits = hdr.bitsPerSample;
  115. signed short *subFormat = (signed short *)&hdr.subformat[0];
  116. if (m_data->m_dataType == B3_FLOAT32 || m_data->m_dataType == B3_FLOAT64)
  117. *subFormat = 3;
  118. else
  119. *subFormat = 1;
  120. }
  121. m_data->m_byteswap = false;
  122. if (!m_data->m_isMachineLittleEndian)
  123. {
  124. m_data->m_byteswap = true;
  125. b3Swap32((unsigned char *)&hdr.chunkSize);
  126. b3Swap16((unsigned char *)&hdr.formatCode);
  127. b3Swap16((unsigned char *)&hdr.nChannels);
  128. b3Swap32((unsigned char *)&hdr.sampleRate);
  129. b3Swap32((unsigned char *)&hdr.bytesPerSecond);
  130. b3Swap16((unsigned char *)&hdr.bytesPerSample);
  131. b3Swap16((unsigned char *)&hdr.bitsPerSample);
  132. b3Swap16((unsigned char *)&hdr.cbSize);
  133. b3Swap16((unsigned char *)&hdr.validBits);
  134. b3Swap16((unsigned char *)&hdr.subformat[0]);
  135. b3Swap32((unsigned char *)&hdr.factSize);
  136. }
  137. char data[4] = {'d', 'a', 't', 'a'};
  138. int dataSize = 0;
  139. if (fwrite(&hdr, 1, bytesToWrite, m_data->m_file) != bytesToWrite)
  140. return false;
  141. if (fwrite(&data, 4, 1, m_data->m_file) != 1)
  142. return false;
  143. if (fwrite(&dataSize, 4, 1, m_data->m_file) != 1)
  144. return false;
  145. return true;
  146. }
  147. void b3WriteWavFile::closeWavFile()
  148. {
  149. if (m_data->m_file == 0)
  150. return;
  151. flushData(1);
  152. int bytesPerSample = 1;
  153. if (m_data->m_dataType == B3_FLOAT32)
  154. bytesPerSample = 4;
  155. else if (m_data->m_dataType == B3_FLOAT64)
  156. bytesPerSample = 8;
  157. bool useExtensible = false;
  158. int dataLocation = 40;
  159. if (bytesPerSample > 2 || m_data->m_numChannels > 2)
  160. {
  161. useExtensible = true;
  162. dataLocation = 76;
  163. }
  164. int bytes = (int)(m_data->m_frameCounter * m_data->m_numChannels * bytesPerSample);
  165. if (bytes % 2)
  166. { // pad extra byte if odd
  167. signed char sample = 0;
  168. fwrite(&sample, 1, 1, m_data->m_file);
  169. }
  170. #ifndef __LITTLE_ENDIAN__
  171. b3Swap32((unsigned char *)&bytes);
  172. #endif
  173. fseek(m_data->m_file, dataLocation, SEEK_SET); // jump to data length
  174. fwrite(&bytes, 4, 1, m_data->m_file);
  175. bytes = (int)(m_data->m_frameCounter * m_data->m_numChannels * bytesPerSample + 44);
  176. if (useExtensible) bytes += 36;
  177. #ifndef __LITTLE_ENDIAN__
  178. b3Swap32((unsigned char *)&bytes);
  179. #endif
  180. fseek(m_data->m_file, 4, SEEK_SET); // jump to file size
  181. fwrite(&bytes, 4, 1, m_data->m_file);
  182. if (useExtensible)
  183. { // fill in the "fact" chunk frames value
  184. bytes = (int)m_data->m_frameCounter;
  185. #ifndef __LITTLE_ENDIAN__
  186. b3Swap32((unsigned char *)&bytes);
  187. #endif
  188. fseek(m_data->m_file, 68, SEEK_SET);
  189. fwrite(&bytes, 4, 1, m_data->m_file);
  190. }
  191. fclose(m_data->m_file);
  192. m_data->m_file = 0;
  193. }
  194. void b3WriteWavFile::tick(double *frames, int numFrames)
  195. {
  196. int iFrames = 0;
  197. int j, nChannels = m_data->m_numChannels;
  198. for (int i = 0; i < numFrames; i++)
  199. {
  200. for (j = 0; j < nChannels; j++)
  201. {
  202. double sample = frames[iFrames++];
  203. if (sample < -1.)
  204. {
  205. sample = -1.;
  206. m_data->m_clipped = true;
  207. }
  208. if (sample > 1)
  209. {
  210. sample = 1.;
  211. m_data->m_clipped = true;
  212. }
  213. if (m_data->m_dataType == B3_FLOAT32)
  214. {
  215. m_data->m_floatBuffer.push_back((float)sample);
  216. }
  217. else
  218. {
  219. m_data->m_doubleBuffer.push_back(sample);
  220. }
  221. flushData(m_data->m_bufferSize);
  222. }
  223. m_data->m_frameCounter++;
  224. }
  225. }
  226. void b3WriteWavFile::flushData(int bufferSize)
  227. {
  228. if (m_data->m_dataType == B3_FLOAT32)
  229. {
  230. if (m_data->m_floatBuffer.size() >= bufferSize)
  231. {
  232. fwrite(&m_data->m_floatBuffer[0], sizeof(float), m_data->m_floatBuffer.size(), m_data->m_file);
  233. m_data->m_floatBuffer.resize(0);
  234. }
  235. }
  236. else
  237. {
  238. if (m_data->m_doubleBuffer.size() >= bufferSize)
  239. {
  240. fwrite(&m_data->m_doubleBuffer[0], sizeof(double), m_data->m_doubleBuffer.size(), m_data->m_file);
  241. m_data->m_doubleBuffer.resize(0);
  242. }
  243. }
  244. }