mirror of https://github.com/axmolengine/axmol.git
Improve bit depth support, both 8bit, 16bit, 32bit FLT, 64bit DBL
This commit is contained in:
parent
5776457900
commit
5a165dd270
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#define LOG_TAG "AudioDecoderWav"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
#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;
|
||||
|
|
|
@ -35,15 +35,15 @@
|
|||
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC
|
||||
#import <OpenAL/al.h>
|
||||
|
||||
#import <OpenAL/alext.h>
|
||||
#else
|
||||
|
||||
#ifdef OPENAL_PLAIN_INCLUDES
|
||||
#include <al.h>
|
||||
#include <alext.h>
|
||||
#else
|
||||
#include <AL/al.h>
|
||||
#include <AL/alext.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#include "platform/CCPlatformMacros.h"
|
||||
|
|
|
@ -29,6 +29,17 @@
|
|||
#include <string>
|
||||
|
||||
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;
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue