Skip to content

Commit e16d7ff

Browse files
authored
feat: convolver impl (#533)
1 parent 0db0ddf commit e16d7ff

34 files changed

+968
-38
lines changed

packages/audiodocs/docs/core/base-audio-context.mdx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ Creates [`AudioBufferSourceNode`](/docs/sources/audio-buffer-source-node).
139139

140140
| Parameter | Type | Description |
141141
| :---: | :---: | :---- |
142-
| `pitchCorrection` <Optional /> | [`AudioBufferBaseSourceNodeOptions`](/docs/sources/audio-buffer-source-node#constructor) | Dictionary object that specifies if pitch correction has to be available. |
142+
| `options` <Optional /> | [`AudioBufferBaseSourceNodeOptions`](/docs/sources/audio-buffer-source-node#constructor) | Dictionary object that specifies if pitch correction has to be available. |
143143

144144
#### Returns `AudioBufferSourceNode`.
145145

@@ -149,7 +149,7 @@ Creates [`AudioBufferQueueSourceNode`](/docs/sources/audio-buffer-queue-source-n
149149

150150
| Parameter | Type | Description |
151151
| :---: | :---: | :---- |
152-
| `pitchCorrection` <Optional /> | [`AudioBufferBaseSourceNodeOptions`](/docs/sources/audio-buffer-queue-source-node#constructor) | Dictionary object that specifies if pitch correction has to be available. |
152+
| `options` <Optional /> | [`AudioBufferBaseSourceNodeOptions`](/docs/sources/audio-buffer-queue-source-node#constructor) | Dictionary object that specifies if pitch correction has to be available. |
153153

154154
#### Returns `AudioBufferQueueSourceNode`.
155155

@@ -159,6 +159,23 @@ Creates [`GainNode`](/docs/effects/gain-node).
159159

160160
#### Returns `GainNode`.
161161

162+
163+
### `createConvolver`
164+
165+
Creates [`ConvolverNode`](/docs/effects/convolver-node).
166+
167+
| Parameter | Type | Description |
168+
| :---: | :---: | :---- |
169+
| `options` <Optional /> | [`ConvolverNodeOptions`](/docs/effects/convolver-node#constructor) | Dictionary object that specifies associated buffer and normalization. |
170+
171+
#### Errors
172+
173+
| Error type | Description |
174+
| :---: | :---- |
175+
| `NotSupportedError` | `numOfChannels` of buffer is not 1, 2 or 4. |
176+
177+
#### Returns `ConvolverNode`.
178+
162179
### `createOscillator`
163180

164181
Creates [`OscillatorNode`](/docs/sources/oscillator-node).
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
sidebar_position: 5
3+
---
4+
5+
import AudioNodePropsTable from "@site/src/components/AudioNodePropsTable"
6+
7+
# ConvolverNode
8+
9+
The `ConvolverNode` interface represents a linear convolution effect, that can be applied to a signal given an impulse response.
10+
This is the easiest way to achieve `echo` or [`reverb`](https://en.wikipedia.org/wiki/Reverb_effect) effects.
11+
12+
#### [`AudioNode`](/docs/core/audio-node#properties) properties
13+
14+
<AudioNodePropsTable numberOfInputs={1} numberOfOutputs={1} channelCount={2} channelCountMode={"clamped-max"} channelInterpretation={"speakers"} />
15+
16+
:::info
17+
Convolver is a node with tail-time, which means, that it continues to output non-silent audio with zero input for the length of the buffer.
18+
:::
19+
20+
## Constructor
21+
22+
[`BaseAudioContext.createConvolver(options: ConvolverNodeOptions)`](/docs/core/base-audio-context#createconvolver)
23+
24+
```jsx
25+
interface ConvolverNodeOptions {
26+
buffer?: AudioBuffer | null; // impulse response
27+
disableNormalization?: boolean; // if normalization of output should be applied, true by default
28+
}
29+
```
30+
31+
## Properties
32+
33+
It inherits all properties from [`AudioNode`](/docs/core/audio-node#properties) and has no individual ones.
34+
35+
:::caution
36+
Linear convolution is a heavy computational process, so if your audio has some weird artefacts that should not be there, try to decrease the duration of impulse response buffer.
37+
:::

packages/audiodocs/docs/other/web-audio-api-coverage.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ sidebar_position: 2
2424
| OscillatorNode ||
2525
| PeriodicWave ||
2626
| StereoPannerNode ||
27+
| ConvolverNode ||
2728
| AudioContext | 🚧 | Available props and methods: `close`, `suspend`, `resume` |
2829
| BaseAudioContext | 🚧 | Available props and methods: `currentTime`, `destination`, `sampleRate`, `state`, `decodeAudioData`, all create methods for available or partially implemented nodes |
2930
| AudioListener ||
@@ -34,7 +35,6 @@ sidebar_position: 2
3435
| AudioWorkletProcessor ||
3536
| ChannelMergerNode ||
3637
| ChannelSplitterNode ||
37-
| ConvolverNode | 🚧 |
3838
| DelayNode ||
3939
| DynamicsCompressorNode ||
4040
| IIRFilterNode ||

packages/audiodocs/src/components/Badges/index.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@ import React from 'react';
22
import styles from './styles.module.css';
33

44
export function Optional({ footnote }) {
5-
return <div className={styles.badge}>Optional{footnote ? '*' : ''}</div>;
5+
return <div className={`${styles.badge} ${styles.basic}`}>Optional{footnote ? '*' : ''}</div>;
66
}
77

88
export function ReadOnly({ footnote }) {
9-
return <div className={styles.badge}>Read only{footnote ? '*' : ''}</div>;
9+
return <div className={`${styles.badge} ${styles.basic}`}>Read only{footnote ? '*' : ''}</div>;
1010
}
1111

1212
export function Overridden({ footnote }) {
13-
return <div className={styles.badge}>Overridden{footnote ? '*' : ''}</div>;
13+
return <div className={`${styles.badge} ${styles.basic}`}>Overridden{footnote ? '*' : ''}</div>;
1414
}
1515

1616
export function OnlyiOS({ footnote }) {
17-
return <div className={styles.badge}>iOS only{footnote ? '*' : ''}</div>;
17+
return <div className={`${styles.badge} ${styles.basic}`}>iOS only{footnote ? '*' : ''}</div>;
1818
}
1919

2020
export function Experimental({ footnote }) {

packages/audiodocs/src/components/Badges/styles.module.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@
99
margin: 0 0.25rem;
1010
}
1111

12+
.basic {
13+
color: var(--swm-off-white);
14+
background-color: var(--swm-blue-light-100);
15+
}
16+
17+
[data-theme='dark'] .basic {
18+
background-color: var(--swm-blue-dark-140);
19+
}
20+
1221
.experimental {
1322
color: var(--swm-off-white);
1423
background-color: var(--swm-purple-light-100);

packages/react-native-audio-api/RNAudioAPI.podspec

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ Pod::Spec.new do |s|
3232
sss.header_dir = "audioapi"
3333
sss.header_mappings_dir = "ios/audioapi"
3434
end
35+
36+
ss.subspec "audioapi_dsp" do |sss|
37+
sss.source_files = "common/cpp/audioapi/dsp/**/*.{cpp}"
38+
sss.header_dir = "audioapi/dsp"
39+
sss.header_mappings_dir = "common/cpp/audioapi/dsp"
40+
sss.compiler_flags = "-O3"
41+
end
3542
end
3643

3744
s.ios.frameworks = 'CoreFoundation', 'CoreAudio', 'AudioToolbox', 'Accelerate', 'MediaPlayer', 'AVFoundation'

packages/react-native-audio-api/android/src/main/cpp/audioapi/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ cmake_minimum_required(VERSION 3.12.0)
33
file(GLOB_RECURSE ANDROID_CPP_SOURCES CONFIGURE_DEPENDS "${ANDROID_CPP_DIR}/audioapi/*.cpp")
44
file(GLOB_RECURSE COMMON_CPP_SOURCES CONFIGURE_DEPENDS "${COMMON_CPP_DIR}/audioapi/*.cpp" "${COMMON_CPP_DIR}/audioapi/*.c")
55

6+
set_source_files_properties(
7+
${COMMON_CPP_SOURCES}/dsp/*.cpp
8+
PROPERTIES
9+
COMPILE_FLAGS "-O3"
10+
)
11+
612
set(INCLUDE_DIR ${COMMON_CPP_DIR}/audioapi/external/include)
713
set(FFMPEG_INCLUDE_DIR ${COMMON_CPP_DIR}/audioapi/external/ffmpeg_include)
814
set(EXTERNAL_DIR ${COMMON_CPP_DIR}/audioapi/external)

packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <audioapi/HostObjects/analysis/AnalyserNodeHostObject.h>
66
#include <audioapi/HostObjects/destinations/AudioDestinationNodeHostObject.h>
77
#include <audioapi/HostObjects/effects/BiquadFilterNodeHostObject.h>
8+
#include <audioapi/HostObjects/effects/ConvolverNodeHostObject.h>
89
#include <audioapi/HostObjects/effects/GainNodeHostObject.h>
910
#include <audioapi/HostObjects/effects/PeriodicWaveHostObject.h>
1011
#include <audioapi/HostObjects/effects/StereoPannerNodeHostObject.h>
@@ -49,6 +50,7 @@ BaseAudioContextHostObject::BaseAudioContextHostObject(
4950
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createBufferQueueSource),
5051
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createBuffer),
5152
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createPeriodicWave),
53+
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createConvolver),
5254
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createAnalyser));
5355
}
5456

@@ -269,4 +271,20 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createAnalyser) {
269271
auto analyserHostObject = std::make_shared<AnalyserNodeHostObject>(analyser);
270272
return jsi::Object::createFromHostObject(runtime, analyserHostObject);
271273
}
274+
275+
JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createConvolver) {
276+
auto disableNormalization = args[1].getBool();
277+
std::shared_ptr<ConvolverNode> convolver;
278+
if (args[0].isUndefined()) {
279+
convolver = context_->createConvolver(nullptr, disableNormalization);
280+
} else {
281+
auto bufferHostObject =
282+
args[0].getObject(runtime).asHostObject<AudioBufferHostObject>(runtime);
283+
convolver = context_->createConvolver(
284+
bufferHostObject->audioBuffer_, disableNormalization);
285+
}
286+
auto convolverHostObject =
287+
std::make_shared<ConvolverNodeHostObject>(convolver);
288+
return jsi::Object::createFromHostObject(runtime, convolverHostObject);
289+
}
272290
} // namespace audioapi

packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class BaseAudioContextHostObject : public JsiHostObject {
4141
JSI_HOST_FUNCTION_DECL(createBuffer);
4242
JSI_HOST_FUNCTION_DECL(createPeriodicWave);
4343
JSI_HOST_FUNCTION_DECL(createAnalyser);
44+
JSI_HOST_FUNCTION_DECL(createConvolver);
4445

4546
std::shared_ptr<BaseAudioContext> context_;
4647

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#include <audioapi/HostObjects/effects/ConvolverNodeHostObject.h>
2+
3+
#include <audioapi/HostObjects/sources/AudioBufferHostObject.h>
4+
#include <audioapi/core/effects/ConvolverNode.h>
5+
6+
namespace audioapi {
7+
8+
ConvolverNodeHostObject::ConvolverNodeHostObject(
9+
const std::shared_ptr<ConvolverNode> &node)
10+
: AudioNodeHostObject(node) {
11+
addGetters(
12+
JSI_EXPORT_PROPERTY_GETTER(ConvolverNodeHostObject, normalize),
13+
JSI_EXPORT_PROPERTY_GETTER(ConvolverNodeHostObject, buffer));
14+
addSetters(
15+
JSI_EXPORT_PROPERTY_SETTER(ConvolverNodeHostObject, normalize),
16+
JSI_EXPORT_PROPERTY_SETTER(ConvolverNodeHostObject, buffer));
17+
}
18+
19+
JSI_PROPERTY_GETTER_IMPL(ConvolverNodeHostObject, normalize) {
20+
auto convolverNode = std::static_pointer_cast<ConvolverNode>(node_);
21+
return {convolverNode->getNormalize_()};
22+
}
23+
24+
JSI_PROPERTY_GETTER_IMPL(ConvolverNodeHostObject, buffer) {
25+
auto convolverNode = std::static_pointer_cast<ConvolverNode>(node_);
26+
auto buffer = convolverNode->getBuffer();
27+
auto bufferHostObject = std::make_shared<AudioBufferHostObject>(buffer);
28+
return jsi::Object::createFromHostObject(runtime, bufferHostObject);
29+
}
30+
31+
JSI_PROPERTY_SETTER_IMPL(ConvolverNodeHostObject, normalize) {
32+
auto convolverNode = std::static_pointer_cast<ConvolverNode>(node_);
33+
convolverNode->setNormalize(value.getBool());
34+
}
35+
36+
JSI_PROPERTY_SETTER_IMPL(ConvolverNodeHostObject, buffer) {
37+
auto convolverNode = std::static_pointer_cast<ConvolverNode>(node_);
38+
if (value.isNull()) {
39+
convolverNode->setBuffer(nullptr);
40+
return;
41+
}
42+
43+
auto bufferHostObject =
44+
value.getObject(runtime).asHostObject<AudioBufferHostObject>(runtime);
45+
convolverNode->setBuffer(bufferHostObject->audioBuffer_);
46+
}
47+
} // namespace audioapi

0 commit comments

Comments
 (0)