/** * @author Edouard DUPIN * * @copyright 2011, Edouard DUPIN, all right reserved * * @license BSD 3 clauses (see license file) */ #include #include #include #include typedef struct { char riffTag[4]; //!< 00-03 uint32_t size; //!< 04-07 char waveTag[4]; //!< 08-0b char fmtTag[4]; //!< 0c-0f uint32_t waveFormatSize; //!< 10-13 struct { uint16_t type; //!< 00-01 uint16_t channelCount; //!< 02-03 uint32_t samplesPerSec; //!< 04-07 uint32_t bytesPerSec; //!< 08-0b uint16_t bytesPerFrame; //!< 0c-0d uint16_t bitsPerSample; //!< 0e-0f }waveFormat; //!< 14-23 char dataTag[4]; //!< 24-27 uint32_t dataSize; //!< 28-2b }waveHeader; #define CONVERT_UINT32(littleEndien,data) (littleEndien)?(((uint32_t)((uint8_t*)data)[0] | (uint32_t)((uint8_t*)data)[1] << 8 | (uint32_t)((uint8_t*)data)[2] << 16 | (uint32_t)((uint8_t*)data)[3] << 24)): \ (((uint32_t)((uint8_t*)data)[3] | (uint32_t)((uint8_t*)data)[2] << 8 | (uint32_t)((uint8_t*)data)[1] << 16 | (uint32_t)((uint8_t*)data)[0] << 24)) #define CONVERT_INT32(littleEndien,data) (littleEndien)?(((int32_t)((uint8_t*)data)[0] | (int32_t)((uint8_t*)data)[1] << 8 | (int32_t)((uint8_t*)data)[2] << 16 | (int32_t)((int8_t*)data)[3] << 24)): \ (((int32_t)((uint8_t*)data)[3] | (int32_t)((uint8_t*)data)[2] << 8 | (int32_t)((uint8_t*)data)[1] << 16 | (int32_t)((int8_t*)data)[0] << 24)) #define CONVERT_UINT24(littleEndien,data) (littleEndien)?(((uint32_t)((uint8_t*)data)[0]<<8 | (uint32_t)((uint8_t*)data)[1] << 16 | (uint32_t)((uint8_t*)data)[2] << 24)): \ (((uint32_t)((uint8_t*)data)[2]<<8 | (uint32_t)((uint8_t*)data)[1] << 16 | (uint32_t)((uint8_t*)data)[0] << 24)) #define CONVERT_INT24(littleEndien,data) (littleEndien)?(((int32_t)((uint8_t*)data)[0]<<8 | (int32_t)((uint8_t*)data)[1] << 16 | (int32_t)((int8_t*)data)[2] << 24)): \ (((int32_t)((uint8_t*)data)[2]<<8 | (int32_t)((uint8_t*)data)[1] << 16 | (int32_t)((int8_t*)data)[0] << 24)) #define CONVERT_UINT16(littleEndien,data) (littleEndien)?(((uint16_t)((uint8_t*)data)[0] | (uint16_t)((uint8_t*)data)[1] << 8)): \ (((uint16_t)((uint8_t*)data)[1] | (uint16_t)((uint8_t*)data)[0] << 8)) #define CONVERT_INT16(littleEndien,data) (littleEndien)?(((int16_t)((uint8_t*)data)[0] | (int16_t)((int8_t*)data)[1] << 8)): \ (((int16_t)((uint8_t*)data)[1] | (int16_t)((int8_t*)data)[0] << 8)) #define COMPR_PCM (1) #define COMPR_MADPCM (2) #define COMPR_ALAW (6) #define COMPR_MULAW (7) #define COMPR_ADPCM (17) #define COMPR_YADPCM (20) #define COMPR_GSM (49) #define COMPR_G721 (64) #define COMPR_MPEG (80) int16_t* ewolsa::wav::loadAudioFile(const std::string& _filename, int8_t _nbChan, int32_t& _nbSampleOut) { _nbSampleOut = 0; waveHeader myHeader; memset(&myHeader, 0, sizeof(waveHeader)); etk::FSNode fileAccess(_filename); // Start loading the XML : EWOLSA_DEBUG("open file (WAV) \"" << fileAccess << "\""); if (false == fileAccess.exist()) { EWOLSA_ERROR("File Does not exist : \"" << fileAccess << "\""); return NULL; } int32_t fileSize = fileAccess.fileSize(); if (0 == fileSize) { EWOLSA_ERROR("This file is empty : \"" << fileAccess << "\""); return NULL; } if (false == fileAccess.fileOpenRead()) { EWOLSA_ERROR("Can not open the file : \"" << fileAccess << "\""); return NULL; } // try to find endienness : if (fileSize < (int64_t)sizeof(waveHeader)) { EWOLSA_ERROR("File : \"" << fileAccess << "\" == > has not enouth data inside might be minumum of " << (int32_t)(sizeof(waveHeader))); return NULL; } // ---------------------------------------------- // read the header : // ---------------------------------------------- if (fileAccess.fileRead(&myHeader.riffTag, 1, 4)!=4) { EWOLSA_ERROR("Can not 4 element in the file : \"" << fileAccess << "\""); return NULL; } bool littleEndien = false; if( myHeader.riffTag[0] == 'R' && myHeader.riffTag[1] == 'I' && myHeader.riffTag[2] == 'F' && (myHeader.riffTag[3] == 'F' || myHeader.riffTag[3] == 'X') ) { if (myHeader.riffTag[3] == 'F' ) { littleEndien = true; } } else { EWOLSA_ERROR("file: \"" << fileAccess << "\" Does not start with \"RIF\" " ); return NULL; } // get the data size : unsigned char tmpData[32]; if (fileAccess.fileRead(tmpData, 1, 4)!=4) { EWOLSA_ERROR("Can not 4 element in the file : \"" << fileAccess << "\""); return NULL; } myHeader.size = CONVERT_UINT32(littleEndien, tmpData); // get the data size : if (fileAccess.fileRead(&myHeader.waveTag, 1, 4)!=4) { EWOLSA_ERROR("Can not 4 element in the file : \"" << fileAccess << "\""); return NULL; } if( myHeader.waveTag[0] != 'W' || myHeader.waveTag[1] != 'A' || myHeader.waveTag[2] != 'V' || myHeader.waveTag[3] != 'E' ) { EWOLSA_ERROR("file: \"" << fileAccess << "\" This is not a wave file " << myHeader.waveTag[0] << myHeader.waveTag[1] << myHeader.waveTag[2] << myHeader.waveTag[3] ); return NULL; } // get the data size : if (fileAccess.fileRead(&myHeader.fmtTag, 1, 4)!=4) { EWOLSA_ERROR("Can not 4 element in the file : \"" << fileAccess << "\""); return NULL; } if( myHeader.fmtTag[0] != 'f' || myHeader.fmtTag[1] != 'm' || myHeader.fmtTag[2] != 't' || myHeader.fmtTag[3] != ' ' ) { EWOLSA_ERROR("file: \"" << fileAccess << "\" header error ..." << myHeader.fmtTag[0] << myHeader.fmtTag[1] << myHeader.fmtTag[2] << myHeader.fmtTag[3]); return NULL; } // get the data size : if (fileAccess.fileRead(tmpData, 1, 4)!=4) { EWOLSA_ERROR("Can not 4 element in the file : \"" << fileAccess << "\""); return NULL; } myHeader.waveFormatSize = CONVERT_UINT32(littleEndien, tmpData); if (myHeader.waveFormatSize != 16) { EWOLSA_ERROR("file : \"" << fileAccess << "\" == > header error ..."); return NULL; } if (fileAccess.fileRead(tmpData, 1, 16)!=16) { EWOLSA_ERROR("Can not 16 element in the file : \"" << fileAccess << "\""); return NULL; } unsigned char * tmppp = tmpData; myHeader.waveFormat.type = CONVERT_UINT16(littleEndien, tmppp); tmppp += 2; myHeader.waveFormat.channelCount = CONVERT_UINT16(littleEndien, tmppp); tmppp += 2; myHeader.waveFormat.samplesPerSec = CONVERT_UINT32(littleEndien, tmppp); tmppp += 4; myHeader.waveFormat.bytesPerSec = CONVERT_UINT32(littleEndien, tmppp); tmppp += 4; myHeader.waveFormat.bytesPerFrame = CONVERT_UINT16(littleEndien, tmppp); tmppp += 2; myHeader.waveFormat.bitsPerSample = CONVERT_UINT16(littleEndien, tmppp); EWOLSA_DEBUG("audio properties : "); EWOLSA_DEBUG(" type : " << myHeader.waveFormat.type); EWOLSA_DEBUG(" channelCount : " << myHeader.waveFormat.channelCount); EWOLSA_DEBUG(" samplesPerSec : " << myHeader.waveFormat.samplesPerSec); EWOLSA_DEBUG(" bytesPerSec : " << myHeader.waveFormat.bytesPerSec); EWOLSA_DEBUG(" bytesPerFrame : " << myHeader.waveFormat.bytesPerFrame); EWOLSA_DEBUG(" bitsPerSample : " << myHeader.waveFormat.bitsPerSample); // get the data size : if (fileAccess.fileRead(&myHeader.dataTag, 1, 4)!=4) { EWOLSA_ERROR("Can not 4 element in the file : \"" << fileAccess << "\""); return NULL; } if( myHeader.dataTag[0] != 'd' || myHeader.dataTag[1] != 'a' || myHeader.dataTag[2] != 't' || myHeader.dataTag[3] != 'a' ) { EWOLSA_ERROR("file: \"" << fileAccess << "\" header error ..." << myHeader.dataTag[0] << myHeader.dataTag[1] << myHeader.dataTag[2] << myHeader.dataTag[3]); return NULL; } // get the data size : if (fileAccess.fileRead(tmpData, 1, 4)!=4) { EWOLSA_ERROR("Can not 4 element in the file : \"" << fileAccess << "\""); return NULL; } myHeader.dataSize = CONVERT_UINT32(littleEndien, tmpData); // ---------------------------------------------- // end of the header reading done ... // ---------------------------------------------- //Parse the data and transform it if needed ... if (COMPR_PCM != myHeader.waveFormat.type) { EWOLSA_ERROR("File : \"" << fileAccess << "\" == > support only PCM compression ..."); return NULL; } if (myHeader.waveFormat.channelCount == 0 || myHeader.waveFormat.channelCount>2) { EWOLSA_ERROR("File : \"" << fileAccess << "\" == > support only mono or stereo ..." << myHeader.waveFormat.channelCount); return NULL; } if ( ! ( myHeader.waveFormat.bitsPerSample == 16 || myHeader.waveFormat.bitsPerSample == 24 || myHeader.waveFormat.bitsPerSample == 32 ) ) { EWOLSA_ERROR("File : \"" << fileAccess << "\" == > not supported bit/sample ..." << myHeader.waveFormat.bitsPerSample); return NULL; } if( ! ( 44100 == myHeader.waveFormat.samplesPerSec || 48000 == myHeader.waveFormat.samplesPerSec) ) { EWOLSA_ERROR("File : \"" << fileAccess << "\" == > not supported frequency " << myHeader.waveFormat.samplesPerSec << " != 48000"); return NULL; } EWOLSA_DEBUG(" dataSize : " << myHeader.dataSize); //int32_t globalDataSize = myHeader.dataSize; int32_t nbSample = (myHeader.dataSize/((myHeader.waveFormat.bitsPerSample/8)*myHeader.waveFormat.channelCount)); int32_t outputSize = _nbChan*nbSample; int16_t * outputData = new int16_t[outputSize*sizeof(int16_t)]; if (NULL == outputData) { EWOLSA_ERROR("Allocation ERROR try to allocate " << (int32_t)(outputSize*sizeof(int16_t) ) << "bytes"); return NULL; } int16_t * tmpOut = outputData; for( int32_t iii=0; iii>1) + (right>>1))>>16); } else { *tmpOut++ = (int16_t)(left>>16); *tmpOut++ = (int16_t)(right>>16); } } // close the file: fileAccess.fileClose(); _nbSampleOut = nbSample; return outputData; }