| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- // b3WriteWavFile is copied from Stk::FileWvOut/FileWrite
- // See also https://github.com/thestk/stk
- // by Perry R. Cook and Gary P. Scavone, 1995--2014.
- #include "b3WriteWavFile.h"
- #include "Bullet3Common/b3AlignedObjectArray.h"
- #include "b3SwapUtils.h"
- #define B3_FLOAT32 32
- #define B3_FLOAT64 64
- // WAV header structure. See
- // http://www-mmsp.ece.mcgill.ca/documents/audioformats/WAVE/Docs/rfc2361.txt
- // for information regarding format codes.
- struct b3WaveHeader
- {
- char riff[4]; // "RIFF"
- int fileSize; // in bytes
- char wave[4]; // "WAVE"
- char fmt[4]; // "fmt "
- int chunkSize; // in bytes (16 for PCM)
- union {
- signed short formatCode; // 1=PCM, 2=ADPCM, 3=IEEE float, 6=A-Law, 7=Mu-Law
- unsigned short uformatCode;
- };
- signed short nChannels; // 1=mono, 2=stereo
- int sampleRate;
- int bytesPerSecond;
- signed short bytesPerSample; // 2=16-bit mono, 4=16-bit stereo
- signed short bitsPerSample;
- signed short cbSize; // size of extension
- signed short validBits; // valid bits per sample
- int channelMask; // speaker position mask
- char subformat[16]; // format code and GUID
- char fact[4]; // "fact"
- int factSize; // fact chunk size
- int frames; // sample frames
- };
- struct b3WriteWavFileInternalData
- {
- FILE *m_file;
- int m_numChannels;
- int m_sampleRate;
- int m_dataType; // single precision 32bit float, 64bit double
- bool m_byteswap;
- int m_frameCounter;
- int m_bufferIndex;
- int m_bufferSize;
- bool m_clipped;
- bool m_isMachineLittleEndian;
- b3AlignedObjectArray<float> m_floatBuffer;
- b3AlignedObjectArray<double> m_doubleBuffer;
- b3WriteWavFileInternalData()
- : m_file(0),
- m_numChannels(0),
- m_dataType(B3_FLOAT32),
- m_byteswap(false),
- m_frameCounter(0),
- m_bufferIndex(0),
- m_bufferSize(1024),
- m_clipped(false)
- {
- m_floatBuffer.reserve(m_bufferSize);
- m_doubleBuffer.reserve(m_bufferSize);
- m_isMachineLittleEndian = b3MachineIsLittleEndian();
- }
- };
- b3WriteWavFile::b3WriteWavFile()
- {
- m_data = new b3WriteWavFileInternalData();
- }
- b3WriteWavFile::~b3WriteWavFile()
- {
- closeWavFile();
- delete m_data;
- }
- bool b3WriteWavFile::setWavFile(std::string fileName, int sampleRate, int numChannels, bool useDoublePrecision)
- {
- m_data->m_numChannels = numChannels;
- m_data->m_sampleRate = sampleRate;
- if (useDoublePrecision)
- {
- m_data->m_dataType = B3_FLOAT64;
- }
- else
- {
- m_data->m_dataType = B3_FLOAT32;
- }
- if (fileName.find(".wav") == std::string::npos)
- fileName += ".wav";
- m_data->m_file = fopen(fileName.c_str(), "wb");
- if (!m_data->m_file)
- {
- return false;
- }
- 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};
- hdr.nChannels = (signed short)m_data->m_numChannels;
- if (m_data->m_dataType == B3_FLOAT32)
- {
- hdr.formatCode = 3;
- hdr.bitsPerSample = 32;
- }
- else if (m_data->m_dataType == B3_FLOAT64)
- {
- hdr.formatCode = 3;
- hdr.bitsPerSample = 64;
- }
- hdr.bytesPerSample = (signed short)(m_data->m_numChannels * hdr.bitsPerSample / 8);
- hdr.bytesPerSecond = (int)(hdr.sampleRate * hdr.bytesPerSample);
- unsigned int bytesToWrite = 36;
- if (m_data->m_numChannels > 2 || hdr.bitsPerSample > 16)
- { // use extensible format
- bytesToWrite = 72;
- hdr.chunkSize += 24;
- hdr.uformatCode = 0xFFFE;
- hdr.cbSize = 22;
- hdr.validBits = hdr.bitsPerSample;
- signed short *subFormat = (signed short *)&hdr.subformat[0];
- if (m_data->m_dataType == B3_FLOAT32 || m_data->m_dataType == B3_FLOAT64)
- *subFormat = 3;
- else
- *subFormat = 1;
- }
- m_data->m_byteswap = false;
- if (!m_data->m_isMachineLittleEndian)
- {
- m_data->m_byteswap = true;
- b3Swap32((unsigned char *)&hdr.chunkSize);
- b3Swap16((unsigned char *)&hdr.formatCode);
- b3Swap16((unsigned char *)&hdr.nChannels);
- b3Swap32((unsigned char *)&hdr.sampleRate);
- b3Swap32((unsigned char *)&hdr.bytesPerSecond);
- b3Swap16((unsigned char *)&hdr.bytesPerSample);
- b3Swap16((unsigned char *)&hdr.bitsPerSample);
- b3Swap16((unsigned char *)&hdr.cbSize);
- b3Swap16((unsigned char *)&hdr.validBits);
- b3Swap16((unsigned char *)&hdr.subformat[0]);
- b3Swap32((unsigned char *)&hdr.factSize);
- }
- char data[4] = {'d', 'a', 't', 'a'};
- int dataSize = 0;
- if (fwrite(&hdr, 1, bytesToWrite, m_data->m_file) != bytesToWrite)
- return false;
- if (fwrite(&data, 4, 1, m_data->m_file) != 1)
- return false;
- if (fwrite(&dataSize, 4, 1, m_data->m_file) != 1)
- return false;
- return true;
- }
- void b3WriteWavFile::closeWavFile()
- {
- if (m_data->m_file == 0)
- return;
- flushData(1);
- int bytesPerSample = 1;
- if (m_data->m_dataType == B3_FLOAT32)
- bytesPerSample = 4;
- else if (m_data->m_dataType == B3_FLOAT64)
- bytesPerSample = 8;
- bool useExtensible = false;
- int dataLocation = 40;
- if (bytesPerSample > 2 || m_data->m_numChannels > 2)
- {
- useExtensible = true;
- dataLocation = 76;
- }
- int bytes = (int)(m_data->m_frameCounter * m_data->m_numChannels * bytesPerSample);
- if (bytes % 2)
- { // pad extra byte if odd
- signed char sample = 0;
- fwrite(&sample, 1, 1, m_data->m_file);
- }
- #ifndef __LITTLE_ENDIAN__
- b3Swap32((unsigned char *)&bytes);
- #endif
- fseek(m_data->m_file, dataLocation, SEEK_SET); // jump to data length
- fwrite(&bytes, 4, 1, m_data->m_file);
- bytes = (int)(m_data->m_frameCounter * m_data->m_numChannels * bytesPerSample + 44);
- if (useExtensible) bytes += 36;
- #ifndef __LITTLE_ENDIAN__
- b3Swap32((unsigned char *)&bytes);
- #endif
- fseek(m_data->m_file, 4, SEEK_SET); // jump to file size
- fwrite(&bytes, 4, 1, m_data->m_file);
- if (useExtensible)
- { // fill in the "fact" chunk frames value
- bytes = (int)m_data->m_frameCounter;
- #ifndef __LITTLE_ENDIAN__
- b3Swap32((unsigned char *)&bytes);
- #endif
- fseek(m_data->m_file, 68, SEEK_SET);
- fwrite(&bytes, 4, 1, m_data->m_file);
- }
- fclose(m_data->m_file);
- m_data->m_file = 0;
- }
- void b3WriteWavFile::tick(double *frames, int numFrames)
- {
- int iFrames = 0;
- int j, nChannels = m_data->m_numChannels;
- for (int i = 0; i < numFrames; i++)
- {
- for (j = 0; j < nChannels; j++)
- {
- double sample = frames[iFrames++];
- if (sample < -1.)
- {
- sample = -1.;
- m_data->m_clipped = true;
- }
- if (sample > 1)
- {
- sample = 1.;
- m_data->m_clipped = true;
- }
- if (m_data->m_dataType == B3_FLOAT32)
- {
- m_data->m_floatBuffer.push_back((float)sample);
- }
- else
- {
- m_data->m_doubleBuffer.push_back(sample);
- }
- flushData(m_data->m_bufferSize);
- }
- m_data->m_frameCounter++;
- }
- }
- void b3WriteWavFile::flushData(int bufferSize)
- {
- if (m_data->m_dataType == B3_FLOAT32)
- {
- if (m_data->m_floatBuffer.size() >= bufferSize)
- {
- fwrite(&m_data->m_floatBuffer[0], sizeof(float), m_data->m_floatBuffer.size(), m_data->m_file);
- m_data->m_floatBuffer.resize(0);
- }
- }
- else
- {
- if (m_data->m_doubleBuffer.size() >= bufferSize)
- {
- fwrite(&m_data->m_doubleBuffer[0], sizeof(double), m_data->m_doubleBuffer.size(), m_data->m_file);
- m_data->m_doubleBuffer.resize(0);
- }
- }
- }
|