| 740 | using Processing::Processing; |
| 741 | |
| 742 | void setCoefficients(const Coefficients& coefficients) |
| 743 | { |
| 744 | auto& music = getMusic(); |
| 745 | |
| 746 | struct State |
| 747 | { |
| 748 | float xnz1{}; |
| 749 | float xnz2{}; |
| 750 | float ynz1{}; |
| 751 | float ynz2{}; |
| 752 | }; |
| 753 | |
| 754 | // We use a mutable lambda to tie the lifetime of the state and coefficients to the lambda itself |
| 755 | // This is necessary since the Echo object will be destroyed before the Music object |
| 756 | // While the Music object exists, it is possible that the audio engine will try to call |
| 757 | // this lambda hence we need to always have usable coefficients and state until the Music and the |
| 758 | // associated lambda are destroyed |
| 759 | music.setEffectProcessor( |
| 760 | [coefficients, |
| 761 | enabled = getEnabled(), |
| 762 | state = std::vector<State>()](const float* inputFrames, |
| 763 | unsigned int& inputFrameCount, |
| 764 | float* outputFrames, |
| 765 | unsigned int& outputFrameCount, |
| 766 | unsigned int frameChannelCount) mutable |
| 767 | { |
| 768 | // IMPORTANT: The channel count of the audio engine currently sourcing data from this sound |
| 769 | // will always be provided in frameChannelCount, this can be different from the channel count |
| 770 | // of the audio source so make sure to size your buffers according to the engine and not the source |
| 771 | // Ensure we have as many state objects as the audio engine has channels |
| 772 | if (state.size() < frameChannelCount) |
| 773 | state.resize(frameChannelCount - state.size()); |
| 774 | |
| 775 | for (auto frame = 0u; frame < outputFrameCount; ++frame) |
| 776 | { |
| 777 | for (auto channel = 0u; channel < frameChannelCount; ++channel) |
| 778 | { |
| 779 | auto& channelState = state[channel]; |
| 780 | |
| 781 | const auto xn = inputFrames ? inputFrames[channel] : 0.f; // Read silence if no input data available |
| 782 | const auto yn = coefficients.a0 * xn + coefficients.a1 * channelState.xnz1 + |
| 783 | coefficients.a2 * channelState.xnz2 - coefficients.b1 * channelState.ynz1 - |
| 784 | coefficients.b2 * channelState.ynz2; |
| 785 | |
| 786 | channelState.xnz2 = channelState.xnz1; |
| 787 | channelState.xnz1 = xn; |
| 788 | channelState.ynz2 = channelState.ynz1; |
| 789 | channelState.ynz1 = yn; |
| 790 | |
| 791 | outputFrames[channel] = *enabled ? yn : xn; |
| 792 | } |
| 793 | |
| 794 | inputFrames += (inputFrames ? frameChannelCount : 0u); |
| 795 | outputFrames += frameChannelCount; |
| 796 | } |
| 797 | |
| 798 | // We processed data 1:1 |
| 799 | inputFrameCount = outputFrameCount; |
nothing calls this directly
no test coverage detected