mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-22 00:06:59 +02:00
Add support for loop points to ma_data_source.
The loop points are specified relative to the range, and will be clamped as such.
This commit is contained in:
+165
-17
@@ -5915,6 +5915,8 @@ typedef struct
|
||||
const ma_data_source_vtable* vtable;
|
||||
ma_uint64 rangeBegInFrames;
|
||||
ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */
|
||||
ma_uint64 loopBegInFrames; /* Relative to rangeBegInFrames. */
|
||||
ma_uint64 loopEndInFrames; /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */
|
||||
ma_data_source* pCurrent; /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */
|
||||
ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */
|
||||
ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */
|
||||
@@ -5931,10 +5933,16 @@ MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_
|
||||
MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor);
|
||||
MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength); /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */
|
||||
#if defined(MA_EXPERIMENTAL__DATA_LOOPING_AND_CHAINING)
|
||||
MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBeg, ma_uint64 rangeEnd);
|
||||
MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames);
|
||||
MA_API void ma_data_source_get_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames);
|
||||
MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames);
|
||||
MA_API void ma_data_source_get_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames);
|
||||
MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource);
|
||||
MA_API ma_data_source* ma_data_source_get_current(ma_data_source* pDataSource);
|
||||
MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource);
|
||||
MA_API ma_data_source* ma_data_source_get_next(ma_data_source* pDataSource);
|
||||
MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext);
|
||||
MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(ma_data_source* pDataSource);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -43174,6 +43182,8 @@ MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_da
|
||||
pDataSourceBase->vtable = pConfig->vtable;
|
||||
pDataSourceBase->rangeBegInFrames = 0;
|
||||
pDataSourceBase->rangeEndInFrames = ~((ma_uint64)0);
|
||||
pDataSourceBase->loopBegInFrames = 0;
|
||||
pDataSourceBase->loopEndInFrames = ~((ma_uint64)0);
|
||||
pDataSourceBase->pCurrent = pDataSource; /* Always read from ourself by default. */
|
||||
pDataSourceBase->pNext = NULL;
|
||||
pDataSourceBase->onGetNext = NULL;
|
||||
@@ -43225,7 +43235,7 @@ static ma_result ma_data_source_resolve_current(ma_data_source* pDataSource, ma_
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
||||
static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead, ma_bool32 loop)
|
||||
{
|
||||
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
||||
|
||||
@@ -43233,7 +43243,7 @@ static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDa
|
||||
return MA_AT_END;
|
||||
}
|
||||
|
||||
if (pDataSourceBase->rangeEndInFrames == ~((ma_uint64)0)) {
|
||||
if (pDataSourceBase->rangeEndInFrames == ~((ma_uint64)0) && (pDataSourceBase->loopEndInFrames == ~((ma_uint64)0) || loop == MA_FALSE)) {
|
||||
/* No range is set - just read like normal. The data source itself will tell us when the end is reached. */
|
||||
return pDataSourceBase->cb.onRead(pDataSourceBase, pFramesOut, frameCount, pFramesRead);
|
||||
} else {
|
||||
@@ -43241,16 +43251,26 @@ static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDa
|
||||
ma_result result;
|
||||
ma_uint64 cursor;
|
||||
ma_uint64 framesRead = 0;
|
||||
ma_uint64 rangeEnd;
|
||||
|
||||
result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &cursor);
|
||||
if (result != MA_SUCCESS) {
|
||||
/* Failed to retrieve the cursor. Cannot read within a range. Just read like normal - this may happen for things like noise data sources where it doesn't really matter. */
|
||||
/* Failed to retrieve the cursor. Cannot read within a range or loop points. Just read like normal - this may happen for things like noise data sources where it doesn't really matter. */
|
||||
return pDataSourceBase->cb.onRead(pDataSourceBase, pFramesOut, frameCount, pFramesRead);
|
||||
}
|
||||
|
||||
/* We have the cursor. We need to make sure we don't read beyond our range. */
|
||||
if (frameCount > (pDataSourceBase->rangeEndInFrames - cursor)) {
|
||||
frameCount = (pDataSourceBase->rangeEndInFrames - cursor);
|
||||
rangeEnd = pDataSourceBase->rangeEndInFrames;
|
||||
|
||||
/* If looping, make sure we're within range. */
|
||||
if (loop) {
|
||||
if (pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) {
|
||||
rangeEnd = ma_min(rangeEnd, pDataSourceBase->rangeBegInFrames + pDataSourceBase->loopEndInFrames);
|
||||
}
|
||||
}
|
||||
|
||||
if (frameCount > (rangeEnd - cursor) && rangeEnd != ~((ma_uint64)0)) {
|
||||
frameCount = (rangeEnd - cursor);
|
||||
}
|
||||
|
||||
result = pDataSourceBase->cb.onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead);
|
||||
@@ -43260,7 +43280,7 @@ static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDa
|
||||
}
|
||||
|
||||
/* We need to make sure MA_AT_END is returned if we hit the end of the range. */
|
||||
if (result != MA_AT_END && (cursor + framesRead) == pDataSourceBase->rangeEndInFrames) {
|
||||
if (result != MA_AT_END && (cursor + framesRead) == rangeEnd) {
|
||||
result = MA_AT_END;
|
||||
}
|
||||
|
||||
@@ -43299,7 +43319,7 @@ MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, voi
|
||||
return result;
|
||||
}
|
||||
|
||||
return ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pFramesOut, frameCount, pFramesRead);
|
||||
return ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pFramesOut, frameCount, pFramesRead, loop);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -43322,7 +43342,7 @@ MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, voi
|
||||
break;
|
||||
}
|
||||
|
||||
result = ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pRunningFramesOut, framesRemaining, &framesProcessed);
|
||||
result = ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pRunningFramesOut, framesRemaining, &framesProcessed, loop);
|
||||
totalFramesProcessed += framesProcessed;
|
||||
|
||||
/*
|
||||
@@ -43353,7 +43373,7 @@ MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, voi
|
||||
emptyLoopCounter = 0;
|
||||
}
|
||||
|
||||
if (ma_data_source_seek_to_pcm_frame(pCurrentDataSource, 0) != MA_SUCCESS) {
|
||||
if (ma_data_source_seek_to_pcm_frame(pCurrentDataSource, pCurrentDataSource->loopBegInFrames) != MA_SUCCESS) {
|
||||
break; /* Failed to loop. Abort. */
|
||||
}
|
||||
} else {
|
||||
@@ -43685,31 +43705,65 @@ MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSo
|
||||
|
||||
|
||||
#if defined(MA_EXPERIMENTAL__DATA_LOOPING_AND_CHAINING)
|
||||
MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBeg, ma_uint64 rangeEnd)
|
||||
MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames)
|
||||
{
|
||||
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
||||
ma_result result;
|
||||
ma_uint64 cursor;
|
||||
ma_uint64 loopBegAbsolute;
|
||||
ma_uint64 loopEndAbsolute;
|
||||
|
||||
if (pDataSource == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
if (rangeEnd < rangeBeg) {
|
||||
if (rangeEndInFrames < rangeBegInFrames) {
|
||||
return MA_INVALID_ARGS; /* The end of the range must come after the beginning. */
|
||||
}
|
||||
|
||||
pDataSourceBase->rangeBegInFrames = rangeBeg;
|
||||
pDataSourceBase->rangeEndInFrames = rangeEnd;
|
||||
/*
|
||||
The loop points need to be updated. We'll be storing the loop points relative to the range. We'll update
|
||||
these so that they maintain their absolute positioning. The loop points will then be clamped to the range.
|
||||
*/
|
||||
loopBegAbsolute = pDataSourceBase->loopBegInFrames + pDataSourceBase->rangeBegInFrames;
|
||||
loopEndAbsolute = pDataSourceBase->loopEndInFrames + ((pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) ? pDataSourceBase->rangeBegInFrames : 0);
|
||||
|
||||
pDataSourceBase->rangeBegInFrames = rangeBegInFrames;
|
||||
pDataSourceBase->rangeEndInFrames = rangeEndInFrames;
|
||||
|
||||
/* Make the loop points relative again, and make sure they're clamped to within the range. */
|
||||
if (loopBegAbsolute > pDataSourceBase->rangeBegInFrames) {
|
||||
pDataSourceBase->loopBegInFrames = loopBegAbsolute - pDataSourceBase->rangeBegInFrames;
|
||||
} else {
|
||||
pDataSourceBase->loopBegInFrames = 0;
|
||||
}
|
||||
|
||||
if (pDataSourceBase->loopBegInFrames > pDataSourceBase->rangeEndInFrames) {
|
||||
pDataSourceBase->loopBegInFrames = pDataSourceBase->rangeEndInFrames;
|
||||
}
|
||||
|
||||
/* Only need to update the loop end point if it's not -1. */
|
||||
if (loopEndAbsolute != ~((ma_uint64)0)) {
|
||||
if (loopEndAbsolute > pDataSourceBase->rangeBegInFrames) {
|
||||
pDataSourceBase->loopEndInFrames = loopEndAbsolute - pDataSourceBase->rangeBegInFrames;
|
||||
} else {
|
||||
pDataSourceBase->loopEndInFrames = 0;
|
||||
}
|
||||
|
||||
if (pDataSourceBase->loopEndInFrames > pDataSourceBase->rangeEndInFrames && pDataSourceBase->loopEndInFrames) {
|
||||
pDataSourceBase->loopEndInFrames = pDataSourceBase->rangeEndInFrames;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* If the new range is past the current cursor position we need to seek to it. */
|
||||
result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor);
|
||||
if (result == MA_SUCCESS) {
|
||||
/* Seek to within range. Note that our seek positions here are relative to the new range. */
|
||||
if (cursor < rangeBeg) {
|
||||
if (cursor < rangeBegInFrames) {
|
||||
ma_data_source_seek_to_pcm_frame(pDataSource, 0);
|
||||
} else if (cursor > rangeEnd) {
|
||||
ma_data_source_seek_to_pcm_frame(pDataSource, rangeEnd - rangeBeg);
|
||||
} else if (cursor > rangeEndInFrames) {
|
||||
ma_data_source_seek_to_pcm_frame(pDataSource, rangeEndInFrames - rangeBegInFrames);
|
||||
}
|
||||
} else {
|
||||
/* We failed to get the cursor position. Probably means the data source has no notion of a cursor such a noise data source. Just pretend the seeking worked. */
|
||||
@@ -43718,6 +43772,67 @@ MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSou
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
MA_API void ma_data_source_get_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames)
|
||||
{
|
||||
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
||||
|
||||
if (pDataSource == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pRangeBegInFrames != NULL) {
|
||||
*pRangeBegInFrames = pDataSourceBase->rangeBegInFrames;
|
||||
}
|
||||
|
||||
if (pRangeEndInFrames != NULL) {
|
||||
*pRangeEndInFrames = pDataSourceBase->rangeEndInFrames;
|
||||
}
|
||||
}
|
||||
|
||||
MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames)
|
||||
{
|
||||
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
||||
|
||||
if (pDataSource == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
if (loopEndInFrames < loopBegInFrames) {
|
||||
return MA_INVALID_ARGS; /* The end of the loop point must come after the beginning. */
|
||||
}
|
||||
|
||||
if (loopEndInFrames > pDataSourceBase->rangeEndInFrames && loopEndInFrames != ~((ma_uint64)0)) {
|
||||
return MA_INVALID_ARGS; /* The end of the loop point must not go beyond the range. */
|
||||
}
|
||||
|
||||
pDataSourceBase->loopBegInFrames = loopBegInFrames;
|
||||
pDataSourceBase->loopEndInFrames = loopEndInFrames;
|
||||
|
||||
/* The end cannot exceed the range. */
|
||||
if (pDataSourceBase->loopEndInFrames > (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames) && pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) {
|
||||
pDataSourceBase->loopEndInFrames = (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames);
|
||||
}
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
MA_API void ma_data_source_get_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames)
|
||||
{
|
||||
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
||||
|
||||
if (pDataSource == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pLoopBegInFrames != NULL) {
|
||||
*pLoopBegInFrames = pDataSourceBase->loopBegInFrames;
|
||||
}
|
||||
|
||||
if (pLoopEndInFrames != NULL) {
|
||||
*pLoopEndInFrames = pDataSourceBase->loopEndInFrames;
|
||||
}
|
||||
}
|
||||
|
||||
MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource)
|
||||
{
|
||||
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
||||
@@ -43731,6 +43846,17 @@ MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
MA_API ma_data_source* ma_data_source_get_current(ma_data_source* pDataSource)
|
||||
{
|
||||
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
||||
|
||||
if (pDataSource == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pDataSourceBase->pCurrent;
|
||||
}
|
||||
|
||||
MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource)
|
||||
{
|
||||
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
||||
@@ -43744,6 +43870,17 @@ MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_so
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
MA_API ma_data_source* ma_data_source_get_next(ma_data_source* pDataSource)
|
||||
{
|
||||
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
||||
|
||||
if (pDataSource == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pDataSourceBase->pNext;
|
||||
}
|
||||
|
||||
MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext)
|
||||
{
|
||||
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
||||
@@ -43756,6 +43893,17 @@ MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, m
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(ma_data_source* pDataSource)
|
||||
{
|
||||
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
||||
|
||||
if (pDataSource == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pDataSourceBase->onGetNext;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user