mirror of https://github.com/axmolengine/axmol.git
* fixed #10482: New AudioEngine class can't play large ogg file on Win32. * [win32] Small logic fix in AudioPlayer::readPcmData.
This commit is contained in:
parent
6eaf15a2c2
commit
cc9871b71d
|
@ -21,6 +21,9 @@
|
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#define LOG_TAG "AudioCache"
|
||||
|
||||
#include "platform/CCPlatformConfig.h"
|
||||
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
|
||||
|
@ -35,10 +38,68 @@
|
|||
#include "base/CCDirector.h"
|
||||
#include "base/CCScheduler.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#define PCMDATA_CACHEMAXSIZE 2621440
|
||||
|
||||
using namespace cocos2d::experimental;
|
||||
|
||||
//FIXME: Move _winLog, winLog to a separated file
|
||||
static void _winLog(const char *format, va_list args)
|
||||
{
|
||||
static const int MAX_LOG_LENGTH = 16 * 1024;
|
||||
int bufferSize = MAX_LOG_LENGTH;
|
||||
char* buf = nullptr;
|
||||
|
||||
do
|
||||
{
|
||||
buf = new (std::nothrow) char[bufferSize];
|
||||
if (buf == nullptr)
|
||||
return; // not enough memory
|
||||
|
||||
int ret = vsnprintf(buf, bufferSize - 3, format, args);
|
||||
if (ret < 0)
|
||||
{
|
||||
bufferSize *= 2;
|
||||
|
||||
delete[] buf;
|
||||
}
|
||||
else
|
||||
break;
|
||||
|
||||
} while (true);
|
||||
|
||||
strcat(buf, "\n");
|
||||
|
||||
int pos = 0;
|
||||
int len = strlen(buf);
|
||||
char tempBuf[MAX_LOG_LENGTH + 1] = { 0 };
|
||||
WCHAR wszBuf[MAX_LOG_LENGTH + 1] = { 0 };
|
||||
|
||||
do
|
||||
{
|
||||
std::copy(buf + pos, buf + pos + MAX_LOG_LENGTH, tempBuf);
|
||||
|
||||
tempBuf[MAX_LOG_LENGTH] = 0;
|
||||
|
||||
MultiByteToWideChar(CP_UTF8, 0, tempBuf, -1, wszBuf, sizeof(wszBuf));
|
||||
OutputDebugStringW(wszBuf);
|
||||
|
||||
pos += MAX_LOG_LENGTH;
|
||||
|
||||
} while (pos < len);
|
||||
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
void audioLog(const char * format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
_winLog(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
AudioCache::AudioCache()
|
||||
: _pcmData(nullptr)
|
||||
, _pcmDataSize(0)
|
||||
|
@ -100,7 +161,7 @@ void AudioCache::readDataTask()
|
|||
vf = new OggVorbis_File;
|
||||
int openCode;
|
||||
if (openCode = ov_fopen(FileUtils::getInstance()->getSuitableFOpen(_fileFullPath).c_str(), vf)){
|
||||
log("Input does not appear to be an Ogg bitstream: %s. Code: 0x%x\n", _fileFullPath.c_str(), openCode);
|
||||
ALOGE("Input does not appear to be an Ogg bitstream: %s. Code: 0x%x\n", _fileFullPath.c_str(), openCode);
|
||||
goto ExitThread;
|
||||
}
|
||||
|
||||
|
@ -119,13 +180,13 @@ void AudioCache::readDataTask()
|
|||
int error = MPG123_OK;
|
||||
mpg123handle = mpg123_new(nullptr, &error);
|
||||
if (!mpg123handle){
|
||||
log("Basic setup goes wrong: %s", mpg123_plain_strerror(error));
|
||||
ALOGE("Basic setup goes wrong: %s", mpg123_plain_strerror(error));
|
||||
goto ExitThread;
|
||||
}
|
||||
|
||||
if (mpg123_open(mpg123handle,_fileFullPath.c_str()) != MPG123_OK ||
|
||||
mpg123_getformat(mpg123handle, &rate, &_channels, &_mp3Encoding) != MPG123_OK) {
|
||||
log("Trouble with mpg123: %s\n", mpg123_strerror(mpg123handle) );
|
||||
ALOGE("Trouble with mpg123: %s\n", mpg123_strerror(mpg123handle) );
|
||||
goto ExitThread;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,38 @@
|
|||
#define QUEUEBUFFER_NUM 5
|
||||
#define QUEUEBUFFER_TIME_STEP 0.1f
|
||||
|
||||
// log, CCLOG aren't threadsafe, since we uses sub threads for parsing pcm data, threadsafe log output
|
||||
// is needed. Define the following macros (ALOGV, ALOGD, ALOGI, ALOGW, ALOGE) for threadsafe log output.
|
||||
|
||||
//FIXME:Move the definition of the following macros to a separated file.
|
||||
|
||||
void audioLog(const char * format, ...);
|
||||
|
||||
#define QUOTEME_(x) #x
|
||||
#define QUOTEME(x) QUOTEME_(x)
|
||||
|
||||
#if defined(COCOS2D_DEBUG) && COCOS2D_DEBUG > 0
|
||||
#define ALOGV(fmt, ...) audioLog("V/" LOG_TAG " (" QUOTEME(__LINE__) "): " fmt "", ##__VA_ARGS__)
|
||||
#else
|
||||
#define ALOGV(fmt, ...) do {} while(false)
|
||||
#endif
|
||||
#define ALOGD(fmt, ...) audioLog("D/" LOG_TAG " (" QUOTEME(__LINE__) "): " fmt "", ##__VA_ARGS__)
|
||||
#define ALOGI(fmt, ...) audioLog("I/" LOG_TAG " (" QUOTEME(__LINE__) "): " fmt "", ##__VA_ARGS__)
|
||||
#define ALOGW(fmt, ...) audioLog("W/" LOG_TAG " (" QUOTEME(__LINE__) "): " fmt "", ##__VA_ARGS__)
|
||||
#define ALOGE(fmt, ...) audioLog("E/" LOG_TAG " (" QUOTEME(__LINE__) "): " fmt "", ##__VA_ARGS__)
|
||||
|
||||
#if defined(COCOS2D_DEBUG) && COCOS2D_DEBUG > 0
|
||||
#define CHECK_AL_ERROR_DEBUG() \
|
||||
do { \
|
||||
GLenum __error = alGetError(); \
|
||||
if (__error) { \
|
||||
ALOGE("OpenAL error 0x%04X in %s %s %d\n", __error, __FILE__, __FUNCTION__, __LINE__); \
|
||||
} \
|
||||
} while (false)
|
||||
#else
|
||||
#define CHECK_AL_ERROR_DEBUG()
|
||||
#endif
|
||||
|
||||
NS_CC_BEGIN
|
||||
namespace experimental{
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#define LOG_TAG "AudioEngine-Win32"
|
||||
|
||||
#include "platform/CCPlatformConfig.h"
|
||||
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
|
||||
|
@ -88,7 +90,7 @@ bool AudioEngineImpl::init()
|
|||
alGenSources(MAX_AUDIOINSTANCES, _alSources);
|
||||
alError = alGetError();
|
||||
if(alError != AL_NO_ERROR){
|
||||
log("%s:generating sources fail! error = %x\n", __FUNCTION__, alError);
|
||||
ALOGE("%s:generating sources fail! error = %x\n", __FUNCTION__, alError);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -140,14 +142,14 @@ AudioCache* AudioEngineImpl::preload(const std::string& filePath, std::function<
|
|||
}
|
||||
else
|
||||
{
|
||||
log("Basic setup goes wrong: %s", mpg123_plain_strerror(error));
|
||||
ALOGE("Basic setup goes wrong: %s", mpg123_plain_strerror(error));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log("Unsupported media type file: %s\n", filePath.c_str());
|
||||
ALOGE("Unsupported media type file: %s\n", filePath.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -245,7 +247,7 @@ void AudioEngineImpl::setVolume(int audioID,float volume)
|
|||
|
||||
auto error = alGetError();
|
||||
if (error != AL_NO_ERROR) {
|
||||
log("%s: audio id = %d, error = %x\n", __FUNCTION__,audioID,error);
|
||||
ALOGE("%s: audio id = %d, error = %x\n", __FUNCTION__,audioID,error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -266,7 +268,7 @@ void AudioEngineImpl::setLoop(int audioID, bool loop)
|
|||
|
||||
auto error = alGetError();
|
||||
if (error != AL_NO_ERROR) {
|
||||
log("%s: audio id = %d, error = %x\n", __FUNCTION__,audioID,error);
|
||||
ALOGE("%s: audio id = %d, error = %x\n", __FUNCTION__,audioID,error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -283,7 +285,7 @@ bool AudioEngineImpl::pause(int audioID)
|
|||
auto error = alGetError();
|
||||
if (error != AL_NO_ERROR) {
|
||||
ret = false;
|
||||
log("%s: audio id = %d, error = %x\n", __FUNCTION__,audioID,error);
|
||||
ALOGE("%s: audio id = %d, error = %x\n", __FUNCTION__,audioID,error);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -297,7 +299,7 @@ bool AudioEngineImpl::resume(int audioID)
|
|||
auto error = alGetError();
|
||||
if (error != AL_NO_ERROR) {
|
||||
ret = false;
|
||||
log("%s: audio id = %d, error = %x\n", __FUNCTION__,audioID,error);
|
||||
ALOGE("%s: audio id = %d, error = %x\n", __FUNCTION__,audioID,error);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -313,7 +315,7 @@ bool AudioEngineImpl::stop(int audioID)
|
|||
auto error = alGetError();
|
||||
if (error != AL_NO_ERROR) {
|
||||
ret = false;
|
||||
log("%s: audio id = %d, error = %x\n", __FUNCTION__,audioID,error);
|
||||
ALOGE("%s: audio id = %d, error = %x\n", __FUNCTION__,audioID,error);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -380,7 +382,7 @@ float AudioEngineImpl::getCurrentTime(int audioID)
|
|||
|
||||
auto error = alGetError();
|
||||
if (error != AL_NO_ERROR) {
|
||||
log("%s, audio id:%d,error code:%x", __FUNCTION__,audioID,error);
|
||||
ALOGE("%s, audio id:%d,error code:%x", __FUNCTION__,audioID,error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -407,7 +409,7 @@ bool AudioEngineImpl::setCurrentTime(int audioID, float time)
|
|||
|
||||
auto error = alGetError();
|
||||
if (error != AL_NO_ERROR) {
|
||||
log("%s: audio id = %d, error = %x\n", __FUNCTION__,audioID,error);
|
||||
ALOGE("%s: audio id = %d, error = %x\n", __FUNCTION__,audioID,error);
|
||||
}
|
||||
ret = true;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#define LOG_TAG "AudioPlayer"
|
||||
|
||||
#include "platform/CCPlatformConfig.h"
|
||||
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
|
||||
|
@ -109,7 +112,7 @@ bool AudioPlayer::play2d(AudioCache* cache)
|
|||
alSourceQueueBuffers(_alSource, QUEUEBUFFER_NUM, _bufferIds);
|
||||
}
|
||||
else {
|
||||
log("%s:alGenBuffers error code:%x", __FUNCTION__,alError);
|
||||
ALOGE("%s:alGenBuffers error code:%x", __FUNCTION__,alError);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -125,7 +128,7 @@ bool AudioPlayer::play2d(AudioCache* cache)
|
|||
|
||||
auto alError = alGetError();
|
||||
if (alError != AL_NO_ERROR) {
|
||||
log("%s:alSourcePlay error code:%x\n", __FUNCTION__, alError);
|
||||
ALOGE("%s:alSourcePlay error code:%x\n", __FUNCTION__, alError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -135,6 +138,26 @@ bool AudioPlayer::play2d(AudioCache* cache)
|
|||
return true;
|
||||
}
|
||||
|
||||
int AudioPlayer::readPcmData(char* buffer, int bufferSize, const std::function<int/*readBytes*/(char* /*buf*/, int /*bytesToRead*/)>& fileReader)
|
||||
{
|
||||
assert(buffer != nullptr && bufferSize > 0);
|
||||
|
||||
int readBytes = 0;
|
||||
int readBytesOnce = 0;
|
||||
int remainBytes = bufferSize;
|
||||
do
|
||||
{
|
||||
readBytesOnce = fileReader(buffer + readBytes, remainBytes);
|
||||
if (readBytesOnce > 0)
|
||||
{
|
||||
readBytes += readBytesOnce;
|
||||
remainBytes -= readBytesOnce;
|
||||
}
|
||||
} while (readBytesOnce > 0 && readBytes < bufferSize);
|
||||
|
||||
return readBytes;
|
||||
}
|
||||
|
||||
void AudioPlayer::rotateBufferThread(int offsetFrame)
|
||||
{
|
||||
ALint sourceState;
|
||||
|
@ -142,6 +165,9 @@ void AudioPlayer::rotateBufferThread(int offsetFrame)
|
|||
mpg123_handle* mpg123handle = nullptr;
|
||||
OggVorbis_File* vorbisFile = nullptr;
|
||||
|
||||
std::function<int(char*, int)> fileReader = nullptr;
|
||||
std::function<void(int)> fileSeeker = nullptr;
|
||||
|
||||
auto audioFileFormat = _audioCache->_fileFormat;
|
||||
char* tmpBuffer = (char*)malloc(_audioCache->_queBufferBytes);
|
||||
|
||||
|
@ -152,7 +178,7 @@ void AudioPlayer::rotateBufferThread(int offsetFrame)
|
|||
int error = MPG123_OK;
|
||||
mpg123handle = mpg123_new(nullptr, &error);
|
||||
if (!mpg123handle){
|
||||
log("Basic setup goes wrong: %s", mpg123_plain_strerror(error));
|
||||
ALOGE("Basic setup goes wrong: %s", mpg123_plain_strerror(error));
|
||||
goto ExitBufferThread;
|
||||
}
|
||||
long rate = 0;
|
||||
|
@ -160,7 +186,7 @@ void AudioPlayer::rotateBufferThread(int offsetFrame)
|
|||
int mp3Encoding = 0;
|
||||
if (mpg123_open(mpg123handle,_audioCache->_fileFullPath.c_str()) != MPG123_OK
|
||||
|| mpg123_getformat(mpg123handle, &rate, &channels, &mp3Encoding) != MPG123_OK){
|
||||
log("Trouble with mpg123: %s\n", mpg123_strerror(mpg123handle) );
|
||||
ALOGE("Trouble with mpg123: %s\n", mpg123_strerror(mpg123handle) );
|
||||
goto ExitBufferThread;
|
||||
}
|
||||
|
||||
|
@ -178,7 +204,7 @@ void AudioPlayer::rotateBufferThread(int offsetFrame)
|
|||
vorbisFile = new OggVorbis_File;
|
||||
int openCode;
|
||||
if (openCode = ov_fopen(FileUtils::getInstance()->getSuitableFOpen(_audioCache->_fileFullPath).c_str(), vorbisFile)){
|
||||
log("Input does not appear to be an Ogg bitstream: %s. Code: 0x%x\n", _audioCache->_fileFullPath.c_str(), openCode);
|
||||
ALOGE("Input does not appear to be an Ogg bitstream: %s. Code: 0x%x\n", _audioCache->_fileFullPath.c_str(), openCode);
|
||||
goto ExitBufferThread;
|
||||
}
|
||||
if (offsetFrame != 0) {
|
||||
|
@ -192,6 +218,30 @@ void AudioPlayer::rotateBufferThread(int offsetFrame)
|
|||
|
||||
alSourcePlay(_alSource);
|
||||
|
||||
if (audioFileFormat == AudioCache::FileFormat::MP3)
|
||||
{
|
||||
fileReader = [&](char* buffer, int bufferSize) -> int {
|
||||
size_t ret = 0;
|
||||
mpg123_read(mpg123handle, (unsigned char*)buffer, bufferSize, &ret);
|
||||
return ret;
|
||||
};
|
||||
|
||||
fileSeeker = [&](int pos) {
|
||||
mpg123_seek(mpg123handle, pos, SEEK_SET);
|
||||
};
|
||||
}
|
||||
else if (audioFileFormat == AudioCache::FileFormat::OGG)
|
||||
{
|
||||
fileReader = [&](char* buffer, int bufferSize) -> int {
|
||||
int current_section = 0;
|
||||
return ov_read(vorbisFile, buffer, bufferSize, 0, 2, 1, ¤t_section);
|
||||
};
|
||||
|
||||
fileSeeker = [&](int pos) {
|
||||
ov_pcm_seek(vorbisFile, pos);
|
||||
};
|
||||
}
|
||||
|
||||
while (!_exitThread) {
|
||||
alGetSourcei(_alSource, AL_SOURCE_STATE, &sourceState);
|
||||
if (sourceState == AL_PLAYING) {
|
||||
|
@ -203,18 +253,7 @@ void AudioPlayer::rotateBufferThread(int offsetFrame)
|
|||
if (_timeDirty) {
|
||||
_timeDirty = false;
|
||||
offsetFrame = _currTime * _audioCache->_sampleRate;
|
||||
|
||||
switch (audioFileFormat)
|
||||
{
|
||||
case AudioCache::FileFormat::MP3:
|
||||
mpg123_seek(mpg123handle,offsetFrame,SEEK_SET);
|
||||
break;
|
||||
case AudioCache::FileFormat::OGG:
|
||||
ov_pcm_seek(vorbisFile,offsetFrame);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fileSeeker(offsetFrame);
|
||||
}
|
||||
else {
|
||||
_currTime += QUEUEBUFFER_TIME_STEP;
|
||||
|
@ -227,15 +266,13 @@ void AudioPlayer::rotateBufferThread(int offsetFrame)
|
|||
}
|
||||
}
|
||||
|
||||
size_t readRet = 0;
|
||||
if(audioFileFormat == AudioCache::FileFormat::MP3)
|
||||
{
|
||||
mpg123_read(mpg123handle,(unsigned char*)tmpBuffer, _audioCache->_queBufferBytes,&readRet);
|
||||
int readRet = readPcmData(tmpBuffer, _audioCache->_queBufferBytes, fileReader);
|
||||
if (readRet <= 0) {
|
||||
if (_loop) {
|
||||
mpg123_seek(mpg123handle,0,SEEK_SET);
|
||||
mpg123_read(mpg123handle,(unsigned char*)tmpBuffer, _audioCache->_queBufferBytes,&readRet);
|
||||
} else {
|
||||
fileSeeker(0);
|
||||
readRet = readPcmData(tmpBuffer, _audioCache->_queBufferBytes, fileReader);
|
||||
}
|
||||
else {
|
||||
_exitThread = true;
|
||||
break;
|
||||
}
|
||||
|
@ -244,32 +281,22 @@ void AudioPlayer::rotateBufferThread(int offsetFrame)
|
|||
{
|
||||
_audioCache->_bytesOfRead += readRet;
|
||||
}
|
||||
}
|
||||
else if(audioFileFormat == AudioCache::FileFormat::OGG)
|
||||
{
|
||||
int current_section;
|
||||
readRet = ov_read(vorbisFile,tmpBuffer,_audioCache->_queBufferBytes,0,2,1,¤t_section);
|
||||
if (readRet <= 0) {
|
||||
if (_loop) {
|
||||
ov_pcm_seek(vorbisFile,0);
|
||||
readRet = ov_read(vorbisFile,tmpBuffer,_audioCache->_queBufferBytes,0,2,1,¤t_section);
|
||||
} else {
|
||||
_exitThread = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//ALOGV("readRet: %d, queBufferBytes: %d", (int)readRet, _audioCache->_queBufferBytes);
|
||||
ALuint bid;
|
||||
alSourceUnqueueBuffers(_alSource, 1, &bid);
|
||||
CHECK_AL_ERROR_DEBUG();
|
||||
alBufferData(bid, _audioCache->_alBufferFormat, tmpBuffer, readRet, _audioCache->_sampleRate);
|
||||
CHECK_AL_ERROR_DEBUG();
|
||||
alSourceQueueBuffers(_alSource, 1, &bid);
|
||||
CHECK_AL_ERROR_DEBUG();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lk(_sleepMutex);
|
||||
if (_exitThread)
|
||||
{
|
||||
ALOGV("thread exit...");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -292,6 +319,7 @@ ExitBufferThread:
|
|||
}
|
||||
free(tmpBuffer);
|
||||
_readForRemove = true;
|
||||
ALOGV("%s exited.\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
bool AudioPlayer::setLoop(bool loop)
|
||||
|
|
|
@ -61,6 +61,7 @@ public:
|
|||
protected:
|
||||
void rotateBufferThread(int offsetFrame);
|
||||
bool play2d(AudioCache* cache);
|
||||
int readPcmData(char* buffer, int bufferSize, const std::function<int/*readBytes*/(char* /*buf*/, int /*bytesToRead*/)>& fileReader);
|
||||
|
||||
AudioCache* _audioCache;
|
||||
|
||||
|
|
Loading…
Reference in New Issue