From 1e4533847b76b209cdf5e0ad1f701b67e3fc29e9 Mon Sep 17 00:00:00 2001 From: michal Date: Tue, 17 Jun 2025 10:19:38 +0200 Subject: [PATCH 1/3] feat: convolver impl --- .../src/examples/Oscillator/Oscillator.tsx | 51 +- .../HostObjects/BaseAudioContextHostObject.h | 9 + .../HostObjects/ConvolverNodeHostObject.h | 56 + .../cpp/audioapi/core/BaseAudioContext.cpp | 8 + .../cpp/audioapi/core/BaseAudioContext.h | 2 + .../audioapi/core/effects/ConvolverNode.cpp | 95 ++ .../cpp/audioapi/core/effects/ConvolverNode.h | 38 + .../common/cpp/audioapi/dsp/AudioUtils.cpp | 2 +- .../common/cpp/audioapi/dsp/Convolver.cpp | 189 +++ .../common/cpp/audioapi/dsp/Convolver.h | 33 + .../common/cpp/audioapi/dsp/FFT.cpp | 10 +- .../cpp/audioapi/libs/FFTConvolver/.gitignore | 2 + .../audioapi/libs/FFTConvolver/AudioFFT.cpp | 1120 +++++++++++++++++ .../cpp/audioapi/libs/FFTConvolver/AudioFFT.h | 168 +++ .../audioapi/libs/FFTConvolver/COPYING.txt | 18 + .../libs/FFTConvolver/FFTConvolver.cpp | 206 +++ .../audioapi/libs/FFTConvolver/FFTConvolver.h | 104 ++ .../cpp/audioapi/libs/FFTConvolver/README.md | 10 + .../FFTConvolver/TwoStageFFTConvolver.cpp | 239 ++++ .../libs/FFTConvolver/TwoStageFFTConvolver.h | 129 ++ .../audioapi/libs/FFTConvolver/Utilities.cpp | 117 ++ .../audioapi/libs/FFTConvolver/Utilities.h | 355 ++++++ .../audioapi/libs/FFTConvolver/test/Test.cpp | 342 +++++ packages/react-native-audio-api/src/api.ts | 1 + .../src/core/BaseAudioContext.ts | 5 + .../src/core/ConvolverNode.ts | 35 + .../react-native-audio-api/src/interfaces.ts | 6 + 27 files changed, 3331 insertions(+), 19 deletions(-) create mode 100644 packages/react-native-audio-api/common/cpp/audioapi/HostObjects/ConvolverNodeHostObject.h create mode 100644 packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.cpp create mode 100644 packages/react-native-audio-api/common/cpp/audioapi/core/effects/ConvolverNode.h create mode 100644 packages/react-native-audio-api/common/cpp/audioapi/dsp/Convolver.cpp create mode 100644 packages/react-native-audio-api/common/cpp/audioapi/dsp/Convolver.h create mode 100644 packages/react-native-audio-api/common/cpp/audioapi/libs/FFTConvolver/.gitignore create mode 100755 packages/react-native-audio-api/common/cpp/audioapi/libs/FFTConvolver/AudioFFT.cpp create mode 100755 packages/react-native-audio-api/common/cpp/audioapi/libs/FFTConvolver/AudioFFT.h create mode 100644 packages/react-native-audio-api/common/cpp/audioapi/libs/FFTConvolver/COPYING.txt create mode 100755 packages/react-native-audio-api/common/cpp/audioapi/libs/FFTConvolver/FFTConvolver.cpp create mode 100755 packages/react-native-audio-api/common/cpp/audioapi/libs/FFTConvolver/FFTConvolver.h create mode 100644 packages/react-native-audio-api/common/cpp/audioapi/libs/FFTConvolver/README.md create mode 100755 packages/react-native-audio-api/common/cpp/audioapi/libs/FFTConvolver/TwoStageFFTConvolver.cpp create mode 100755 packages/react-native-audio-api/common/cpp/audioapi/libs/FFTConvolver/TwoStageFFTConvolver.h create mode 100644 packages/react-native-audio-api/common/cpp/audioapi/libs/FFTConvolver/Utilities.cpp create mode 100644 packages/react-native-audio-api/common/cpp/audioapi/libs/FFTConvolver/Utilities.h create mode 100755 packages/react-native-audio-api/common/cpp/audioapi/libs/FFTConvolver/test/Test.cpp create mode 100644 packages/react-native-audio-api/src/core/ConvolverNode.ts diff --git a/apps/common-app/src/examples/Oscillator/Oscillator.tsx b/apps/common-app/src/examples/Oscillator/Oscillator.tsx index ad32abff4..c1319d557 100644 --- a/apps/common-app/src/examples/Oscillator/Oscillator.tsx +++ b/apps/common-app/src/examples/Oscillator/Oscillator.tsx @@ -1,12 +1,12 @@ import React, { useRef, useState, useEffect, FC } from 'react'; -import { StyleSheet, Text, View, Pressable } from 'react-native'; +import { StyleSheet, Text, View, Pressable, Image } from 'react-native'; import { AudioContext, GainNode, OscillatorNode, StereoPannerNode, } from 'react-native-audio-api'; -import type { OscillatorType } from 'react-native-audio-api'; +import type { OscillatorType, AudioBuffer, ConvolverNode } from 'react-native-audio-api'; import { Container, Slider, Spacer, Button } from '../../components'; import { layout, colors } from '../../styles'; @@ -31,6 +31,35 @@ const Oscillator: FC = () => { const oscillatorRef = useRef(null); const gainRef = useRef(null); const panRef = useRef(null); + const convolverRef = useRef(null); + + useEffect(() => { + const fetchImpulseResponse = async () => { + if (!audioContextRef.current) { + audioContextRef.current = new AudioContext(); + } + + const length = audioContextRef.current.sampleRate * 2; // 2 seconds + const impulse = audioContextRef.current.createBuffer(2, length, audioContextRef.current.sampleRate); + + for (let channel = 0; channel < impulse.numberOfChannels; channel++) { + const channelData = impulse.getChannelData(channel); + for (let i = 0; i < length; i++) { + // Exponentially decay the impulse + channelData[i] = (Math.random() * 2 - 1) * Math.pow(1 - i / length, 2); + } + } + + convolverRef.current = audioContextRef.current?.createConvolver(); + convolverRef.current.buffer = impulse; + } + + fetchImpulseResponse(); + + return () => { + audioContextRef.current?.close(); + }; + }, []); const setup = () => { if (!audioContextRef.current) { @@ -50,7 +79,12 @@ const Oscillator: FC = () => { oscillatorRef.current.connect(gainRef.current); gainRef.current.connect(panRef.current); - panRef.current.connect(audioContextRef.current.destination); + if (convolverRef.current) { + panRef.current.connect(convolverRef.current); + convolverRef.current.connect(audioContextRef.current.destination); + } else { + panRef.current.connect(audioContextRef.current.destination); + } }; const handleGainChange = (newValue: number) => { @@ -91,6 +125,7 @@ const Oscillator: FC = () => { } else { setup(); oscillatorRef.current?.start(0); + oscillatorRef.current?.stop(audioContextRef.current?.currentTime!! + 2); } setIsPlaying((prev) => !prev); @@ -103,16 +138,6 @@ const Oscillator: FC = () => { } }; - useEffect(() => { - if (!audioContextRef.current) { - audioContextRef.current = new AudioContext(); - } - - return () => { - audioContextRef.current?.close(); - }; - }, []); - return (