Fix some node timing errors.

This commit fixes a bug relating to nodes with a scheduled start/stop
time. Whether or not the node is considered started or stopped is being
incorrectly reported by `ma_node_get_state_by_time_range()`.

Another issue is fixed in `ma_node_read_pcm_frames()`, which is related
to the fix above, where the frame count can underflow thereby resulting
in a crash.

Public issue https://github.com/mackron/miniaudio/issues/969
This commit is contained in:
David Reid
2026-01-06 13:05:40 +10:00
parent 3b4e87848b
commit 111d620c63
+19 -9
View File
@@ -74965,12 +74965,12 @@ MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_ui
its start time not having been reached yet. Also, the stop time may have also been reached in its start time not having been reached yet. Also, the stop time may have also been reached in
which case it'll be considered stopped. which case it'll be considered stopped.
*/ */
if (ma_node_get_state_time(pNode, ma_node_state_started) > globalTimeBeg) { if (ma_node_get_state_time(pNode, ma_node_state_stopped) < globalTimeBeg) {
return ma_node_state_stopped; /* Start time has not yet been reached. */ return ma_node_state_stopped; /* End time is before the start of the range. */
} }
if (ma_node_get_state_time(pNode, ma_node_state_stopped) <= globalTimeEnd) { if (ma_node_get_state_time(pNode, ma_node_state_started) > globalTimeEnd) {
return ma_node_state_stopped; /* Stop time has been reached. */ return ma_node_state_stopped; /* Start time is after the end of the range. */
} }
/* Getting here means the node is marked as started and is within its start/stop times. */ /* Getting here means the node is marked as started and is within its start/stop times. */
@@ -75050,14 +75050,14 @@ static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusInde
return MA_INVALID_ARGS; /* Invalid output bus index. */ return MA_INVALID_ARGS; /* Invalid output bus index. */
} }
globalTimeBeg = globalTime;
globalTimeEnd = globalTime + frameCount;
/* Don't do anything if we're in a stopped state. */ /* Don't do anything if we're in a stopped state. */
if (ma_node_get_state_by_time_range(pNode, globalTime, globalTime + frameCount) != ma_node_state_started) { if (ma_node_get_state_by_time_range(pNode, globalTimeBeg, globalTimeEnd) != ma_node_state_started) {
return MA_SUCCESS; /* We're in a stopped state. This is not an error - we just need to not read anything. */ return MA_SUCCESS; /* We're in a stopped state. This is not an error - we just need to not read anything. */
} }
globalTimeBeg = globalTime;
globalTimeEnd = globalTime + frameCount;
startTime = ma_node_get_state_time(pNode, ma_node_state_started); startTime = ma_node_get_state_time(pNode, ma_node_state_started);
stopTime = ma_node_get_state_time(pNode, ma_node_state_stopped); stopTime = ma_node_get_state_time(pNode, ma_node_state_stopped);
@@ -75070,11 +75070,16 @@ static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusInde
therefore need to offset it by a number of frames to accommodate. The same thing applies for therefore need to offset it by a number of frames to accommodate. The same thing applies for
the stop time. the stop time.
*/ */
timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(globalTimeEnd - startTime) : 0; timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(startTime - globalTimeBeg) : 0;
timeOffsetEnd = (globalTimeEnd > stopTime) ? (ma_uint32)(globalTimeEnd - stopTime) : 0; timeOffsetEnd = (globalTimeEnd > stopTime) ? (ma_uint32)(globalTimeEnd - stopTime) : 0;
/* Trim based on the start offset. We need to silence the start of the buffer. */ /* Trim based on the start offset. We need to silence the start of the buffer. */
if (timeOffsetBeg > 0) { if (timeOffsetBeg > 0) {
MA_ASSERT(timeOffsetBeg <= frameCount);
if (timeOffsetBeg > frameCount) {
timeOffsetBeg = frameCount;
}
ma_silence_pcm_frames(pFramesOut, timeOffsetBeg, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex)); ma_silence_pcm_frames(pFramesOut, timeOffsetBeg, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex));
pFramesOut += timeOffsetBeg * ma_node_get_output_channels(pNode, outputBusIndex); pFramesOut += timeOffsetBeg * ma_node_get_output_channels(pNode, outputBusIndex);
frameCount -= timeOffsetBeg; frameCount -= timeOffsetBeg;
@@ -75082,6 +75087,11 @@ static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusInde
/* Trim based on the end offset. We don't need to silence the tail section because we'll just have a reduced value written to pFramesRead. */ /* Trim based on the end offset. We don't need to silence the tail section because we'll just have a reduced value written to pFramesRead. */
if (timeOffsetEnd > 0) { if (timeOffsetEnd > 0) {
MA_ASSERT(timeOffsetEnd <= frameCount);
if (timeOffsetEnd > frameCount) {
timeOffsetEnd = frameCount;
}
frameCount -= timeOffsetEnd; frameCount -= timeOffsetEnd;
} }