diff --git a/cocos/audio/AudioCache.cpp b/cocos/audio/AudioCache.cpp index e31cc6b96f..fc1db7eca0 100644 --- a/cocos/audio/AudioCache.cpp +++ b/cocos/audio/AudioCache.cpp @@ -129,26 +129,38 @@ void AudioCache::readDataTask(unsigned int selfId) _readDataTaskMutex.lock(); _state = State::LOADING; - AudioDecoder* decoder = AudioDecoderManager::createDecoder(_fileFullPath.c_str()); + AudioDecoder* decoder = AudioDecoderManager::createDecoder(_fileFullPath); do { - if (decoder == nullptr || !decoder->open(_fileFullPath.c_str())) + if (decoder == nullptr || !decoder->open(_fileFullPath)) break; const uint32_t originalTotalFrames = decoder->getTotalFrames(); const uint32_t bytesPerFrame = decoder->getBytesPerFrame(); const uint32_t sampleRate = decoder->getSampleRate(); const uint32_t channelCount = decoder->getChannelCount(); + const PCM_FORMAT pcmFormat = decoder->getPcmFormat(); uint32_t totalFrames = originalTotalFrames; uint32_t dataSize = totalFrames * bytesPerFrame; uint32_t remainingFrames = totalFrames; uint32_t adjustFrames = 0; - if(bytesPerFrame > 1) + switch (pcmFormat) { + case PCM_FORMAT::PCM_16: _format = channelCount > 1 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16; // bits depth: 16bits - else + break; + case PCM_FORMAT::PCM_U8: _format = channelCount > 1 ? AL_FORMAT_STEREO8 : AL_FORMAT_MONO8; // bits depth: 8bits + break; + case PCM_FORMAT::PCM_FLT32: + _format = channelCount > 1 ? AL_FORMAT_STEREO_FLOAT32 : AL_FORMAT_MONO_FLOAT32; + break; + case PCM_FORMAT::PCM_FLT64: + _format = channelCount > 1 ? AL_FORMAT_STEREO_DOUBLE_EXT : AL_FORMAT_MONO_DOUBLE_EXT; + break; + default: assert(false); + } _sampleRate = (ALsizei)sampleRate; _duration = 1.0f * totalFrames / sampleRate; diff --git a/cocos/audio/AudioDecoder.cpp b/cocos/audio/AudioDecoder.cpp index fc2ebf07c8..c2997aae4e 100644 --- a/cocos/audio/AudioDecoder.cpp +++ b/cocos/audio/AudioDecoder.cpp @@ -37,6 +37,7 @@ AudioDecoder::AudioDecoder() , _bytesPerFrame(0) , _sampleRate(0) , _channelCount(0) + , _pcmFormat(PCM_FORMAT::PCM_16) { } @@ -89,4 +90,9 @@ AudioDecoder::AudioDecoder() return _channelCount; } -} // namespace cocos2d { + PCM_FORMAT AudioDecoder::getPcmFormat() const + { + return _pcmFormat; + } + +} \ No newline at end of file diff --git a/cocos/audio/AudioDecoderMp3.cpp b/cocos/audio/AudioDecoderMp3.cpp index 834fc3e732..9d9f2794b6 100644 --- a/cocos/audio/AudioDecoderMp3.cpp +++ b/cocos/audio/AudioDecoderMp3.cpp @@ -131,10 +131,12 @@ namespace cocos2d { if (mp3Encoding == MPG123_ENC_SIGNED_16) { _bytesPerFrame = 2 * _channelCount; + _pcmFormat = PCM_FORMAT::PCM_16; } else if (mp3Encoding == MPG123_ENC_FLOAT_32) { _bytesPerFrame = 4 * _channelCount; + _pcmFormat = PCM_FORMAT::PCM_FLT32; } else { diff --git a/cocos/audio/AudioDecoderWav.cpp b/cocos/audio/AudioDecoderWav.cpp index 58fd49a4eb..c5c8348149 100644 --- a/cocos/audio/AudioDecoderWav.cpp +++ b/cocos/audio/AudioDecoderWav.cpp @@ -25,6 +25,7 @@ #define LOG_TAG "AudioDecoderWav" #include +#include #include "audio/include/AudioDecoderWav.h" #include "audio/include/AudioMacros.h" #include "platform/CCFileUtils.h" @@ -33,174 +34,113 @@ #define _FOURCC2ID(a,b,c,d) ((uint32_t)((a) | ((b) << 8) | ((c) << 16) | (((uint32_t)(d)) << 24))) #endif -#if !defined(_WIN32) -typedef struct _GUID { - unsigned long Data1; - unsigned short Data2; - unsigned short Data3; - unsigned char Data4[8]; -} GUID; -__inline int IsEqualGUID(const GUID& rguid1, const GUID& rguid2) -{ - return !::memcmp(&rguid1, &rguid2, sizeof(GUID)); -} -#endif - -const uint32_t WAV_DATA_ID = _FOURCC2ID('d', 'a', 't', 'a'); - -// 00000001-0000-0010-8000-00aa00389b71 -static const GUID WavSubTypePCM = { - 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } -}; - -// 00000003-0000-0010-8000-00aa00389b71 -static const GUID WavSubTypeIEEE_FLOAT = { - 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } -}; - -enum class WavFormat : uint16_t { - UNKNOWN = 0x0, // Unknown Wave Format - PCM = 0x1, // PCM Format - ADPCM = 0x2, // Microsoft ADPCM Format - IEEE = 0x3, // IEEE float/double - ALAW = 0x6, // 8-bit ITU-T G.711 A-law - MULAW = 0x7, // 8-bit ITU-T G.711 MUL-law - IMA_ADPCM = 0x11, // IMA ADPCM Format - EXT = 0xFFFE // Set via subformat -}; - -bool wav_open(const std::string& fullPath, WAV_FILE* wavf) -{ - bool succeed = wavf->FileStream.open(fullPath); - if (!succeed) - return false; - - auto& fileStream = wavf->FileStream; - wavf->PcmDataOffset = 0; - - // Read to BitsPerSample - const int bytesToRead = offsetof(struct WAV_FILE_HEADER, Subchunk2ID) - offsetof(struct WAV_FILE_HEADER, ChunkID); - fileStream.read(&wavf->FileHeader, bytesToRead); - wavf->PcmDataOffset += bytesToRead; - - // check somthings - auto h = &wavf->FileHeader; - // Read PCM data or extensible data if exists. - if (h->Subchunk1Size == 16 && h->AudioFormat == (uint16_t)WavFormat::PCM) - { - // PCM - for(;;) { - // Note: 8-bit samples are stored as unsigned bytes, ranging from 0 to 255. 16-bit samples are stored as 2's-complement signed integers, ranging from -32768 to 32767. - // data - fileStream.read(&h->Subchunk2ID, sizeof(uint32_t)); - fileStream.read(&h->Subchunk2Size, sizeof(uint32_t)); - wavf->PcmDataOffset += 8; - - if (h->Subchunk2ID == WAV_DATA_ID) { - break; - } - else { // Skip other non "data" chunk - fileStream.seek(h->Subchunk2Size, SEEK_CUR); - wavf->PcmDataOffset += h->Subchunk2Size; - } - }; - } - else if (h->Subchunk1Size > 16 && h->AudioFormat == (uint16_t)WavFormat::EXT) - { - fileStream.read(&wavf->ExtraParamSize, sizeof(uint16_t)); - wavf->PcmDataOffset += 2; - - if (wavf->ExtraParamSize == 22) - { - // if cbSize is set to 22 => WAVEFORMATEXTENSIBLE - fileStream.read(&wavf->Samples, sizeof(uint16_t)); - - // DWORD dwChannelMask; which channels are present in stream - fileStream.read(&wavf->ChannelMask, sizeof(uint32_t)); - - GUID GuidSubFormat = { 0 }; - fileStream.read(&GuidSubFormat, sizeof(GUID)); - - wavf->PcmDataOffset += 22; - - // Check sub-format. - if (!IsEqualGUID(GuidSubFormat, WavSubTypePCM) - && !IsEqualGUID(GuidSubFormat, WavSubTypeIEEE_FLOAT)) - { - fileStream.close(); - return false; - } - - uint32_t chunk; - // Find "data" chunk. - while (fileStream.read(&chunk, sizeof(uint32_t)) == sizeof(uint32_t)) - { - wavf->PcmDataOffset += 4; - - if (chunk == WAV_DATA_ID) - { - h->Subchunk2ID = chunk; - fileStream.read(&h->Subchunk2Size, sizeof(uint32_t)); - wavf->PcmDataOffset += 4; - break; - } - else - { - // Read other non "data" chunks. - uint32_t chunkSize; - fileStream.read(&chunkSize, sizeof(uint32_t)); - - wavf->PcmDataOffset += 4; - - fileStream.seek(chunkSize, SEEK_CUR); // skip only - wavf->PcmDataOffset += chunkSize; - } - } - } - else - { - fileStream.close(); - return false; - } - } - else - { - fileStream.close(); - return false; - } - - wavf->BytesPerFrame = h->BitsPerSample / 8 * h->NumChannels; - - return true; -} - -int wav_read(WAV_FILE* wavf, char* pcmBuf, uint32_t bytesToRead) -{ - return wavf->FileStream.read(pcmBuf, bytesToRead); -} - -int wav_pcm_seek(WAV_FILE* wavf, int frameOffset) -{ - auto offset = frameOffset * wavf->BytesPerFrame + wavf->PcmDataOffset; - return wavf->FileStream.seek(offset, SEEK_SET) >= 0 ? 0 : -1; -} - -int wav_pcm_tell(WAV_FILE* wavf) -{ - auto offset = wavf->FileStream.seek(0, SEEK_CUR); - return (offset - wavf->PcmDataOffset) / wavf->BytesPerFrame; -} - -int wav_close(WAV_FILE* wavf) -{ - return wavf->FileStream.close(); -} - namespace cocos2d { + enum : uint32_t { + WAV_SIGN_ID = _FOURCC2ID('W', 'A', 'V', 'E'), + WAV_FMT_ID = _FOURCC2ID('f', 'm', 't', ' '), + WAV_DATA_ID = _FOURCC2ID('d', 'a', 't', 'a'), + WAV_HEADER_SIZE = sizeof(struct WAV_CHUNK_HEADER), + WAV_RIFF_SIZE = sizeof(WAV_RIFF_CHUNK), + }; + + static bool wav_scan_chunk(WAV_FILE* wavf, uint32_t chunkID, void* header, void* body, uint32_t bodySize) { + auto& fs = wavf->Stream; + auto h = (WAV_CHUNK_HEADER*)header; + for (; fs.read(h, WAV_HEADER_SIZE) == WAV_HEADER_SIZE; ) { + wavf->PcmDataOffset += WAV_HEADER_SIZE; + if (h->ChunkID == chunkID) + { // chunk found + if (body) + { // require read body? + fs.read(body, h->ChunkSize); + wavf->PcmDataOffset += h->ChunkSize; + } + return true; + } + else { + // Skip other non specified chunk + fs.seek(h->ChunkSize, SEEK_CUR); + wavf->PcmDataOffset += h->ChunkSize; + } + } + return false; + } + static bool wav_open(const std::string& fullPath, WAV_FILE* wavf) + { + bool succeed = wavf->Stream.open(fullPath); + if (!succeed) + return false; + + auto& fileStream = wavf->Stream; + wavf->PcmDataOffset = 0; + + // Parsing RIFF chunk + fileStream.read(&wavf->FileHeader, WAV_RIFF_SIZE); + wavf->PcmDataOffset += WAV_RIFF_SIZE; + + if (wavf->FileHeader.Riff.Format != WAV_SIGN_ID) + return false; // not .wav file + + // check somthings + auto h = &wavf->FileHeader; + + // Parsing FMT chunk + if (!wav_scan_chunk(wavf, WAV_FMT_ID, &wavf->FileHeader.Fmt, &wavf->FileHeader.Fmt.AudioFormat, sizeof(wavf->FileHeader.Fmt) - sizeof(WAV_RIFF_CHUNK))) + return false; + + auto& fmtInfo = h->Fmt; + // Read PCM data or extensible data if exists. + switch (fmtInfo.AudioFormat) + { // Check supported format + case WAV_FORMAT::PCM: + case WAV_FORMAT::IEEE: + break; + default: + fileStream.close(); + return false;; + } + + wavf->BytesPerFrame = fmtInfo.BitsPerSample / 8 * fmtInfo.NumChannels; + + int bitDepth = (wavf->BytesPerFrame / fmtInfo.NumChannels) << 3; + switch (bitDepth) + { + case 4: wavf->PcmFormat = PCM_FORMAT::PCM_16; break; + case 8: wavf->PcmFormat = PCM_FORMAT::PCM_U8; break; + case 16: wavf->PcmFormat = PCM_FORMAT::PCM_16; break; + case 24: wavf->PcmFormat = PCM_FORMAT::PCM_24; break; + case 32: wavf->PcmFormat = (fmtInfo.AudioFormat == WAV_FORMAT::IEEE) ? PCM_FORMAT::PCM_FLT32 : PCM_FORMAT::PCM_32; break; + case 64: wavf->PcmFormat = (fmtInfo.AudioFormat == WAV_FORMAT::IEEE) ? PCM_FORMAT::PCM_FLT64 : PCM_FORMAT::PCM_64; break; + } + + return wav_scan_chunk(wavf, WAV_DATA_ID, &h->PcmData, nullptr, 0); + } + + static int wav_read(WAV_FILE* wavf, char* pcmBuf, uint32_t bytesToRead) + { + return wavf->Stream.read(pcmBuf, bytesToRead); + } + + static int wav_pcm_seek(WAV_FILE* wavf, int frameOffset) + { + auto offset = frameOffset * wavf->BytesPerFrame + wavf->PcmDataOffset; + return wavf->Stream.seek(offset, SEEK_SET) >= 0 ? 0 : -1; + } + + static int wav_pcm_tell(WAV_FILE* wavf) + { + auto offset = wavf->Stream.seek(0, SEEK_CUR); + return (offset - wavf->PcmDataOffset) / wavf->BytesPerFrame; + } + + static int wav_close(WAV_FILE* wavf) + { + return wavf->Stream.close(); + } AudioDecoderWav::AudioDecoderWav() { - memset(&_wavf, 0, offsetof(WAV_FILE, FileStream) ); + memset(&_wavf, 0, offsetof(WAV_FILE, Stream)); } AudioDecoderWav::~AudioDecoderWav() @@ -212,10 +152,11 @@ namespace cocos2d { { if (wav_open(fullPath, &_wavf)) { - _sampleRate = _wavf.FileHeader.SampleRate; - _channelCount = _wavf.FileHeader.NumChannels; + _sampleRate = _wavf.FileHeader.Fmt.SampleRate; + _channelCount = _wavf.FileHeader.Fmt.NumChannels; _bytesPerFrame = _wavf.BytesPerFrame; - _totalFrames = _wavf.FileHeader.Subchunk2Size / _bytesPerFrame; + _totalFrames = _wavf.FileHeader.PcmData.ChunkSize / _bytesPerFrame; + _pcmFormat = _wavf.PcmFormat; _isOpened = true; return true; diff --git a/cocos/audio/include/AudioCache.h b/cocos/audio/include/AudioCache.h index 626566e144..21aa480203 100644 --- a/cocos/audio/include/AudioCache.h +++ b/cocos/audio/include/AudioCache.h @@ -35,15 +35,15 @@ #if CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC #import - +#import #else - #ifdef OPENAL_PLAIN_INCLUDES #include +#include #else #include +#include #endif - #endif #include "platform/CCPlatformMacros.h" diff --git a/cocos/audio/include/AudioDecoder.h b/cocos/audio/include/AudioDecoder.h index d5f6c0a302..449a9d311c 100644 --- a/cocos/audio/include/AudioDecoder.h +++ b/cocos/audio/include/AudioDecoder.h @@ -29,6 +29,17 @@ #include namespace cocos2d { +enum class PCM_FORMAT : uint16_t +{ + PCM_UNK, // Unknown + PCM_U8, + PCM_16, + PCM_24, + PCM_32, + PCM_64, + PCM_FLT32, + PCM_FLT64, +}; /** * @brief The class for decoding compressed audio file to PCM buffer. @@ -103,6 +114,8 @@ public: */ virtual uint32_t getChannelCount() const; + virtual PCM_FORMAT getPcmFormat() const; + protected: AudioDecoder(); virtual ~AudioDecoder(); @@ -112,6 +125,7 @@ protected: uint32_t _bytesPerFrame; uint32_t _sampleRate; uint32_t _channelCount; + PCM_FORMAT _pcmFormat; friend class AudioDecoderManager; }; diff --git a/cocos/audio/include/AudioDecoderWav.h b/cocos/audio/include/AudioDecoderWav.h index efe6044fb5..ad37c4e196 100644 --- a/cocos/audio/include/AudioDecoderWav.h +++ b/cocos/audio/include/AudioDecoderWav.h @@ -29,22 +29,47 @@ #include "audio/include/AudioDecoder.h" #include "platform/PXFileStream.h" -#pragma pack(push,1) +namespace cocos2d { + // http://soundfile.sapp.org/doc/WaveFormat/ -struct WAV_FILE_HEADER { +enum class WAV_FORMAT : uint16_t +{ + UNKNOWN = 0x0, // Unknown Wave Format + PCM = 0x1, // PCM Format + ADPCM = 0x2, // Microsoft ADPCM Format + IEEE = 0x3, // IEEE float/double + ALAW = 0x6, // 8-bit ITU-T G.711 A-law + MULAW = 0x7, // 8-bit ITU-T G.711 MUL-law + IMA_ADPCM = 0x11, // IMA ADPCM Format + EXT = 0xFFFE // Set via subformat +}; + +#pragma pack(push,1) +struct WAV_CHUNK_HEADER { uint32_t ChunkID; + /* + ** The ChunkSize gives the size of the valid data in the chunk. It does not include the padding, the size of chunkID, or the size of chunkSize. + ** see: https://docs.microsoft.com/en-us/windows/win32/xaudio2/resource-interchange-file-format--riff- + */ uint32_t ChunkSize; +}; +struct WAV_RIFF_CHUNK { + WAV_CHUNK_HEADER Header; uint32_t Format; - uint32_t Subchunk1ID; - uint32_t Subchunk1Size; - uint16_t AudioFormat; +}; +struct WAV_FMT_CHUNK { + WAV_CHUNK_HEADER Header; + WAV_FORMAT AudioFormat; uint16_t NumChannels; uint32_t SampleRate; uint32_t ByteRate; uint16_t BlockAlign; uint16_t BitsPerSample; - uint32_t Subchunk2ID; - uint32_t Subchunk2Size; +}; +struct WAV_FILE_HEADER { + WAV_RIFF_CHUNK Riff; + WAV_FMT_CHUNK Fmt; + WAV_CHUNK_HEADER PcmData; }; #pragma pack(pop) @@ -53,13 +78,13 @@ struct WAV_FILE WAV_FILE_HEADER FileHeader; uint32_t PcmDataOffset; uint32_t BytesPerFrame; - uint16_t ExtraParamSize; - uint32_t Samples; - uint32_t ChannelMask; - cocos2d::PXFileStream FileStream; + PCM_FORMAT PcmFormat; + uint16_t ExtraParamSize; // unused + uint32_t Samples; // unused + uint32_t ChannelMask; // unused + cocos2d::PXFileStream Stream; }; -namespace cocos2d { /** * @brief The class for decoding compressed audio file to PCM buffer.