Commit 956701fc authored by David Reid's avatar David Reid

Update the Vorbis decoder to use the new backend infrastructure.

parent e0bfc59b
......@@ -47187,300 +47187,776 @@ static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig,
typedef struct
{
stb_vorbis* pInternalVorbis;
ma_uint8* pData;
size_t dataSize;
size_t dataCapacity;
ma_uint32 framesConsumed; /* The number of frames consumed in ppPacketData. */
ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */
float** ppPacketData;
} ma_vorbis_decoder;
ma_data_source_base ds;
ma_read_proc onRead;
ma_seek_proc onSeek;
ma_tell_proc onTell;
void* pReadSeekTellUserData;
ma_allocation_callbacks allocationCallbacks; /* Store the allocation callbacks within the structure because we may need to dynamically expand a buffer in ma_stbvorbis_read_pcm_frames() when using push mode. */
ma_format format; /* Only f32 is allowed with stb_vorbis. */
ma_uint32 channels;
ma_uint32 sampleRate;
ma_uint64 cursor;
#if !defined(MA_NO_VORBIS)
stb_vorbis* stb;
ma_bool32 usingPushMode;
struct
{
ma_uint8* pData;
size_t dataSize;
size_t dataCapacity;
ma_uint32 framesConsumed; /* The number of frames consumed in ppPacketData. */
ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */
float** ppPacketData;
} push;
#endif
} ma_stbvorbis;
static ma_uint64 ma_vorbis_decoder_read_pcm_frames(ma_vorbis_decoder* pVorbis, ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
{
float* pFramesOutF;
ma_uint64 totalFramesRead;
ma_result result;
MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis);
MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis);
MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis);
MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks);
MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex);
MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor);
MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength);
MA_ASSERT(pVorbis != NULL);
MA_ASSERT(pDecoder != NULL);
pFramesOutF = (float*)pFramesOut;
static ma_result ma_stbvorbis_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
{
return ma_stbvorbis_read_pcm_frames((ma_stbvorbis*)pDataSource, pFramesOut, frameCount, pFramesRead);
}
totalFramesRead = 0;
while (frameCount > 0) {
/* Read from the in-memory buffer first. */
ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->framesRemaining, frameCount); /* Safe cast because pVorbis->framesRemaining is 32-bit. */
static ma_result ma_stbvorbis_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
{
return ma_stbvorbis_seek_to_pcm_frame((ma_stbvorbis*)pDataSource, frameIndex);
}
if (pFramesOut != NULL) {
ma_uint64 iFrame;
for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) {
ma_uint32 iChannel;
for (iChannel = 0; iChannel < pDecoder->internalChannels; ++iChannel) {
pFramesOutF[iChannel] = pVorbis->ppPacketData[iChannel][pVorbis->framesConsumed+iFrame];
}
pFramesOutF += pDecoder->internalChannels;
}
}
static ma_result ma_stbvorbis_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
{
return ma_stbvorbis_get_data_format((ma_stbvorbis*)pDataSource, pFormat, pChannels, pSampleRate, NULL, 0);
}
pVorbis->framesConsumed += framesToReadFromCache;
pVorbis->framesRemaining -= framesToReadFromCache;
frameCount -= framesToReadFromCache;
totalFramesRead += framesToReadFromCache;
static ma_result ma_stbvorbis_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
{
return ma_stbvorbis_get_cursor_in_pcm_frames((ma_stbvorbis*)pDataSource, pCursor);
}
if (frameCount == 0) {
break;
}
static ma_result ma_stbvorbis_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
{
return ma_stbvorbis_get_length_in_pcm_frames((ma_stbvorbis*)pDataSource, pLength);
}
MA_ASSERT(pVorbis->framesRemaining == 0);
static ma_data_source_vtable g_ma_stbvorbis_ds_vtable =
{
ma_stbvorbis_ds_read,
ma_stbvorbis_ds_seek,
NULL, /* onMap() */
NULL, /* onUnmap() */
ma_stbvorbis_ds_get_data_format,
ma_stbvorbis_ds_get_cursor,
ma_stbvorbis_ds_get_length
};
/* We've run out of cached frames, so decode the next packet and continue iteration. */
do
{
int samplesRead;
int consumedDataSize;
if (pVorbis->dataSize > INT_MAX) {
break; /* Too big. */
}
static ma_result ma_stbvorbis_init_internal(const ma_decoding_backend_config* pConfig, ma_stbvorbis* pVorbis)
{
ma_result result;
ma_data_source_config dataSourceConfig;
samplesRead = 0;
consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->pInternalVorbis, pVorbis->pData, (int)pVorbis->dataSize, NULL, (float***)&pVorbis->ppPacketData, &samplesRead);
if (consumedDataSize != 0) {
size_t leftoverDataSize = (pVorbis->dataSize - (size_t)consumedDataSize);
size_t i;
for (i = 0; i < leftoverDataSize; ++i) {
pVorbis->pData[i] = pVorbis->pData[i + consumedDataSize];
}
(void)pConfig;
pVorbis->dataSize = leftoverDataSize;
pVorbis->framesConsumed = 0;
pVorbis->framesRemaining = samplesRead;
break;
} else {
/* Need more data. If there's any room in the existing buffer allocation fill that first. Otherwise expand. */
size_t bytesRead;
if (pVorbis->dataCapacity == pVorbis->dataSize) {
/* No room. Expand. */
size_t oldCap = pVorbis->dataCapacity;
size_t newCap = pVorbis->dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE;
ma_uint8* pNewData;
pNewData = (ma_uint8*)ma__realloc_from_callbacks(pVorbis->pData, newCap, oldCap, &pDecoder->allocationCallbacks);
if (pNewData == NULL) {
return totalFramesRead; /* Out of memory. */
}
if (pVorbis == NULL) {
return MA_INVALID_ARGS;
}
pVorbis->pData = pNewData;
pVorbis->dataCapacity = newCap;
}
MA_ZERO_OBJECT(pVorbis);
pVorbis->format = ma_format_f32; /* Only supporting f32. */
/* Fill in a chunk. */
result = ma_decoder_read_bytes(pDecoder, pVorbis->pData + pVorbis->dataSize, (pVorbis->dataCapacity - pVorbis->dataSize), &bytesRead);
pVorbis->dataSize += bytesRead;
dataSourceConfig = ma_data_source_config_init();
dataSourceConfig.vtable = &g_ma_stbvorbis_ds_vtable;
if (result != MA_SUCCESS) {
return totalFramesRead; /* Error reading more data, or end of file. */
}
}
} while (MA_TRUE);
result = ma_data_source_init(&dataSourceConfig, &pVorbis->ds);
if (result != MA_SUCCESS) {
return result; /* Failed to initialize the base data source. */
}
return totalFramesRead;
return MA_SUCCESS;
}
static ma_result ma_vorbis_decoder_seek_to_pcm_frame(ma_vorbis_decoder* pVorbis, ma_decoder* pDecoder, ma_uint64 frameIndex)
#if !defined(MA_NO_VORBIS)
static ma_result ma_stbvorbis_post_init(ma_stbvorbis* pVorbis)
{
float buffer[4096];
ma_result result;
stb_vorbis_info info;
MA_ASSERT(pVorbis != NULL);
MA_ASSERT(pDecoder != NULL);
/*
This is terribly inefficient because stb_vorbis does not have a good seeking solution with it's push API. Currently this just performs
a full decode right from the start of the stream. Later on I'll need to write a layer that goes through all of the Ogg pages until we
find the one containing the sample we need. Then we know exactly where to seek for stb_vorbis.
info = stb_vorbis_get_info(pVorbis->stb);
TODO: Use seeking logic documented for stb_vorbis_flush_pushdata().
*/
result = ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start);
pVorbis->channels = info.channels;
pVorbis->sampleRate = info.sample_rate;
return MA_SUCCESS;
}
#endif
MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis)
{
ma_result result;
result = ma_stbvorbis_init_internal(pConfig, pVorbis);
if (result != MA_SUCCESS) {
return result;
}
stb_vorbis_flush_pushdata(pVorbis->pInternalVorbis);
pVorbis->framesConsumed = 0;
pVorbis->framesRemaining = 0;
pVorbis->dataSize = 0;
if (onRead == NULL || onSeek == NULL) {
return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
}
pVorbis->onRead = onRead;
pVorbis->onSeek = onSeek;
pVorbis->onTell = onTell;
pVorbis->pReadSeekTellUserData = pReadSeekTellUserData;
ma_allocation_callbacks_init_copy(&pVorbis->allocationCallbacks, pAllocationCallbacks);
#if !defined(MA_NO_VORBIS)
{
/*
stb_vorbis lacks a callback based API for it's pulling API which means we're stuck with the
pushing API. In order for us to be able to successfully initialize the decoder we need to
supply it with enough data. We need to keep loading data until we have enough.
*/
stb_vorbis* stb;
size_t dataSize = 0;
size_t dataCapacity = 0;
ma_uint8* pData = NULL; /* <-- Must be initialized to NULL. */
for (;;) {
int vorbisError;
int consumedDataSize; /* <-- Fill by stb_vorbis_open_pushdata(). */
size_t bytesRead;
ma_uint8* pNewData;
/* Allocate memory for the new chunk. */
dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE;
pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity, pAllocationCallbacks);
if (pNewData == NULL) {
ma_free(pData, pAllocationCallbacks);
return MA_OUT_OF_MEMORY;
}
pData = pNewData;
while (frameIndex > 0) {
ma_uint32 framesRead;
ma_uint32 framesToRead = ma_countof(buffer)/pDecoder->internalChannels;
if (framesToRead > frameIndex) {
framesToRead = (ma_uint32)frameIndex;
/* Read in the next chunk. */
result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pData, dataSize), (dataCapacity - dataSize), &bytesRead);
dataSize += bytesRead;
if (result != MA_SUCCESS) {
ma_free(pData, pAllocationCallbacks);
return result;
}
/* We have a maximum of 31 bits with stb_vorbis. */
if (dataSize > INT_MAX) {
ma_free(pData, pAllocationCallbacks);
return MA_TOO_BIG;
}
stb = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL);
if (stb != NULL) {
/*
Successfully opened the Vorbis decoder. We might have some leftover unprocessed
data so we'll need to move that down to the front.
*/
dataSize -= (size_t)consumedDataSize; /* Consume the data. */
MA_MOVE_MEMORY(pData, ma_offset_ptr(pData, consumedDataSize), dataSize);
break;
} else {
/* Failed to open the decoder. */
if (vorbisError == VORBIS_need_more_data) {
continue;
} else {
ma_free(pData, pAllocationCallbacks);
return MA_ERROR; /* Failed to open the stb_vorbis decoder. */
}
}
}
framesRead = (ma_uint32)ma_vorbis_decoder_read_pcm_frames(pVorbis, pDecoder, buffer, framesToRead);
if (framesRead == 0) {
return MA_ERROR;
MA_ASSERT(stb != NULL);
pVorbis->stb = stb;
pVorbis->push.pData = pData;
pVorbis->push.dataSize = dataSize;
pVorbis->push.dataCapacity = dataCapacity;
pVorbis->usingPushMode = MA_TRUE;
result = ma_stbvorbis_post_init(pVorbis);
if (result != MA_SUCCESS) {
stb_vorbis_close(pVorbis->stb);
ma_free(pData, pAllocationCallbacks);
return result;
}
frameIndex -= framesRead;
return MA_SUCCESS;
}
return MA_SUCCESS;
#else
{
/* vorbis is disabled. */
(void)pAllocationCallbacks;
return MA_NOT_IMPLEMENTED;
}
#endif
}
static ma_result ma_decoder_internal_on_seek_to_pcm_frame__vorbis(ma_decoder* pDecoder, ma_uint64 frameIndex)
MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis)
{
ma_vorbis_decoder* pVorbis = (ma_vorbis_decoder*)pDecoder->pInternalDecoder;
MA_ASSERT(pVorbis != NULL);
ma_result result;
return ma_vorbis_decoder_seek_to_pcm_frame(pVorbis, pDecoder, frameIndex);
}
result = ma_stbvorbis_init_internal(pConfig, pVorbis);
if (result != MA_SUCCESS) {
return result;
}
static ma_result ma_decoder_internal_on_uninit__vorbis(ma_decoder* pDecoder)
{
ma_vorbis_decoder* pVorbis = (ma_vorbis_decoder*)pDecoder->pInternalDecoder;
MA_ASSERT(pVorbis != NULL);
#if !defined(MA_NO_VORBIS)
{
(void)pAllocationCallbacks; /* Don't know how to make use of this with stb_vorbis. */
/* We can use stb_vorbis' pull mode for file based streams. */
pVorbis->stb = stb_vorbis_open_filename(pFilePath, NULL, NULL);
if (pVorbis->stb == NULL) {
return MA_INVALID_FILE;
}
stb_vorbis_close(pVorbis->pInternalVorbis);
ma__free_from_callbacks(pVorbis->pData, &pDecoder->allocationCallbacks);
ma__free_from_callbacks(pVorbis, &pDecoder->allocationCallbacks);
pVorbis->usingPushMode = MA_FALSE;
return MA_SUCCESS;
result = ma_stbvorbis_post_init(pVorbis);
if (result != MA_SUCCESS) {
stb_vorbis_close(pVorbis->stb);
return result;
}
return MA_SUCCESS;
}
#else
{
/* vorbis is disabled. */
(void)pFilePath;
(void)pAllocationCallbacks;
return MA_NOT_IMPLEMENTED;
}
#endif
}
static ma_uint64 ma_decoder_internal_on_read_pcm_frames__vorbis(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis)
{
ma_vorbis_decoder* pVorbis;
ma_result result;
MA_ASSERT(pDecoder != NULL);
MA_ASSERT(pFramesOut != NULL);
MA_ASSERT(pDecoder->internalFormat == ma_format_f32);
result = ma_stbvorbis_init_internal(pConfig, pVorbis);
if (result != MA_SUCCESS) {
return result;
}
pVorbis = (ma_vorbis_decoder*)pDecoder->pInternalDecoder;
MA_ASSERT(pVorbis != NULL);
#if !defined(MA_NO_VORBIS)
{
(void)pAllocationCallbacks;
return ma_vorbis_decoder_read_pcm_frames(pVorbis, pDecoder, pFramesOut, frameCount);
/* stb_vorbis uses an int as it's size specifier, restricting it to 32-bit even on 64-bit systems. *sigh*. */
if (dataSize > INT_MAX) {
return MA_TOO_BIG;
}
pVorbis->stb = stb_vorbis_open_memory((const unsigned char*)pData, (int)dataSize, NULL, NULL);
if (pVorbis->stb == NULL) {
return MA_INVALID_FILE;
}
pVorbis->usingPushMode = MA_FALSE;
result = ma_stbvorbis_post_init(pVorbis);
if (result != MA_SUCCESS) {
stb_vorbis_close(pVorbis->stb);
return result;
}
return MA_SUCCESS;
}
#else
{
/* vorbis is disabled. */
(void)pData;
(void)dataSize;
(void)pAllocationCallbacks;
return MA_NOT_IMPLEMENTED;
}
#endif
}
static ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__vorbis(ma_decoder* pDecoder)
MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks)
{
/* No good way to do this with Vorbis. */
(void)pDecoder;
return 0;
if (pVorbis == NULL) {
return;
}
#if !defined(MA_NO_VORBIS)
{
stb_vorbis_close(pVorbis->stb);
/* We'll have to clear some memory if we're using push mode. */
if (pVorbis->usingPushMode) {
ma_free(pVorbis->push.pData, pAllocationCallbacks);
}
}
#else
{
/* vorbis is disabled. Should never hit this since initialization would have failed. */
MA_ASSERT(MA_FALSE);
}
#endif
ma_data_source_uninit(&pVorbis->ds);
}
static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
{
ma_result result;
stb_vorbis* pInternalVorbis = NULL;
size_t dataSize = 0;
size_t dataCapacity = 0;
ma_uint8* pData = NULL;
stb_vorbis_info vorbisInfo;
size_t vorbisDataSize;
ma_vorbis_decoder* pVorbis;
MA_ASSERT(pConfig != NULL);
MA_ASSERT(pDecoder != NULL);
if (pVorbis == NULL) {
return MA_INVALID_ARGS;
}
/* We grow the buffer in chunks. */
do
#if !defined(MA_NO_VORBIS)
{
/* Allocate memory for a new chunk. */
ma_uint8* pNewData;
size_t bytesRead;
int vorbisError = 0;
int consumedDataSize = 0;
size_t oldCapacity = dataCapacity;
dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE;
pNewData = (ma_uint8*)ma__realloc_from_callbacks(pData, dataCapacity, oldCapacity, &pDecoder->allocationCallbacks);
if (pNewData == NULL) {
ma__free_from_callbacks(pData, &pDecoder->allocationCallbacks);
return MA_OUT_OF_MEMORY;
}
/* We always use floating point format. */
ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
ma_uint64 totalFramesRead = 0;
ma_format format;
ma_uint32 channels;
pData = pNewData;
ma_stbvorbis_get_data_format(pVorbis, &format, &channels, NULL, NULL, 0);
if (format == ma_format_f32) {
/* We read differently depending on whether or not we're using push mode. */
if (pVorbis->usingPushMode) {
/* Push mode. This is the complex case. */
float* pFramesOutF32 = (float*)pFramesOut;
while (totalFramesRead < frameCount) {
/* The first thing to do is read from any already-cached frames. */
ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->push.framesRemaining, (frameCount - totalFramesRead)); /* Safe cast because pVorbis->framesRemaining is 32-bit. */
/* The output pointer can be null in which case we just treate it as a seek. */
if (pFramesOut != NULL) {
ma_uint64 iFrame;
for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) {
ma_uint32 iChannel;
for (iChannel = 0; iChannel < pVorbis->channels; iChannel += 1) {
pFramesOutF32[iChannel] = pVorbis->push.ppPacketData[iChannel][pVorbis->push.framesConsumed + iFrame];
}
pFramesOutF32 += pVorbis->channels;
}
}
/* Fill in a chunk. */
result = ma_decoder_read_bytes(pDecoder, pData + dataSize, (dataCapacity - dataSize), &bytesRead);
dataSize += bytesRead;
/* Update pointers and counters. */
pVorbis->push.framesConsumed += framesToReadFromCache;
pVorbis->push.framesRemaining -= framesToReadFromCache;
totalFramesRead += framesToReadFromCache;
if (result != MA_SUCCESS && (result != MA_AT_END || bytesRead == 0)) {
return result;
/* Don't bother reading any more frames right now if we've just finished loading. */
if (totalFramesRead == frameCount) {
break;
}
MA_ASSERT(pVorbis->push.framesRemaining == 0);
/* Getting here means we've run out of cached frames. We'll need to load some more. */
for (;;) {
int samplesRead = 0;
int consumedDataSize;
/* We need to case dataSize to an int, so make sure we can do it safely. */
if (pVorbis->push.dataSize > INT_MAX) {
break; /* Too big. */
}
consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->stb, pVorbis->push.pData, (int)pVorbis->push.dataSize, NULL, &pVorbis->push.ppPacketData, &samplesRead);
if (consumedDataSize != 0) {
/* Successfully decoded a Vorbis frame. Consume the data. */
pVorbis->push.dataSize -= (size_t)consumedDataSize;
MA_MOVE_MEMORY(pVorbis->push.pData, ma_offset_ptr(pVorbis->push.pData, consumedDataSize), pVorbis->push.dataSize);
pVorbis->push.framesConsumed = 0;
pVorbis->push.framesRemaining = samplesRead;
break;
} else {
/* Not enough data. Read more. */
size_t bytesRead;
/* Expand the data buffer if necessary. */
if (pVorbis->push.dataCapacity == pVorbis->push.dataSize) {
size_t newCap = pVorbis->push.dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE;
ma_uint8* pNewData;
pNewData = (ma_uint8*)ma_realloc(pVorbis->push.pData, newCap, &pVorbis->allocationCallbacks);
if (pNewData == NULL) {
result = MA_OUT_OF_MEMORY;
break;
}
pVorbis->push.pData = pNewData;
pVorbis->push.dataCapacity = newCap;
}
/* We should have enough room to load some data. */
result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pVorbis->push.pData, pVorbis->push.dataSize), (pVorbis->push.dataCapacity - pVorbis->push.dataSize), &bytesRead);
pVorbis->push.dataSize += bytesRead;
if (result != MA_SUCCESS) {
break; /* Failed to read any data. Get out. */
}
}
}
}
} else {
/* Pull mode. This is the simple case, but we still need to run in a loop because stb_vorbis loves using 32-bit instead of 64-bit. */
while (totalFramesRead < frameCount) {
ma_uint64 framesRemaining = (frameCount - totalFramesRead);
int framesRead;
if (framesRemaining > INT_MAX) {
framesRemaining = INT_MAX;
}
framesRead = stb_vorbis_get_samples_float_interleaved(pVorbis->stb, channels, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), (int)framesRemaining * channels); /* Safe cast. */
totalFramesRead += framesRead;
if (framesRead < framesRemaining) {
break; /* Nothing left to read. Get out. */
}
}
}
} else {
result = MA_INVALID_ARGS;
}
if (dataSize > INT_MAX) {
return MA_ERROR; /* Too big. */
pVorbis->cursor += totalFramesRead;
if (totalFramesRead == 0) {
result = MA_AT_END;
}
if (pFramesRead != NULL) {
*pFramesRead = totalFramesRead;
}
pInternalVorbis = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL);
if (pInternalVorbis != NULL) {
return result;
}
#else
{
/* vorbis is disabled. Should never hit this since initialization would have failed. */
MA_ASSERT(MA_FALSE);
(void)pFramesOut;
(void)frameCount;
(void)pFramesRead;
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex)
{
if (pVorbis == NULL) {
return MA_INVALID_ARGS;
}
#if !defined(MA_NO_VORBIS)
{
/* Different seeking methods depending on whether or not we're using push mode. */
if (pVorbis->usingPushMode) {
/* Push mode. This is the complex case. */
ma_result result;
float buffer[4096];
/*
If we get here it means we were able to open the stb_vorbis decoder. There may be some leftover bytes in our buffer, so
we need to move those bytes down to the front of the buffer since they'll be needed for future decoding.
This is terribly inefficient because stb_vorbis does not have a good seeking solution with it's push API. Currently this just performs
a full decode right from the start of the stream. Later on I'll need to write a layer that goes through all of the Ogg pages until we
find the one containing the sample we need. Then we know exactly where to seek for stb_vorbis.
TODO: Use seeking logic documented for stb_vorbis_flush_pushdata().
*/
size_t leftoverDataSize = (dataSize - (size_t)consumedDataSize);
size_t i;
for (i = 0; i < leftoverDataSize; ++i) {
pData[i] = pData[i + consumedDataSize];
/* Seek to the start of the file to begin with. */
result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, 0, ma_seek_origin_start);
if (result != MA_SUCCESS) {
return result;
}
dataSize = leftoverDataSize;
break; /* Success. */
stb_vorbis_flush_pushdata(pVorbis->stb);
pVorbis->push.framesRemaining = 0;
pVorbis->push.dataSize = 0;
/* Move the cursor back to the start. We'll increment this in the loop below. */
pVorbis->cursor = 0;
while (pVorbis->cursor < frameIndex) {
ma_uint64 framesRead;
ma_uint64 framesToRead = ma_countof(buffer)/pVorbis->channels;
if (framesToRead > (frameIndex - pVorbis->cursor)) {
framesToRead = (frameIndex - pVorbis->cursor);
}
result = ma_stbvorbis_read_pcm_frames(pVorbis, buffer, framesToRead, &framesRead);
pVorbis->cursor += framesRead;
if (result != MA_SUCCESS) {
return result;
}
}
} else {
if (vorbisError == VORBIS_need_more_data) {
continue;
} else {
return MA_ERROR; /* Failed to open the stb_vorbis decoder. */
/* Pull mode. This is the simple case. */
int vorbisResult;
if (frameIndex > UINT_MAX) {
return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */
}
vorbisResult = stb_vorbis_seek(pVorbis->stb, (unsigned int)frameIndex); /* Safe cast. */
if (vorbisResult == 0) {
return MA_ERROR; /* See failed. */
}
pVorbis->cursor = frameIndex;
}
return MA_SUCCESS;
}
#else
{
/* vorbis is disabled. Should never hit this since initialization would have failed. */
MA_ASSERT(MA_FALSE);
(void)frameIndex;
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
{
/* Defaults for safety. */
if (pFormat != NULL) {
*pFormat = ma_format_unknown;
}
if (pChannels != NULL) {
*pChannels = 0;
}
if (pSampleRate != NULL) {
*pSampleRate = 0;
}
if (pChannelMap != NULL) {
MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
}
if (pVorbis == NULL) {
return MA_INVALID_OPERATION;
}
if (pFormat != NULL) {
*pFormat = pVorbis->format;
}
#if !defined(MA_NO_VORBIS)
{
if (pChannels != NULL) {
*pChannels = pVorbis->channels;
}
if (pSampleRate != NULL) {
*pSampleRate = pVorbis->sampleRate;
}
if (pChannelMap != NULL) {
ma_get_standard_channel_map(ma_standard_channel_map_vorbis, (ma_uint32)ma_min(pVorbis->channels, channelMapCap), pChannelMap);
}
return MA_SUCCESS;
}
#else
{
/* vorbis is disabled. Should never hit this since initialization would have failed. */
MA_ASSERT(MA_FALSE);
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor)
{
if (pCursor == NULL) {
return MA_INVALID_ARGS;
}
*pCursor = 0; /* Safety. */
if (pVorbis == NULL) {
return MA_INVALID_ARGS;
}
#if !defined(MA_NO_VORBIS)
{
*pCursor = pVorbis->cursor;
return MA_SUCCESS;
}
#else
{
/* vorbis is disabled. Should never hit this since initialization would have failed. */
MA_ASSERT(MA_FALSE);
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength)
{
if (pLength == NULL) {
return MA_INVALID_ARGS;
}
*pLength = 0; /* Safety. */
if (pVorbis == NULL) {
return MA_INVALID_ARGS;
}
#if !defined(MA_NO_VORBIS)
{
if (pVorbis->usingPushMode) {
*pLength = 0; /* I don't know of a good way to determine this reliably with stb_vorbis and push mode. */
} else {
*pLength = stb_vorbis_stream_length_in_samples(pVorbis->stb);
}
} while (MA_TRUE);
return MA_SUCCESS;
}
#else
{
/* vorbis is disabled. Should never hit this since initialization would have failed. */
MA_ASSERT(MA_FALSE);
return MA_NOT_IMPLEMENTED;
}
#endif
}
/* If we get here it means we successfully opened the Vorbis decoder. */
vorbisInfo = stb_vorbis_get_info(pInternalVorbis);
static ma_result ma_decoding_backend_init__stbvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
{
ma_result result;
ma_stbvorbis* pVorbis;
/* Don't allow more than MA_MAX_CHANNELS channels. */
if (vorbisInfo.channels > MA_MAX_CHANNELS) {
stb_vorbis_close(pInternalVorbis);
ma__free_from_callbacks(pData, &pDecoder->allocationCallbacks);
return MA_ERROR; /* Too many channels. */
(void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */
/* For now we're just allocating the decoder backend on the heap. */
pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
if (pVorbis == NULL) {
return MA_OUT_OF_MEMORY;
}
result = ma_stbvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis);
if (result != MA_SUCCESS) {
ma_free(pVorbis, pAllocationCallbacks);
return result;
}
vorbisDataSize = sizeof(ma_vorbis_decoder) + sizeof(float)*vorbisInfo.max_frame_size;
pVorbis = (ma_vorbis_decoder*)ma__malloc_from_callbacks(vorbisDataSize, &pDecoder->allocationCallbacks);
*ppBackend = pVorbis;
return MA_SUCCESS;
}
static ma_result ma_decoding_backend_init_file__stbvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
{
ma_result result;
ma_stbvorbis* pVorbis;
(void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */
/* For now we're just allocating the decoder backend on the heap. */
pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
if (pVorbis == NULL) {
stb_vorbis_close(pInternalVorbis);
ma__free_from_callbacks(pData, &pDecoder->allocationCallbacks);
return MA_OUT_OF_MEMORY;
}
MA_ZERO_MEMORY(pVorbis, vorbisDataSize);
pVorbis->pInternalVorbis = pInternalVorbis;
pVorbis->pData = pData;
pVorbis->dataSize = dataSize;
pVorbis->dataCapacity = dataCapacity;
result = ma_stbvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis);
if (result != MA_SUCCESS) {
ma_free(pVorbis, pAllocationCallbacks);
return result;
}
pDecoder->onReadPCMFrames = ma_decoder_internal_on_read_pcm_frames__vorbis;
pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__vorbis;
pDecoder->onUninit = ma_decoder_internal_on_uninit__vorbis;
pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__vorbis;
pDecoder->pInternalDecoder = pVorbis;
*ppBackend = pVorbis;
/* The internal format is always f32. */
pDecoder->internalFormat = ma_format_f32;
pDecoder->internalChannels = vorbisInfo.channels;
pDecoder->internalSampleRate = vorbisInfo.sample_rate;
ma_get_standard_channel_map(ma_standard_channel_map_vorbis, pDecoder->internalChannels, pDecoder->internalChannelMap);
return MA_SUCCESS;
}
static ma_result ma_decoding_backend_init_memory__stbvorbis(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
{
ma_result result;
ma_stbvorbis* pVorbis;
(void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */
/* For now we're just allocating the decoder backend on the heap. */
pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
if (pVorbis == NULL) {
return MA_OUT_OF_MEMORY;
}
result = ma_stbvorbis_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pVorbis);
if (result != MA_SUCCESS) {
ma_free(pVorbis, pAllocationCallbacks);
return result;
}
*ppBackend = pVorbis;
return MA_SUCCESS;
}
static void ma_decoding_backend_uninit__stbvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
{
ma_stbvorbis* pVorbis = (ma_stbvorbis*)pBackend;
(void)pUserData;
ma_stbvorbis_uninit(pVorbis, pAllocationCallbacks);
ma_free(pVorbis, pAllocationCallbacks);
}
static ma_result ma_decoding_backend_get_channel_map__stbvorbis(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap)
{
ma_stbvorbis* pVorbis = (ma_stbvorbis*)pBackend;
(void)pUserData;
return ma_stbvorbis_get_data_format(pVorbis, NULL, NULL, NULL, pChannelMap, channelMapCap);
}
static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_stbvorbis =
{
ma_decoding_backend_init__stbvorbis,
ma_decoding_backend_init_file__stbvorbis,
NULL, /* onInitFileW() */
ma_decoding_backend_init_memory__stbvorbis,
ma_decoding_backend_uninit__stbvorbis,
ma_decoding_backend_get_channel_map__stbvorbis
};
static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
{
return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder);
}
#endif /* STB_VORBIS_INCLUDE_STB_VORBIS_H */
/* Raw */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment