I will try to make this description as short as possible…
Currently, I am working on a project in the field of dynamic binaural rendering where a mono input gets processed by multiple (12 in total) processors, each consisting mainly of a DelayLine and a Convolution object. Further processing (HRTF-related) is done by code from an open source project, which my code is building upon.
To mention the problem first: The adjustment of the delay times of multiple DelayLines causes a crackling distortion in the output. Even if they only get updated once (results in one “crack”). When the delay times get updated continuosly, the distorion becomes heavier.
In order to adjust the respective delay times of each processor, depending on a “listener position” retrieved from an XY-Pad, I check for an updated position within a timer callback defined in the PluginProcessor. The timer runs with an interval of 80ms. If there is an updated position (PluginEditor sets a flag) I open a thread and execute a dedicated function for the adjustments
// check for updated Listener Position and update delay processors and dfp
if (updateDelayProcessorParams) {
// Lambda function to use in thread call
auto extrapolateFunction = [this]() {
extrapolate();
};
/* update delays (TOAs), amplitudes*/
try {
std::thread extrapolThread(extrapolateFunction);
extrapolThread.detach();
}
catch (const std::exception& exception) {
std::cout << "Could not create thread" << exception.what() << std::endl;
}
/* Reset the flag */
updateDelayProcessorParams = false;
}
The code for the extrapolate function is this:
void PluginProcessor::extrapolate()
{
Vector3D<float> translation(*listenerPositionCurrent - *listenerPositionInit);
for (auto it = delayProcessors.begin(); it != delayProcessors.end(); ++it)
{
auto& dp = *it;
float x = dp.initCoords.x;
float y = dp.initCoords.y;
float z = dp.initCoords.z;
float aziExtrap, eleExtrap, disExtrap;
// get index of current DelayProcessor instance
int dpIndex = std::distance(delayProcessors.begin(), it);
// initial cartesian coordinates already calculated in initDelayProcessors()
// translate x, y & z by the translation vector above
x -= translation.x;
y -= translation.y;
z -= translation.z;
// convert shifted Cartesian coordinates back to spherical coordinates
CoordHelpers::cartesian2sphericalBnf(x, y, z, aziExtrap, eleExtrap, disExtrap);
// caused output to stutter (probably blocked the audio processing?)
// when moving the positioner thumb too much
//std::lock_guard<std::mutex> lock(extrapolMutex);
// Update DelayProcessors with new spherical values
// includes adaption of delay time (toa) and amplitude in setDistance()
dp.setAzimuth(aziExtrap);
dp.setElevation(eleExtrap);
dp.setDistance(disExtrap);
// Update Diffuse Field Processor delay to match with onset of direct sound
if (dpIndex == 0)
dfp.setDelayByDistance(disExtrap);
// Update BNF spherical coordinates
binauraliser_setSourceAzi_deg(hBin, dpIndex, aziExtrap);
binauraliser_setSourceElev_deg(hBin, dpIndex, eleExtrap);
binauraliserNF_setSourceDist_m(hBin, dpIndex, disExtrap);
// fetch DelayProcessor's gainFactor in PluginProcessor
// and pass on to binauraliser_setSourceGain()
binauraliser_setSourceGain(hBin, dpIndex, dp.getGainFactor());
}
// tell Editor to call pannerView::refreshPanView()
setRefreshWindow(true);
return;
}
What I have tried already is opening a thread for the execution, as you have seen in the code above. Also I tried introducing a mutex every time in the setter function for the delay of every processor. Another approach, as seen in the code above as well, but commented out, was introducing a mutex in the extrapolate() function itfself. This caused a weird behavior of its own, the audio sounded “chunked”, probably because the mutex kept locking everything so consistently, that the updated processors could not be accessed by the audio thread (?). I tried increasing the interval of the timer mentioned in the beginning, but to no success.
When I first created a small, basic “proof-of-concept” version with just one DelayLine it worked without the distortions. I could move a slider around and everything sounded fine.
I have read everything related to this topic on this forum, that I could find, but most of the threads were 3-4 years old and dealed mainly with interpolation types. I assume that the dsp::DelayLineInterpolation Types were not present at that time. For my project I am using the Lagrange3rd interpolation type on every delay line that I have set up. Other people suggested using SmoothedValue types for their delay times, but the people to whom this was recommended said that this had not helped.
Has anyone in the meantime dealt with this and has a recommendation about how to resolve these kind of distortions? Is there any kind of efficiency improvement to be done here or in general when dealing with DelayLine objects? If more code or general info needs to be provided I am happy to do so.