mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-24 09:14:04 +02:00
Fix a bug with pitch shifting.
This bug results in the first period of samples being pitched at 1.0 regardless of whether or not ma_sound_set_pitch() had been called. What happens is that the first period is processed at a rate of 1.0, and then the pitch is applied after the fact. Then, the next period comes along and resamples at the pitch set by ma_sound_set_pitch() which results in a harsh sounding glitch.
This commit is contained in:
+25
-16
@@ -10076,6 +10076,17 @@ static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float
|
|||||||
pSound->seekTarget = MA_SEEK_TARGET_NONE;
|
pSound->seekTarget = MA_SEEK_TARGET_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
We want to update the pitch once. For sounds, this can be either at the start or at the end. If
|
||||||
|
we don't force this to only ever be updating once, we could end up in a situation where
|
||||||
|
retrieving the required input frame count ends up being different to what we actually retrieve.
|
||||||
|
What could happen is that the required input frame count is calculated, the pitch is update,
|
||||||
|
and then this processing function is called resulting in a different number of input frames
|
||||||
|
being processed. Do not call this in ma_engine_node_process_pcm_frames__general() or else
|
||||||
|
you'll hit the aforementioned bug.
|
||||||
|
*/
|
||||||
|
ma_engine_node_update_pitch_if_required(&pSound->engineNode);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
For the convenience of the caller, we're doing to allow data sources to use non-floating-point formats and channel counts that differ
|
For the convenience of the caller, we're doing to allow data sources to use non-floating-point formats and channel counts that differ
|
||||||
from the main engine.
|
from the main engine.
|
||||||
@@ -10141,33 +10152,31 @@ static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float
|
|||||||
}
|
}
|
||||||
|
|
||||||
*pFrameCountOut = totalFramesRead;
|
*pFrameCountOut = totalFramesRead;
|
||||||
|
|
||||||
/*
|
|
||||||
We want to update the pitch once, at the *end* of processing. If we don't force this to only
|
|
||||||
ever be updating once, we could end up in a situation where retrieving the required input frame
|
|
||||||
count ends up being different to what we actually retrieve. What could happen is that the
|
|
||||||
required input frame count is calculated, the pitch is update, and then this processing function
|
|
||||||
is called resulting in a different number of input frames being processed. Do not call this in
|
|
||||||
ma_engine_node_process_pcm_frames__general() or else you'll hit the aforementioned bug.
|
|
||||||
*/
|
|
||||||
ma_engine_node_update_pitch_if_required(&pSound->engineNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
static void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
||||||
{
|
{
|
||||||
/* For groups, the input data has already been read and we just need to apply the effect. */
|
|
||||||
ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The pitch can only ever be updated once. We cannot update it in ma_engine_node_process_pcm_frames__general()
|
Make sure the pitch is updated before trying to read anything. It's important that this is done
|
||||||
because otherwise we'll introduce a subtle bug in ma_engine_node_process_pcm_frames__sound().
|
only once and not in ma_engine_node_process_pcm_frames__general(). The reason for this is that
|
||||||
|
ma_engine_node_process_pcm_frames__general() will call ma_engine_node_get_required_input_frame_count(),
|
||||||
|
and if another thread modifies the pitch just after that call it can result in a glitch due to
|
||||||
|
the input rate changing.
|
||||||
*/
|
*/
|
||||||
ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode);
|
ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode);
|
||||||
|
|
||||||
|
/* For groups, the input data has already been read and we just need to apply the effect. */
|
||||||
|
ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ma_uint32 ma_engine_node_get_required_input_frame_count__group(ma_node* pNode, ma_uint32 outputFrameCount)
|
static ma_uint32 ma_engine_node_get_required_input_frame_count__group(ma_node* pNode, ma_uint32 outputFrameCount)
|
||||||
{
|
{
|
||||||
ma_uint64 result = ma_engine_node_get_required_input_frame_count((ma_engine_node*)pNode, outputFrameCount);
|
ma_uint64 result;
|
||||||
|
|
||||||
|
/* Our pitch will affect this calculation. We need to update it. */
|
||||||
|
ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode);
|
||||||
|
|
||||||
|
result = ma_engine_node_get_required_input_frame_count((ma_engine_node*)pNode, outputFrameCount);
|
||||||
if (result > 0xFFFFFFFF) {
|
if (result > 0xFFFFFFFF) {
|
||||||
result = 0xFFFFFFFF; /* Will never happen because miniaudio will only ever process in relatively small chunks. */
|
result = 0xFFFFFFFF; /* Will never happen because miniaudio will only ever process in relatively small chunks. */
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user