Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: juce_LookupTable.h can fail on a negative index #1354

Open
1 task done
josmithiii opened this issue Feb 21, 2024 · 0 comments
Open
1 task done

[Bug]: juce_LookupTable.h can fail on a negative index #1354

josmithiii opened this issue Feb 21, 2024 · 0 comments

Comments

@josmithiii
Copy link

Detailed steps on how to reproduce the bug

Just make sound in the FoleysSynth example in Plugin GUI Magic: https://github.com/ffAudio/foleys_gui_magic
The first sinusoid sample has phase -pi which becomes minInputValue (float) -3.14159274, and this is too negative.
It's basically just normal roundoff error in single-precision floating-point, but it leads to the index -0.00000620965511,
which triggers an assertion failure. Here is my workaround (fix?):

diff --git a/modules/juce_dsp/maths/juce_LookupTable.h b/modules/juce_dsp/maths/juce_LookupTable.h
index d4b455b1f..0580fcb93 100644
--- a/modules/juce_dsp/maths/juce_LookupTable.h
+++ b/modules/juce_dsp/maths/juce_LookupTable.h
@@ -231,7 +231,9 @@ public:
     FloatType processSampleUnchecked (FloatType value) const noexcept
     {
         jassert (value >= minInputValue && value <= maxInputValue);
-        return lookupTable[scaler * value + offset];
+        auto index = scaler * value + offset;
+        int roundedIndex = static_cast<int>(std::round(index));
+        return lookupTable[roundedIndex];
     }

     //==============================================================================
@@ -248,12 +250,15 @@ public:

         @see processSampleUnchecked, operator(), operator[]
     */
+
     FloatType processSample (FloatType value) const noexcept
     {
-        auto index = scaler * jlimit (minInputValue, maxInputValue, value) + offset;
-        jassert (isPositiveAndBelow (index, FloatType (lookupTable.getNumPoints())));
-
-        return lookupTable[index];
+        jassert (value >= minInputValue && value <= maxInputValue);
+        auto index = scaler * value + offset;
+        int roundedIndex = static_cast<int>(std::round(index));
+        int limitedIndex = jlimit<int>(0, int(lookupTable.getNumPoints()) - 1, roundedIndex);
+        jassert(roundedIndex == limitedIndex);
+        return lookupTable[limitedIndex];
     }

What is the expected behaviour?

It should be impossible to access addresses outside of the LookupTable input range. They should clip.

Operating systems

macOS

What versions of the operating systems?

Mac OS Sonoma 14.2.1

Architectures

ARM

Stacktrace

(lldb) bt
* thread #12, name = 'com.apple.audio.IOThread.client', stop reason = breakpoint 1.1
  * frame #0: 0x0000000102678a10 FoleysSynth`juce::dsp::LookupTableTransform<float>::processSample(this=0x000060000239c4b0, value=-3.14159274) const at juce_LookupTable.h:261:28
    frame #1: 0x00000001026788c4 FoleysSynth`juce::dsp::LookupTableTransform<float>::operator()(this=0x000060000239c4b0, index=-3.14159274) const at juce_LookupTable.h:269:74
    frame #2: 0x0000000102678898 FoleysSynth`juce::dsp::Oscillator<float>::initialise(this=0x0000600001a102c8, x=-3.14159274)> const&, unsigned long)::'lambda'(float)::operator()(float) const at juce_Oscillator.h:72:58
    frame #3: 0x0000000102678868 FoleysSynth`decltype(std::declval<juce::dsp::Oscillator<float>::initialise(std::__1::function<float (float)> const&, unsigned long)::'lambda'(float)&>()(std::declval<float>())) std::__1::__invoke[abi:v160006]<juce::dsp::Oscillator<float>::initialise(std::__1::function<float (float)> const&, unsigned long)::'lambda'(float)&, float>(__f=0x0000600001a102c8, __args=0x000000016dd25f84) at invoke.h:394:23
    frame #4: 0x0000000102678814 FoleysSynth`float std::__1::__invoke_void_return_wrapper<float, false>::__call<juce::dsp::Oscillator<float>::initialise(std::__1::function<float (float)> const&, unsigned long)::'lambda'(float)&, float>(__args=0x0000600001a102c8, __args=0x000000016dd25f84) at invoke.h:478:16
    frame #5: 0x00000001026787e8 FoleysSynth`std::__1::__function::__alloc_func<juce::dsp::Oscillator<float>::initialise(std::__1::function<float (float)> const&, unsigned long)::'lambda'(float), std::__1::allocator<juce::dsp::Oscillator<float>::initialise(std::__1::function<float (float)> const&, unsigned long)::'lambda'(float)>, float (float)>::operator()[abi:v160006](this=0x0000600001a102c8, __arg=0x000000016dd25f84) at function.h:185:16
    frame #6: 0x0000000102677634 FoleysSynth`std::__1::__function::__func<juce::dsp::Oscillator<float>::initialise(std::__1::function<float (float)> const&, unsigned long)::'lambda'(float), std::__1::allocator<juce::dsp::Oscillator<float>::initialise(std::__1::function<float (float)> const&, unsigned long)::'lambda'(float)>, float (float)>::operator()(this=0x0000600001a102c0, __arg=0x000000016dd25f84) at function.h:356:12
    frame #7: 0x000000010267c14c FoleysSynth`std::__1::__function::__value_func<float (float)>::operator()[abi:v160006](this=0x0000600001a102c0, __args=0x000000016dd25f84) const at function.h:510:16
    frame #8: 0x000000010267bf14 FoleysSynth`std::__1::function<float (float)>::operator()(this=0x0000600001a102c0, __arg=-3.14159274) const at function.h:1156:12
    frame #9: 0x000000010267b914 FoleysSynth`void juce::dsp::Oscillator<float>::process<juce::dsp::ProcessContextReplacing<float>>(this=0x0000600001a102c0, context=0x000000016dd26140) at juce_Oscillator.h:220:39
    frame #10: 0x000000010267b228 FoleysSynth`void juce::dsp::ProcessorChain<juce::dsp::Oscillator<float>, juce::dsp::Gain<float>>::processOne<juce::dsp::ProcessContextReplacing<float>, juce::dsp::Oscillator<float>, 0ul>(this=0x0000600001a102c0, context=0x000000016dd262e0, proc=0x0000600001a102c0, (null)=integral_constant<unsigned long, 0UL> @ 0x000000016dd2618f) at juce_ProcessorChain.h:108:18
    frame #11: 0x000000010267b16c FoleysSynth`auto void juce::dsp::ProcessorChain<juce::dsp::Oscillator<float>, juce::dsp::Gain<float>>::process<juce::dsp::ProcessContextReplacing<float>>(this=0x000000016dd26240, proc=0x0000600001a102c0, index=integral_constant<unsigned long, 0UL> @ 0x000000016dd261bf)::'lambda'(juce::dsp::ProcessContextReplacing<float>&, auto)::operator()<juce::dsp::Oscillator<float>, std::__1::integral_constant<unsigned long, 0ul>>(juce::dsp::ProcessContextReplacing<float>&, auto) const at juce_ProcessorChain.h:95:92
    frame #12: 0x000000010267b11c FoleysSynth`void juce::dsp::detail::forEachInTuple<void juce::dsp::ProcessorChain<juce::dsp::Oscillator<float>, juce::dsp::Gain<float>>::process<juce::dsp::ProcessContextReplacing<float>>(juce::dsp::ProcessContextReplacing<float> const&)::'lambda'(juce::dsp::ProcessContextReplacing<float>&, auto), std::__1::tuple<juce::dsp::Oscillator<float>, juce::dsp::Gain<float>>&, 0ul, 1ul>(fn=0x000000016dd26240, tuple=size=2, (null)=std::__1::index_sequence<0UL, 1UL> @ 0x000000016dd261ff) at juce_ProcessorChain.h:39:10
    frame #13: 0x000000010267b0e0 FoleysSynth`void juce::dsp::detail::forEachInTuple<void juce::dsp::ProcessorChain<juce::dsp::Oscillator<float>, juce::dsp::Gain<float>>::process<juce::dsp::ProcessContextReplacing<float>>(juce::dsp::ProcessContextReplacing<float> const&)::'lambda'(juce::dsp::ProcessContextReplacing<float>&, auto), std::__1::tuple<juce::dsp::Oscillator<float>, juce::dsp::Gain<float>>&>(fn=0x000000016dd26240, tuple=size=2) at juce_ProcessorChain.h:48:9
    frame #14: 0x000000010266db4c FoleysSynth`void juce::dsp::ProcessorChain<juce::dsp::Oscillator<float>, juce::dsp::Gain<float>>::process<juce::dsp::ProcessContextReplacing<float>>(this=0x0000600001a102c0, context=0x000000016dd262e0) at juce_ProcessorChain.h:95:9
    frame #15: 0x000000010266d71c FoleysSynth`FoleysSynth::FoleysVoice::renderNextBlock(this=0x000000012e04d800, outputBuffer=0x000000016dd26688, startSample=0, numSamples=512) at FoleysSynth.cpp:180:22
    frame #16: 0x00000001027e8b54 FoleysSynth`juce::Synthesiser::renderVoices(this=0x000000012e850d08, buffer=0x000000016dd26688, startSample=0, numSamples=512) at juce_Synthesiser.cpp:245:16
    frame #17: 0x00000001027e6d18 FoleysSynth`void juce::Synthesiser::processNextBlock<float>(this=0x000000012e850d08, outputAudio=0x000000016dd26688, midiData=0x0000000138818cd8, startSample=0, numSamples=512) at juce_Synthesiser.cpp:188:17
    frame #18: 0x00000001027e8a80 FoleysSynth`juce::Synthesiser::renderNextBlock(this=0x000000012e850d08, outputAudio=0x000000016dd26688, inputMidi=0x0000000138818cd8, startSample=0, numSamples=512) at juce_Synthesiser.cpp:233:5
    frame #19: 0x0000000102683334 FoleysSynth`FoleysSynthAudioProcessor::processBlock(this=0x000000012e850400, buffer=0x000000016dd26688, midiMessages=0x0000000138818cd8) at PluginProcessor.cpp:112:17

Plug-in formats (if applicable)

Standalone

Plug-in host applications (DAWs) (if applicable)

No response

Testing on the develop branch

The bug is present on the develop branch

Code of Conduct

  • I agree to follow the Code of Conduct
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant