mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-23 16:54:03 +02:00
Add some APIs for ma_audio_buffer and ma_decoder.
* ma_audio_buffer_get_available_frames() * ma_decoder_get_available_frames()
This commit is contained in:
+129
-60
@@ -5288,7 +5288,7 @@ MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer
|
|||||||
MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount);
|
MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount);
|
||||||
MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */
|
MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */
|
||||||
MA_API ma_result ma_audio_buffer_at_end(ma_audio_buffer* pAudioBuffer);
|
MA_API ma_result ma_audio_buffer_at_end(ma_audio_buffer* pAudioBuffer);
|
||||||
|
MA_API ma_result ma_audio_buffer_get_available_frames(ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -5407,7 +5407,8 @@ struct ma_decoder
|
|||||||
ma_decoder_read_proc onRead;
|
ma_decoder_read_proc onRead;
|
||||||
ma_decoder_seek_proc onSeek;
|
ma_decoder_seek_proc onSeek;
|
||||||
void* pUserData;
|
void* pUserData;
|
||||||
ma_uint64 readPointer; /* Used for returning back to a previous position after analysing the stream or whatnot. */
|
ma_uint64 readPointerInBytes; /* In internal encoded data. */
|
||||||
|
ma_uint64 readPointerInPCMFrames; /* In output sample rate. Used for keeping track of how many frames are available for decoding. */
|
||||||
ma_format internalFormat;
|
ma_format internalFormat;
|
||||||
ma_uint32 internalChannels;
|
ma_uint32 internalChannels;
|
||||||
ma_uint32 internalSampleRate;
|
ma_uint32 internalSampleRate;
|
||||||
@@ -5511,6 +5512,17 @@ This is not thread safe without your own synchronization.
|
|||||||
*/
|
*/
|
||||||
MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex);
|
MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Retrieves the number of frames that can be read before reaching the end.
|
||||||
|
|
||||||
|
This calls `ma_decoder_get_length_in_pcm_frames()` so you need to be aware of the rules for that function, in
|
||||||
|
particular ensuring you do not call it on streams of an undefined length, such as internet radio.
|
||||||
|
|
||||||
|
If the total length of the decoder cannot be retrieved, such as with Vorbis decoders, `MA_NOT_IMPLEMENTED` will be
|
||||||
|
returned.
|
||||||
|
*/
|
||||||
|
MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input,
|
Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input,
|
||||||
pConfig should be set to what you want. On output it will be set to what you got.
|
pConfig should be set to what you want. On output it will be set to what you got.
|
||||||
@@ -41616,6 +41628,27 @@ MA_API ma_result ma_audio_buffer_at_end(ma_audio_buffer* pAudioBuffer)
|
|||||||
return pAudioBuffer->cursor == pAudioBuffer->sizeInFrames;
|
return pAudioBuffer->cursor == pAudioBuffer->sizeInFrames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MA_API ma_result ma_audio_buffer_get_available_frames(ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames)
|
||||||
|
{
|
||||||
|
if (pAvailableFrames == NULL) {
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pAvailableFrames = 0;
|
||||||
|
|
||||||
|
if (pAudioBuffer == NULL) {
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pAudioBuffer->sizeInFrames <= pAudioBuffer->cursor) {
|
||||||
|
*pAvailableFrames = 0;
|
||||||
|
} else {
|
||||||
|
*pAvailableFrames = pAudioBuffer->sizeInFrames - pAudioBuffer->cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MA_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************************************************************************************************
|
/**************************************************************************************************************************************************************
|
||||||
@@ -43427,7 +43460,7 @@ static size_t ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size
|
|||||||
MA_ASSERT(pBufferOut != NULL);
|
MA_ASSERT(pBufferOut != NULL);
|
||||||
|
|
||||||
bytesRead = pDecoder->onRead(pDecoder, pBufferOut, bytesToRead);
|
bytesRead = pDecoder->onRead(pDecoder, pBufferOut, bytesToRead);
|
||||||
pDecoder->readPointer += bytesRead;
|
pDecoder->readPointerInBytes += bytesRead;
|
||||||
|
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
@@ -43441,9 +43474,9 @@ static ma_bool32 ma_decoder_seek_bytes(ma_decoder* pDecoder, int byteOffset, ma_
|
|||||||
wasSuccessful = pDecoder->onSeek(pDecoder, byteOffset, origin);
|
wasSuccessful = pDecoder->onSeek(pDecoder, byteOffset, origin);
|
||||||
if (wasSuccessful) {
|
if (wasSuccessful) {
|
||||||
if (origin == ma_seek_origin_start) {
|
if (origin == ma_seek_origin_start) {
|
||||||
pDecoder->readPointer = (ma_uint64)byteOffset;
|
pDecoder->readPointerInBytes = (ma_uint64)byteOffset;
|
||||||
} else {
|
} else {
|
||||||
pDecoder->readPointer += byteOffset;
|
pDecoder->readPointerInBytes += byteOffset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45609,68 +45642,70 @@ MA_API ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesO
|
|||||||
|
|
||||||
/* Fast path. */
|
/* Fast path. */
|
||||||
if (pDecoder->converter.isPassthrough) {
|
if (pDecoder->converter.isPassthrough) {
|
||||||
return pDecoder->onReadPCMFrames(pDecoder, pFramesOut, frameCount);
|
totalFramesReadOut = pDecoder->onReadPCMFrames(pDecoder, pFramesOut, frameCount);
|
||||||
}
|
} else {
|
||||||
|
|
||||||
/*
|
|
||||||
Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we
|
|
||||||
need to run through each sample because we need to ensure it's internal cache is updated.
|
|
||||||
*/
|
|
||||||
if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) {
|
|
||||||
return pDecoder->onReadPCMFrames(pDecoder, NULL, frameCount); /* All decoder backends must support passing in NULL for the output buffer. */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Slow path. Need to run everything through the data converter. */
|
|
||||||
totalFramesReadOut = 0;
|
|
||||||
totalFramesReadIn = 0;
|
|
||||||
pRunningFramesOut = pFramesOut;
|
|
||||||
|
|
||||||
while (totalFramesReadOut < frameCount) {
|
|
||||||
ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */
|
|
||||||
ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels);
|
|
||||||
ma_uint64 framesToReadThisIterationIn;
|
|
||||||
ma_uint64 framesReadThisIterationIn;
|
|
||||||
ma_uint64 framesToReadThisIterationOut;
|
|
||||||
ma_uint64 framesReadThisIterationOut;
|
|
||||||
ma_uint64 requiredInputFrameCount;
|
|
||||||
|
|
||||||
framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
|
|
||||||
framesToReadThisIterationIn = framesToReadThisIterationOut;
|
|
||||||
if (framesToReadThisIterationIn > intermediaryBufferCap) {
|
|
||||||
framesToReadThisIterationIn = intermediaryBufferCap;
|
|
||||||
}
|
|
||||||
|
|
||||||
requiredInputFrameCount = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut);
|
|
||||||
if (framesToReadThisIterationIn > requiredInputFrameCount) {
|
|
||||||
framesToReadThisIterationIn = requiredInputFrameCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (requiredInputFrameCount > 0) {
|
|
||||||
framesReadThisIterationIn = pDecoder->onReadPCMFrames(pDecoder, pIntermediaryBuffer, framesToReadThisIterationIn);
|
|
||||||
totalFramesReadIn += framesReadThisIterationIn;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any
|
Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we
|
||||||
input frames, we still want to try processing frames because there may some output frames generated from cached input data.
|
need to run through each sample because we need to ensure it's internal cache is updated.
|
||||||
*/
|
*/
|
||||||
framesReadThisIterationOut = framesToReadThisIterationOut;
|
if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) {
|
||||||
result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);
|
totalFramesReadOut = pDecoder->onReadPCMFrames(pDecoder, NULL, frameCount); /* All decoder backends must support passing in NULL for the output buffer. */
|
||||||
if (result != MA_SUCCESS) {
|
} else {
|
||||||
break;
|
/* Slow path. Need to run everything through the data converter. */
|
||||||
}
|
totalFramesReadOut = 0;
|
||||||
|
totalFramesReadIn = 0;
|
||||||
|
pRunningFramesOut = pFramesOut;
|
||||||
|
|
||||||
|
while (totalFramesReadOut < frameCount) {
|
||||||
|
ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */
|
||||||
|
ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels);
|
||||||
|
ma_uint64 framesToReadThisIterationIn;
|
||||||
|
ma_uint64 framesReadThisIterationIn;
|
||||||
|
ma_uint64 framesToReadThisIterationOut;
|
||||||
|
ma_uint64 framesReadThisIterationOut;
|
||||||
|
ma_uint64 requiredInputFrameCount;
|
||||||
|
|
||||||
totalFramesReadOut += framesReadThisIterationOut;
|
framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
|
||||||
|
framesToReadThisIterationIn = framesToReadThisIterationOut;
|
||||||
|
if (framesToReadThisIterationIn > intermediaryBufferCap) {
|
||||||
|
framesToReadThisIterationIn = intermediaryBufferCap;
|
||||||
|
}
|
||||||
|
|
||||||
if (pRunningFramesOut != NULL) {
|
requiredInputFrameCount = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut);
|
||||||
pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels));
|
if (framesToReadThisIterationIn > requiredInputFrameCount) {
|
||||||
}
|
framesToReadThisIterationIn = requiredInputFrameCount;
|
||||||
|
}
|
||||||
|
|
||||||
if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {
|
if (requiredInputFrameCount > 0) {
|
||||||
break; /* We're done. */
|
framesReadThisIterationIn = pDecoder->onReadPCMFrames(pDecoder, pIntermediaryBuffer, framesToReadThisIterationIn);
|
||||||
|
totalFramesReadIn += framesReadThisIterationIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any
|
||||||
|
input frames, we still want to try processing frames because there may some output frames generated from cached input data.
|
||||||
|
*/
|
||||||
|
framesReadThisIterationOut = framesToReadThisIterationOut;
|
||||||
|
result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
totalFramesReadOut += framesReadThisIterationOut;
|
||||||
|
|
||||||
|
if (pRunningFramesOut != NULL) {
|
||||||
|
pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {
|
||||||
|
break; /* We're done. */
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pDecoder->readPointerInPCMFrames += totalFramesReadOut;
|
||||||
|
|
||||||
return totalFramesReadOut;
|
return totalFramesReadOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45681,6 +45716,7 @@ MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 fr
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (pDecoder->onSeekToPCMFrame) {
|
if (pDecoder->onSeekToPCMFrame) {
|
||||||
|
ma_result result;
|
||||||
ma_uint64 internalFrameIndex;
|
ma_uint64 internalFrameIndex;
|
||||||
if (pDecoder->internalSampleRate == pDecoder->outputSampleRate) {
|
if (pDecoder->internalSampleRate == pDecoder->outputSampleRate) {
|
||||||
internalFrameIndex = frameIndex;
|
internalFrameIndex = frameIndex;
|
||||||
@@ -45688,13 +45724,44 @@ MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 fr
|
|||||||
internalFrameIndex = ma_calculate_frame_count_after_resampling(pDecoder->internalSampleRate, pDecoder->outputSampleRate, frameIndex);
|
internalFrameIndex = ma_calculate_frame_count_after_resampling(pDecoder->internalSampleRate, pDecoder->outputSampleRate, frameIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return pDecoder->onSeekToPCMFrame(pDecoder, internalFrameIndex);
|
result = pDecoder->onSeekToPCMFrame(pDecoder, internalFrameIndex);
|
||||||
|
if (result == MA_SUCCESS) {
|
||||||
|
pDecoder->readPointerInPCMFrames = frameIndex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */
|
/* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */
|
||||||
return MA_INVALID_ARGS;
|
return MA_INVALID_ARGS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames)
|
||||||
|
{
|
||||||
|
ma_uint64 totalFrameCount;
|
||||||
|
|
||||||
|
if (pAvailableFrames == NULL) {
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pAvailableFrames = 0;
|
||||||
|
|
||||||
|
if (pDecoder == NULL) {
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
totalFrameCount = ma_decoder_get_length_in_pcm_frames(pDecoder);
|
||||||
|
if (totalFrameCount == 0) {
|
||||||
|
return MA_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalFrameCount <= pDecoder->readPointerInPCMFrames) {
|
||||||
|
*pAvailableFrames = 0;
|
||||||
|
} else {
|
||||||
|
*pAvailableFrames = totalFrameCount - pDecoder->readPointerInPCMFrames;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MA_SUCCESS; /* No frames available. */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
|
static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
|
||||||
{
|
{
|
||||||
@@ -62292,6 +62359,8 @@ v0.10.16 - TBD
|
|||||||
- Fix a bug in ma_data_source_read_pcm_frames() where looping doesn't work.
|
- Fix a bug in ma_data_source_read_pcm_frames() where looping doesn't work.
|
||||||
- Fix some compilation warnings on Windows when both DirectSound and WinMM are disabled.
|
- Fix some compilation warnings on Windows when both DirectSound and WinMM are disabled.
|
||||||
- Fix some compilation warnings when no decoders are enabled.
|
- Fix some compilation warnings when no decoders are enabled.
|
||||||
|
- Add ma_audio_buffer_get_available_frames()
|
||||||
|
- Add ma_decoder_get_available_frames()
|
||||||
- Updates to documentation.
|
- Updates to documentation.
|
||||||
|
|
||||||
v0.10.15 - 2020-07-15
|
v0.10.15 - 2020-07-15
|
||||||
|
|||||||
Reference in New Issue
Block a user