`.
+
+
+## Remarks
+
+### `AudioContextOptions`
+
+
+
+`AudioContextOptions` is a dictionary object specifies sample rate for the new context.
+
+```jsx
+interface AudioContextOptions {
+ sampleRate: number;
+}
+```
+
\ No newline at end of file
diff --git a/packages/audiodocs/docs/core/base-audio-context.mdx b/packages/audiodocs/docs/core/base-audio-context.mdx
index 600352c33..bbda9d843 100644
--- a/packages/audiodocs/docs/core/base-audio-context.mdx
+++ b/packages/audiodocs/docs/core/base-audio-context.mdx
@@ -42,7 +42,7 @@ Concept of system-level audio callback does not apply to `OfflineAudioContext`.
| `currentTime` | `number` | Double value representing an ever-increasing hardware time in seconds, starting from 0. | |
| `destination` | `AudioDestinationNode` | Final output destination associated with the context. | |
| `sampleRate` | `number` | Float value representing the sample rate (in samples per seconds) used by all nodes in this context. | |
-| `state` | [`ContextState`](/types/context-state) | Enumerated value represents the current state of the context. | |
+| `state` | [`ContextState`](/core/base-audio-context#contextstate) | Enumerated value represents the current state of the context. | |
## Methods
@@ -84,7 +84,7 @@ The above method lets you create [`AudioBufferSourceNode`](/sources/audio-buffer
| Parameters | Type | Description |
| :---: | :---: | :---- |
-| `pitchCorrection` | [`AudioBufferSourceNodeOptions`](/types/audio-buffer-source-node-options) | Dictionary object that specifies if pitch correction has to be available. |
+| `pitchCorrection` | [`AudioBufferSourceNodeOptions`](/core/base-audio-context#audiobuffersourcenodeoptions) | Dictionary object that specifies if pitch correction has to be available. |
#### Returns `AudioBufferSourceNode`.
@@ -108,7 +108,7 @@ The above method lets you create `PeriodicWave`.
| :---: | :---: | :---- |
| `real` | `Float32Array` | An array of cosine terms. |
| `imag` | `Float32Array` | An array of sine terms. |
-| `constraints` | [`PeriodicWaveConstraints`](/types/periodic-wave-constraints) | An object that specifies if normalization is disabled. |
+| `constraints` | [`PeriodicWaveConstraints`](/core/base-audio-context#periodicwaveconstraints) | An object that specifies if normalization is disabled. |
#### Errors
@@ -149,3 +149,48 @@ The above method lets you decode audio data file. It saves file in the device fi
#### `currentTime`
- Timer starts when context is created, stops when context is suspended.
+
+### `ContextState`
+
+
+
+**Acceptable values:**
+ - `suspended`
+
+ The audio context has been suspended (with one of [`suspend`](/core/audio-context#suspend) or `OfflineAudioContext.suspend`).
+
+ - `running`
+
+ The audio context is running normally.
+
+ - `closed`
+
+ The audio context has been closed (with [`close`](/core/audio-context#close) method).
+
+
+### `AudioBufferSourceNodeOptions`
+
+
+
+`AudioBufferSourceNodeOptions` is a dictionary object specifies if pitch correction algorithm has to be available.
+
+```jsx
+interface AudioBufferSourceNodeOptions {
+ pitchCorrection: boolean
+}
+```
+
+
+### `PeriodicWaveConstraints`
+
+
+
+`PeriodicWaveConstraints` is a dictionary object specifies whether normalization should be disabled during creating periodic wave. If not specified normalization is enabled.
+If normalized, periodic wave will have maximum peak value of 1 and minimum peak value of -1.
+
+```jsx
+interface PeriodicWaveConstraints {
+ disableNormalization: boolean;
+}
+```
+
\ No newline at end of file
diff --git a/packages/audiodocs/docs/inputs/_category_.json b/packages/audiodocs/docs/inputs/_category_.json
new file mode 100644
index 000000000..e87e9fbd5
--- /dev/null
+++ b/packages/audiodocs/docs/inputs/_category_.json
@@ -0,0 +1,7 @@
+{
+ "label": "Inputs",
+ "position": 4,
+ "link": {
+ "type": "generated-index"
+ }
+}
diff --git a/packages/audiodocs/docs/inputs/audio-recorder.mdx b/packages/audiodocs/docs/inputs/audio-recorder.mdx
new file mode 100644
index 000000000..14f53406b
--- /dev/null
+++ b/packages/audiodocs/docs/inputs/audio-recorder.mdx
@@ -0,0 +1,90 @@
+---
+sidebar_position: 4
+---
+
+import AudioNodePropsTable from "@site/src/components/AudioNodePropsTable"
+import { Optional, ReadOnly } from '@site/src/components/Badges';
+
+# AudioRecorder
+
+## Constructor
+
+[`AudioRecorder(options)`](/inputs/audio-recorder#audiorecorderoptions)
+
+## Example
+
+```tsx
+import { AudioRecorder } from 'react-native-audio-api';
+
+function App() {
+ const recorder = new AudioRecorder({
+ sampleRate: 16000,
+ bufferLengthInSamples: 16000,
+ });
+
+ recorder.onAudioReady((event) => {
+ const { buffer, numFrames, when } = event;
+
+ console.log(
+ 'Audio recorder buffer ready:',
+ buffer.duration,
+ numFrames,
+ when
+ );
+ });
+
+ recorder.start();
+}
+```
+
+## Methods
+
+### `start`
+
+The above starts recording.
+
+#### Returns `undefined`.
+
+### `stop`
+
+The above stops recording.
+
+#### Returns `undefined`.
+
+
+### `onAudioReady`
+
+The above allows user to set a callback after every portion of data deliverance.
+
+| Parameters | Type | Description |
+| :---: | :---: | :---- |
+| `callback` | [(OnAudioReadyEventType => void)](/inputs/audio-recorder#onaudioreadyeventtype) | callback that will be invoked |
+
+#### Returns `undefined`.
+
+## Remarks
+
+### `AudioRecorderOptions`
+
+
+Type definitions
+```typescript
+interface AudioRecorderOptions {
+ sampleRate: number;
+ bufferLengthInSamples: number; //how many samples to be put in the buffer
+}
+```
+
+
+### `OnAudioReadyEventType`
+
+
+Type definitions
+```typescript
+interface OnAudioReadyEventType {
+ buffer: AudioBuffer;
+ numFrames: number; //number of frames in a buffer
+ when: number; //timestamp
+}
+```
+
diff --git a/packages/audiodocs/docs/other/compatibility.mdx b/packages/audiodocs/docs/other/compatibility.mdx
index ad073ab93..a6b4a30a8 100644
--- a/packages/audiodocs/docs/other/compatibility.mdx
+++ b/packages/audiodocs/docs/other/compatibility.mdx
@@ -12,12 +12,15 @@ import { Yes, No, Version, Spacer } from '@site/src/components/Compatibility';
-| | 0.74 | 0.75 | 0.76 | 0.77 | 0.78 |
-| ----------------------------------- | ----- | ----- | ----- | ----- | ----- |
-| | | | | | |
-| | | | | | |
-| | | | | | |
-| | | | | | |
+| | 0.74 | 0.75 | 0.76 | 0.77 | 0.78 | 0.79 | 0.80 |
+| ----------------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- |
+| | | | | | | | |
+| | | | | | | | |
+| | | | | | | | |
+| | | | | | | | |
+| | | | | | | | |
+| | | | | | | | |
+| | | | | | | | |
@@ -25,11 +28,12 @@ import { Yes, No, Version, Spacer } from '@site/src/components/Compatibility';
-| | 0.74 | 0.75 | 0.76 | 0.77 |
+| | 0.74 | 0.75 | 0.76 | 0.77 |
| ----------------------------------- | ----- | ----- | ----- | ----- |
-| | | | | |
-| | | | | |
-| | | | | |
-| | | | | |
+| | | | | |
+| | | | | |
+| | | | | |
+| | | | | |
+| | | | | |
diff --git a/packages/audiodocs/docs/other/web-audio-api-coverage.mdx b/packages/audiodocs/docs/other/web-audio-api-coverage.mdx
index 308ba72ab..5571a1651 100644
--- a/packages/audiodocs/docs/other/web-audio-api-coverage.mdx
+++ b/packages/audiodocs/docs/other/web-audio-api-coverage.mdx
@@ -19,6 +19,7 @@ sidebar_position: 2
| AudioScheduledSourceNode | ✅ |
| BiquadFilterNode | ✅ |
| GainNode | ✅ |
+| OfflineAudioContext | ✅ |
| OscillatorNode | ✅ |
| PeriodicWave | ✅ |
| StereoPannerNode | ✅ |
@@ -33,15 +34,13 @@ sidebar_position: 2
| ChannelMergerNode | ❌ |
| ChannelSplitterNode | ❌ |
| ConstantSourceNode | ❌ |
-| ConvolverNode | ❌ |
+| ConvolverNode | 🚧 |
| DelayNode | ❌ |
| DynamicsCompressorNode | ❌ |
| IIRFilterNode | ❌ |
| MediaElementAudioSourceNode | ❌ |
| MediaStreamAudioDestinationNode | ❌ |
| MediaStreamAudioSourceNode | ❌ |
-| OfflineAudioCompletionEvent | ❌ |
-| OfflineAudioContext | ❌ |
| PannerNode | ❌ |
| WaveShaperNode | ❌ |
diff --git a/packages/audiodocs/docs/sources/_category_.json b/packages/audiodocs/docs/sources/_category_.json
index c3572ca7d..7e94200d4 100644
--- a/packages/audiodocs/docs/sources/_category_.json
+++ b/packages/audiodocs/docs/sources/_category_.json
@@ -1,6 +1,6 @@
{
"label": "Sources",
- "position": 4,
+ "position": 3,
"link": {
"type": "generated-index"
}
diff --git a/packages/audiodocs/docs/sources/audio-buffer-source-node.mdx b/packages/audiodocs/docs/sources/audio-buffer-source-node.mdx
index 0998a9bd2..c0603339b 100644
--- a/packages/audiodocs/docs/sources/audio-buffer-source-node.mdx
+++ b/packages/audiodocs/docs/sources/audio-buffer-source-node.mdx
@@ -28,6 +28,7 @@ However, this node is very inexpensive to create, and what is crucial you can re
| `buffer` | [`AudioBuffer`](/sources/audio-buffer) | Associated `AudioBuffer`. |
| `detune` | [`AudioParam`](/core/audio-param) | [`k-rate`](/core/audio-param#a-rate-vs-k-rate) `AudioParam` representing detuning of oscillation in cents. |
| `loop` | `boolean` | Boolean indicating if audio data must be replayed after when end of the associated `AudioBuffer` is reached. |
+| `loopSkip` | `boolean` | Boolean indicating if upon setting up `loopStart` we want to skip immediately to the loop start. |
| `loopStart` | `number` | Float value indicating the time, in seconds, at which playback of the audio must begin, if loop is true. |
| `loopEnd` | `number` | Float value indicating the time, in seconds, at which playback of the audio must end and loop back to `loopStart`, if loop is true. |
| `playbackRate` | [`AudioParam`](/core/audio-param) | [`k-rate`](/core/audio-param#a-rate-vs-k-rate) `AudioParam` defining speed factor at which the audio will be played. |
diff --git a/packages/audiodocs/docs/types/audio-buffer-source-node-options.mdx b/packages/audiodocs/docs/types/audio-buffer-source-node-options.mdx
deleted file mode 100644
index fff7dbf5e..000000000
--- a/packages/audiodocs/docs/types/audio-buffer-source-node-options.mdx
+++ /dev/null
@@ -1,13 +0,0 @@
----
-sidebar_position: 1
----
-
-# AudioBufferSourceNodeOptions
-
-`AudioBufferSourceNodeOptions` is a dictionary object specifies if pitch correction algorithm has to be available.
-
-```jsx
-interface AudioBufferSourceNodeOptions {
- pitchCorrection: boolean
-}
-```
diff --git a/packages/audiodocs/docs/types/audio-context-options.mdx b/packages/audiodocs/docs/types/audio-context-options.mdx
deleted file mode 100644
index 5f4172f44..000000000
--- a/packages/audiodocs/docs/types/audio-context-options.mdx
+++ /dev/null
@@ -1,13 +0,0 @@
----
-sidebar_position: 2
----
-
-# AudioContextOptions
-
-`AudioContextOptions` is a dictionary object specifies sample rate for the new context.
-
-```jsx
-interface AudioContextOptions {
- sampleRate: number;
-}
-```
diff --git a/packages/audiodocs/docs/types/context-state.mdx b/packages/audiodocs/docs/types/context-state.mdx
deleted file mode 100644
index a68e213cd..000000000
--- a/packages/audiodocs/docs/types/context-state.mdx
+++ /dev/null
@@ -1,20 +0,0 @@
----
-sidebar_position: 5
----
-
-# ContextState
-
-`ContextState` type represents state of the [`BaseAudioContext`](/core/base-audio-context).
-
-**Acceptable values:**
- - `suspended`
-
- The audio context has been suspended (with one of [`suspend`](/core/audio-context#suspend) or `OfflineAudioContext.suspend`).
-
- - `running`
-
- The audio context is running normally.
-
- - `closed`
-
- The audio context has been closed (with [`close`](/core/audio-context#close) method).
diff --git a/packages/audiodocs/docs/types/periodic-wave-constraints.mdx b/packages/audiodocs/docs/types/periodic-wave-constraints.mdx
deleted file mode 100644
index c9092e401..000000000
--- a/packages/audiodocs/docs/types/periodic-wave-constraints.mdx
+++ /dev/null
@@ -1,14 +0,0 @@
----
-sidebar_position: 7
----
-
-# PeriodicWaveConstraints
-
-`PeriodicWaveConstraints` is a dictionary object specifies whether normalization should be disabled during creating periodic wave. If not specified normalization is enabled.
-If normalized, periodic wave will have maximum peak value of 1 and minimum peak value of -1.
-
-```jsx
-interface PeriodicWaveConstraints {
- disableNormalization: boolean;
-}
-```
diff --git a/packages/audiodocs/src/css/colors.css b/packages/audiodocs/src/css/colors.css
index bd73bca9c..161f1c02e 100644
--- a/packages/audiodocs/src/css/colors.css
+++ b/packages/audiodocs/src/css/colors.css
@@ -176,7 +176,7 @@
--swm-background-quote-blue: var(--swm-blue-light-40);
--swm-background-quote-green: var(--swm-green-light-40);
--swm-background-quote-red: var(--swm-red-light-40);
- --swm-background-quote-yellow: var(--swm-yellow-light-40);
+ --swm-background-quote-yellow: var(--swm-blue-light-40);
--swm-background-quote-purple: var(--swm-purple-light-40);
/* Code snippets */
@@ -365,8 +365,8 @@
--swm-background-quote-blue: var(--swm-blue-dark-140);
--swm-background-quote-green: var(--swm-green-light-40);
--swm-background-quote-red: var(--swm-red-light-40);
- --swm-background-quote-yellow: var(--swm-yellow-dark-140);
- --swm-background-quote-purple: var(--swm-purple-light-40);
+ --swm-background-quote-yellow: var(--swm-navy-light-60);
+ --swm-background-quote-purple: var(--swm-purple-light-80);
/* Code snippets */
--swm-border: var(--swm-navy-light-60);
diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp
index 48f24e151..1663190e4 100644
--- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp
+++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp
@@ -26,8 +26,6 @@ AndroidAudioRecorder::AndroidAudioRecorder(
}
AndroidAudioRecorder::~AndroidAudioRecorder() {
- AudioRecorder::~AudioRecorder();
-
if (mStream_) {
mStream_->requestStop();
mStream_->close();
diff --git a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioDecoder.cpp b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioDecoder.cpp
index 43713d3bd..8f6e94592 100644
--- a/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioDecoder.cpp
+++ b/packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioDecoder.cpp
@@ -1,4 +1,6 @@
#include
+#include
+#include
#include
#include
@@ -110,4 +112,25 @@ std::shared_ptr AudioDecoder::decodeWithMemoryBlock(
return audioBus;
}
+std::shared_ptr AudioDecoder::decodeWithPCMInBase64(
+ const std::string &data) const {
+ auto decodedData = base64_decode(data, false);
+
+ const auto uint8Data = reinterpret_cast(decodedData.data());
+ size_t frameCount = decodedData.size() / 2;
+
+ auto audioBus = std::make_shared(frameCount, 1, sampleRate_);
+ auto channelData = audioBus->getChannel(0)->getData();
+
+ for (size_t i = 0; i < frameCount; ++i) {
+ auto sample =
+ static_cast((uint8Data[i * 2 + 1] << 8) | uint8Data[i * 2]);
+ channelData[i] = static_cast(sample);
+ }
+
+ dsp::multiplyByScalar(channelData, 1.0f / 32768.0f, channelData, frameCount);
+
+ return audioBus;
+}
+
} // namespace audioapi
diff --git a/packages/react-native-audio-api/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt b/packages/react-native-audio-api/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt
index 3cf5dd62d..b21b02061 100644
--- a/packages/react-native-audio-api/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt
+++ b/packages/react-native-audio-api/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt
@@ -67,6 +67,7 @@ class AudioAPIModule(
category: String?,
mode: String?,
options: ReadableArray?,
+ allowHaptics: Boolean,
) {
// noting to do here
}
diff --git a/packages/react-native-audio-api/android/src/main/java/com/swmansion/audioapi/system/LockScreenManager.kt b/packages/react-native-audio-api/android/src/main/java/com/swmansion/audioapi/system/LockScreenManager.kt
index 4b5ecba1c..b1d5bdc22 100644
--- a/packages/react-native-audio-api/android/src/main/java/com/swmansion/audioapi/system/LockScreenManager.kt
+++ b/packages/react-native-audio-api/android/src/main/java/com/swmansion/audioapi/system/LockScreenManager.kt
@@ -12,7 +12,7 @@ import androidx.media.app.NotificationCompat.MediaStyle
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.bridge.ReadableType
-import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper.Companion.instance
+import com.swmansion.audioapi.R
import java.io.IOException
import java.lang.ref.WeakReference
import java.net.URL
@@ -130,9 +130,6 @@ class LockScreenManager(
}
}
artworkThread!!.start()
- } else {
- md.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, null)
- nb.setLargeIcon(null as Bitmap?)
}
speed =
@@ -185,6 +182,7 @@ class LockScreenManager(
name: String,
enabled: Boolean,
) {
+ pb = PlaybackStateCompat.Builder()
var controlValue = 0L
when (name) {
"remotePlay" -> controlValue = PlaybackStateCompat.ACTION_PLAY
@@ -206,8 +204,31 @@ class LockScreenManager(
}
mediaNotificationManager.get()?.updateActions(controls)
+
+ if (hasControl(PlaybackStateCompat.ACTION_REWIND)) {
+ pb.addCustomAction(
+ PlaybackStateCompat.CustomAction
+ .Builder(
+ "SkipBackward",
+ "Skip Backward",
+ R.drawable.skip_backward_10,
+ ).build(),
+ )
+ }
+
pb.setActions(controls)
+ if (hasControl(PlaybackStateCompat.ACTION_FAST_FORWARD)) {
+ pb.addCustomAction(
+ PlaybackStateCompat.CustomAction
+ .Builder(
+ "SkipForward",
+ "Skip Forward",
+ R.drawable.skip_forward_10,
+ ).build(),
+ )
+ }
+
state = pb.build()
mediaSession.get()?.setPlaybackState(state)
@@ -228,7 +249,7 @@ class LockScreenManager(
// If we are running the app in debug mode, the "local" image will be served from htt://localhost:8080, so we need to check for this case and load those images from URL
if (local && !url.startsWith("http")) {
// Gets the drawable from the RN's helper for local resources
- val helper = instance
+ val helper = com.facebook.react.views.imagehelper.ResourceDrawableIdHelper.instance
val image = helper.getResourceDrawable(reactContext.get()!!, url)
bitmap =
diff --git a/packages/react-native-audio-api/android/src/main/java/com/swmansion/audioapi/system/MediaNotificationManager.kt b/packages/react-native-audio-api/android/src/main/java/com/swmansion/audioapi/system/MediaNotificationManager.kt
index 7f8ffa91f..f49002a45 100644
--- a/packages/react-native-audio-api/android/src/main/java/com/swmansion/audioapi/system/MediaNotificationManager.kt
+++ b/packages/react-native-audio-api/android/src/main/java/com/swmansion/audioapi/system/MediaNotificationManager.kt
@@ -143,32 +143,9 @@ class MediaNotificationManager(
pause = createAction("pause", "Pause", mask, PlaybackStateCompat.ACTION_PAUSE, pause)
stop = createAction("stop", "Stop", mask, PlaybackStateCompat.ACTION_STOP, stop)
next = createAction("next", "Next", mask, PlaybackStateCompat.ACTION_SKIP_TO_NEXT, next)
- previous =
- createAction(
- "previous",
- "Previous",
- mask,
- PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS,
- previous,
- )
-
- skipForward =
- createAction(
- "skip_forward_10",
- "Skip Forward",
- mask,
- PlaybackStateCompat.ACTION_FAST_FORWARD,
- skipForward,
- )
-
- skipBackward =
- createAction(
- "skip_backward_10",
- "Skip Backward",
- mask,
- PlaybackStateCompat.ACTION_REWIND,
- skipBackward,
- )
+ previous = createAction("previous", "Previous", mask, PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS, previous)
+ skipForward = createAction("skip_forward_10", "Skip Forward", mask, PlaybackStateCompat.ACTION_FAST_FORWARD, skipForward)
+ skipBackward = createAction("skip_backward_10", "Skip Backward", mask, PlaybackStateCompat.ACTION_REWIND, skipBackward)
}
private fun createAction(
diff --git a/packages/react-native-audio-api/android/src/main/java/com/swmansion/audioapi/system/MediaSessionCallback.kt b/packages/react-native-audio-api/android/src/main/java/com/swmansion/audioapi/system/MediaSessionCallback.kt
index 54b3efc57..dc8429eae 100644
--- a/packages/react-native-audio-api/android/src/main/java/com/swmansion/audioapi/system/MediaSessionCallback.kt
+++ b/packages/react-native-audio-api/android/src/main/java/com/swmansion/audioapi/system/MediaSessionCallback.kt
@@ -2,7 +2,9 @@ package com.swmansion.audioapi.system
import android.content.Intent
import android.os.Build
+import android.os.Bundle
import android.support.v4.media.session.MediaSessionCompat
+import android.util.Log
import androidx.core.app.NotificationManagerCompat
import com.swmansion.audioapi.AudioAPIModule
import java.lang.ref.WeakReference
@@ -53,4 +55,15 @@ class MediaSessionCallback(
val body = HashMap().apply { put("value", (pos.toDouble() / 1000)) }
audioAPIModule.get()?.invokeHandlerWithEventNameAndEventBody("remoteChangePlaybackPosition", body)
}
+
+ override fun onCustomAction(
+ action: String?,
+ extras: Bundle?,
+ ) {
+ when (action) {
+ "SkipForward" -> onFastForward()
+ "SkipBackward" -> onRewind()
+ else -> Log.w("MediaSessionCallback", "Unknown custom action: $action")
+ }
+ }
}
diff --git a/packages/react-native-audio-api/android/src/oldarch/NativeAudioAPIModuleSpec.java b/packages/react-native-audio-api/android/src/oldarch/NativeAudioAPIModuleSpec.java
index 73774139e..2274af879 100644
--- a/packages/react-native-audio-api/android/src/oldarch/NativeAudioAPIModuleSpec.java
+++ b/packages/react-native-audio-api/android/src/oldarch/NativeAudioAPIModuleSpec.java
@@ -48,7 +48,7 @@ public NativeAudioAPIModuleSpec(ReactApplicationContext reactContext) {
@ReactMethod
@DoNotStrip
- public abstract void setAudioSessionOptions(String category, String mode, ReadableArray options);
+ public abstract void setAudioSessionOptions(String category, String mode, ReadableArray options, boolean allowHaptics);
@ReactMethod
@DoNotStrip
diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioBufferBaseSourceNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioBufferBaseSourceNodeHostObject.h
new file mode 100644
index 000000000..210ec63af
--- /dev/null
+++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioBufferBaseSourceNodeHostObject.h
@@ -0,0 +1,60 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include
+#include
+
+namespace audioapi {
+using namespace facebook;
+
+class AudioBufferBaseSourceNodeHostObject
+ : public AudioScheduledSourceNodeHostObject {
+ public:
+ explicit AudioBufferBaseSourceNodeHostObject(
+ const std::shared_ptr &node)
+ : AudioScheduledSourceNodeHostObject(node) {
+ addGetters(
+ JSI_EXPORT_PROPERTY_GETTER(AudioBufferBaseSourceNodeHostObject, detune),
+ JSI_EXPORT_PROPERTY_GETTER(AudioBufferBaseSourceNodeHostObject, playbackRate));
+
+ addSetters(
+ JSI_EXPORT_PROPERTY_SETTER(AudioBufferBaseSourceNodeHostObject, onPositionChanged),
+ JSI_EXPORT_PROPERTY_SETTER(AudioBufferBaseSourceNodeHostObject, onPositionChangedInterval));
+ }
+
+ JSI_PROPERTY_GETTER(detune) {
+ auto sourceNode =
+ std::static_pointer_cast(node_);
+ auto detune = sourceNode->getDetuneParam();
+ auto detuneHostObject = std::make_shared(detune);
+ return jsi::Object::createFromHostObject(runtime, detuneHostObject);
+ }
+
+ JSI_PROPERTY_GETTER(playbackRate) {
+ auto sourceNode =
+ std::static_pointer_cast(node_);
+ auto playbackRate = sourceNode->getPlaybackRateParam();
+ auto playbackRateHostObject =
+ std::make_shared(playbackRate);
+ return jsi::Object::createFromHostObject(runtime, playbackRateHostObject);
+ }
+
+ JSI_PROPERTY_SETTER(onPositionChanged) {
+ auto sourceNode =
+ std::static_pointer_cast(node_);
+
+ sourceNode->setOnPositionChangedCallbackId(std::stoull(value.getString(runtime).utf8(runtime)));
+ }
+
+ JSI_PROPERTY_SETTER(onPositionChangedInterval) {
+ auto sourceNode =
+ std::static_pointer_cast(node_);
+
+ sourceNode->setOnPositionChangedInterval(static_cast(value.getNumber()));
+ }
+};
+
+} // namespace audioapi
diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioBufferQueueSourceNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioBufferQueueSourceNodeHostObject.h
index 94c1cfcc5..ef61db34e 100644
--- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioBufferQueueSourceNodeHostObject.h
+++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioBufferQueueSourceNodeHostObject.h
@@ -2,8 +2,7 @@
#include
#include
-#include
-#include
+#include
#include
#include
@@ -12,66 +11,21 @@ namespace audioapi {
using namespace facebook;
class AudioBufferQueueSourceNodeHostObject
- : public AudioScheduledSourceNodeHostObject {
+ : public AudioBufferBaseSourceNodeHostObject {
public:
explicit AudioBufferQueueSourceNodeHostObject(
const std::shared_ptr &node)
- : AudioScheduledSourceNodeHostObject(node) {
- addGetters(
- JSI_EXPORT_PROPERTY_GETTER(AudioBufferQueueSourceNodeHostObject, detune),
- JSI_EXPORT_PROPERTY_GETTER(AudioBufferQueueSourceNodeHostObject, playbackRate));
-
- addSetters(
- JSI_EXPORT_PROPERTY_SETTER(AudioBufferQueueSourceNodeHostObject, onPositionChanged),
- JSI_EXPORT_PROPERTY_SETTER(AudioBufferQueueSourceNodeHostObject, onPositionChangedInterval));
-
- // start method is overridden in this class
- functions_->erase("start");
-
+ : AudioBufferBaseSourceNodeHostObject(node) {
addFunctions(
- JSI_EXPORT_FUNCTION(AudioBufferQueueSourceNodeHostObject, start),
- JSI_EXPORT_FUNCTION(AudioBufferQueueSourceNodeHostObject, enqueueBuffer));
- }
-
- JSI_PROPERTY_GETTER(detune) {
- auto audioBufferSourceNode =
- std::static_pointer_cast(node_);
- auto detune = audioBufferSourceNode->getDetuneParam();
- auto detuneHostObject = std::make_shared(detune);
- return jsi::Object::createFromHostObject(runtime, detuneHostObject);
- }
-
- JSI_PROPERTY_GETTER(playbackRate) {
- auto audioBufferSourceNode =
- std::static_pointer_cast(node_);
- auto playbackRate = audioBufferSourceNode->getPlaybackRateParam();
- auto playbackRateHostObject =
- std::make_shared(playbackRate);
- return jsi::Object::createFromHostObject(runtime, playbackRateHostObject);
- }
-
- JSI_PROPERTY_SETTER(onPositionChanged) {
- auto audioBufferQueueSourceNode =
- std::static_pointer_cast(node_);
-
- audioBufferQueueSourceNode->setOnPositionChangedCallbackId(std::stoull(value.getString(runtime).utf8(runtime)));
- }
-
- JSI_PROPERTY_SETTER(onPositionChangedInterval) {
- auto audioBufferQueueSourceNode =
- std::static_pointer_cast(node_);
-
- audioBufferQueueSourceNode->setOnPositionChangedInterval(value.getNumber());
+ JSI_EXPORT_FUNCTION(AudioBufferQueueSourceNodeHostObject, enqueueBuffer),
+ JSI_EXPORT_FUNCTION(AudioBufferQueueSourceNodeHostObject, pause));
}
- JSI_HOST_FUNCTION(start) {
- auto when = args[0].getNumber();
- auto offset = args[1].getNumber();
-
+ JSI_HOST_FUNCTION(pause) {
auto audioBufferQueueSourceNode =
std::static_pointer_cast(node_);
- audioBufferQueueSourceNode->start(when, offset);
+ audioBufferQueueSourceNode->pause();
return jsi::Value::undefined();
}
@@ -82,10 +36,9 @@ class AudioBufferQueueSourceNodeHostObject
auto audioBufferHostObject =
args[0].getObject(runtime).asHostObject(runtime);
- int bufferId = args[1].asNumber();
- auto isLastBuffer = args[2].asBool();
+ auto isLastBuffer = args[1].asBool();
- audioBufferQueueSourceNode->enqueueBuffer(audioBufferHostObject->audioBuffer_, bufferId, isLastBuffer);
+ audioBufferQueueSourceNode->enqueueBuffer(audioBufferHostObject->audioBuffer_, isLastBuffer);
return jsi::Value::undefined();
}
diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioBufferSourceNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioBufferSourceNodeHostObject.h
index a61292285..21fa4c665 100644
--- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioBufferSourceNodeHostObject.h
+++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioBufferSourceNodeHostObject.h
@@ -2,8 +2,7 @@
#include
#include
-#include
-#include
+#include
#include
#include
@@ -12,22 +11,21 @@ namespace audioapi {
using namespace facebook;
class AudioBufferSourceNodeHostObject
- : public AudioScheduledSourceNodeHostObject {
+ : public AudioBufferBaseSourceNodeHostObject {
public:
explicit AudioBufferSourceNodeHostObject(
const std::shared_ptr &node)
- : AudioScheduledSourceNodeHostObject(node) {
+ : AudioBufferBaseSourceNodeHostObject(node) {
addGetters(
JSI_EXPORT_PROPERTY_GETTER(AudioBufferSourceNodeHostObject, loop),
+ JSI_EXPORT_PROPERTY_GETTER(AudioBufferSourceNodeHostObject, loopSkip),
JSI_EXPORT_PROPERTY_GETTER(AudioBufferSourceNodeHostObject, buffer),
JSI_EXPORT_PROPERTY_GETTER(AudioBufferSourceNodeHostObject, loopStart),
- JSI_EXPORT_PROPERTY_GETTER(AudioBufferSourceNodeHostObject, loopEnd),
- JSI_EXPORT_PROPERTY_GETTER(AudioBufferSourceNodeHostObject, detune),
- JSI_EXPORT_PROPERTY_GETTER(AudioBufferSourceNodeHostObject, playbackRate));
+ JSI_EXPORT_PROPERTY_GETTER(AudioBufferSourceNodeHostObject, loopEnd));
addSetters(
JSI_EXPORT_PROPERTY_SETTER(AudioBufferSourceNodeHostObject, loop),
- JSI_EXPORT_PROPERTY_SETTER(AudioBufferSourceNodeHostObject, buffer),
+ JSI_EXPORT_PROPERTY_SETTER(AudioBufferSourceNodeHostObject, loopSkip),
JSI_EXPORT_PROPERTY_SETTER(AudioBufferSourceNodeHostObject, loopStart),
JSI_EXPORT_PROPERTY_SETTER(AudioBufferSourceNodeHostObject, loopEnd));
@@ -35,7 +33,8 @@ class AudioBufferSourceNodeHostObject
functions_->erase("start");
addFunctions(
- JSI_EXPORT_FUNCTION(AudioBufferSourceNodeHostObject, start));
+ JSI_EXPORT_FUNCTION(AudioBufferSourceNodeHostObject, start),
+ JSI_EXPORT_FUNCTION(AudioBufferSourceNodeHostObject, setBuffer));
}
JSI_PROPERTY_GETTER(loop) {
@@ -45,6 +44,13 @@ class AudioBufferSourceNodeHostObject
return {loop};
}
+ JSI_PROPERTY_GETTER(loopSkip) {
+ auto audioBufferSourceNode =
+ std::static_pointer_cast(node_);
+ auto loopSkip = audioBufferSourceNode->getLoopSkip();
+ return {loopSkip};
+ }
+
JSI_PROPERTY_GETTER(buffer) {
auto audioBufferSourceNode =
std::static_pointer_cast(node_);
@@ -72,40 +78,16 @@ class AudioBufferSourceNodeHostObject
return {loopEnd};
}
- JSI_PROPERTY_GETTER(detune) {
- auto audioBufferSourceNode =
- std::static_pointer_cast(node_);
- auto detune = audioBufferSourceNode->getDetuneParam();
- auto detuneHostObject = std::make_shared(detune);
- return jsi::Object::createFromHostObject(runtime, detuneHostObject);
- }
-
- JSI_PROPERTY_GETTER(playbackRate) {
- auto audioBufferSourceNode =
- std::static_pointer_cast(node_);
- auto playbackRate = audioBufferSourceNode->getPlaybackRateParam();
- auto playbackRateHostObject =
- std::make_shared(playbackRate);
- return jsi::Object::createFromHostObject(runtime, playbackRateHostObject);
- }
-
JSI_PROPERTY_SETTER(loop) {
auto audioBufferSourceNode =
std::static_pointer_cast(node_);
audioBufferSourceNode->setLoop(value.getBool());
}
- JSI_PROPERTY_SETTER(buffer) {
+ JSI_PROPERTY_SETTER(loopSkip) {
auto audioBufferSourceNode =
std::static_pointer_cast(node_);
- if (value.isNull()) {
- audioBufferSourceNode->setBuffer(std::shared_ptr(nullptr));
- return;
- }
-
- auto bufferHostObject =
- value.getObject(runtime).asHostObject(runtime);
- audioBufferSourceNode->setBuffer(bufferHostObject->audioBuffer_);
+ audioBufferSourceNode->setLoopSkip(value.getBool());
}
JSI_PROPERTY_SETTER(loopStart) {
@@ -138,6 +120,25 @@ class AudioBufferSourceNodeHostObject
return jsi::Value::undefined();
}
+
+ JSI_HOST_FUNCTION(setBuffer) {
+ auto audioBufferSourceNode =
+ std::static_pointer_cast(node_);
+
+ auto audioBufferSourceNodeJsiObject = args[0].getObject(runtime);
+
+ if (args[1].isNull()) {
+ audioBufferSourceNode->setBuffer(std::shared_ptr(nullptr));
+ return jsi::Value::undefined();
+ }
+
+ auto bufferHostObject =
+ args[1].getObject(runtime).asHostObject(runtime);
+ audioBufferSourceNodeJsiObject.setExternalMemoryPressure(runtime,
+ bufferHostObject->getSizeInBytes() + 16);
+ audioBufferSourceNode->setBuffer(bufferHostObject->audioBuffer_);
+ return jsi::Value::undefined();
+ }
};
} // namespace audioapi
diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.h
index 9fc079d39..6487fa17e 100644
--- a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.h
+++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.h
@@ -8,6 +8,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -40,6 +41,7 @@ class BaseAudioContextHostObject : public JsiHostObject {
addFunctions(
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createOscillator),
+ JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createCustomProcessor),
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createGain),
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createStereoPanner),
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createBiquadFilter),
@@ -49,7 +51,8 @@ class BaseAudioContextHostObject : public JsiHostObject {
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createPeriodicWave),
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createAnalyser),
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, decodeAudioData),
- JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, decodeAudioDataSource));
+ JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, decodeAudioDataSource),
+ JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, decodePCMAudioDataInBase64));
}
JSI_PROPERTY_GETTER(destination) {
@@ -77,6 +80,13 @@ class BaseAudioContextHostObject : public JsiHostObject {
return jsi::Object::createFromHostObject(runtime, oscillatorHostObject);
}
+ JSI_HOST_FUNCTION(createCustomProcessor) {
+ auto identifier = args[0].getString(runtime).utf8(runtime);
+ auto customProcessor = context_->createCustomProcessor(identifier);
+ auto customProcessorHostObject = std::make_shared(customProcessor);
+ return jsi::Object::createFromHostObject(runtime, customProcessorHostObject);
+ }
+
JSI_HOST_FUNCTION(createGain) {
auto gain = context_->createGain();
auto gainHostObject = std::make_shared(gain);
@@ -163,13 +173,14 @@ JSI_HOST_FUNCTION(createBufferQueueSource) {
auto promise = promiseVendor_->createPromise([this, sourcePath](std::shared_ptr promise) {
std::thread([this, sourcePath, promise = std::move(promise)]() {
auto results = context_->decodeAudioDataSource(sourcePath);
- auto audioBufferHostObject = std::make_shared(results);
if (!results) {
promise->reject("Failed to decode audio data source.");
return;
}
+ auto audioBufferHostObject = std::make_shared(results);
+
promise->resolve([audioBufferHostObject = std::move(audioBufferHostObject)](jsi::Runtime &runtime) {
auto jsiObject = jsi::Object::createFromHostObject(runtime, audioBufferHostObject);
jsiObject.setExternalMemoryPressure(runtime, audioBufferHostObject->getSizeInBytes());
@@ -189,13 +200,14 @@ JSI_HOST_FUNCTION(createBufferQueueSource) {
auto promise = promiseVendor_->createPromise([this, data, size](std::shared_ptr promise) {
std::thread([this, data, size, promise = std::move(promise)]() {
auto results = context_->decodeAudioData(data, size);
- auto audioBufferHostObject = std::make_shared(results);
if (!results) {
promise->reject("Failed to decode audio data source.");
return;
}
+ auto audioBufferHostObject = std::make_shared(results);
+
promise->resolve([audioBufferHostObject = std::move(audioBufferHostObject)](jsi::Runtime &runtime) {
auto jsiObject = jsi::Object::createFromHostObject(runtime, audioBufferHostObject);
jsiObject.setExternalMemoryPressure(runtime, audioBufferHostObject->getSizeInBytes());
@@ -207,6 +219,31 @@ JSI_HOST_FUNCTION(createBufferQueueSource) {
return promise;
}
+ JSI_HOST_FUNCTION(decodePCMAudioDataInBase64) {
+ auto b64 = args[0].getString(runtime).utf8(runtime);
+
+ auto promise = promiseVendor_->createPromise([this, b64](std::shared_ptr promise) {
+ std::thread([this, b64, promise = std::move(promise)]() {
+ auto results = context_->decodeWithPCMInBase64(b64);
+
+ if (!results) {
+ promise->reject("Failed to decode audio data source.");
+ return;
+ }
+
+ auto audioBufferHostObject = std::make_shared(results);
+
+ promise->resolve([audioBufferHostObject = std::move(audioBufferHostObject)](jsi::Runtime &runtime) {
+ auto jsiObject = jsi::Object::createFromHostObject(runtime, audioBufferHostObject);
+ jsiObject.setExternalMemoryPressure(runtime, audioBufferHostObject->getSizeInBytes());
+ return jsiObject;
+ });
+ }).detach();
+ });
+
+ return promise;
+ }
+
protected:
std::shared_ptr context_;
std::shared_ptr promiseVendor_;
diff --git a/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/CustomProcessorNodeHostObject.h b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/CustomProcessorNodeHostObject.h
new file mode 100644
index 000000000..58216f424
--- /dev/null
+++ b/packages/react-native-audio-api/common/cpp/audioapi/HostObjects/CustomProcessorNodeHostObject.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+namespace audioapi {
+using namespace facebook;
+
+class CustomProcessorNodeHostObject : public AudioNodeHostObject {
+ public:
+ explicit CustomProcessorNodeHostObject(const std::shared_ptr &node)
+ : AudioNodeHostObject(node) {
+ addGetters(
+ JSI_EXPORT_PROPERTY_GETTER(CustomProcessorNodeHostObject, customProcessor),
+ JSI_EXPORT_PROPERTY_GETTER(CustomProcessorNodeHostObject, processorMode));
+
+ addSetters(
+ JSI_EXPORT_PROPERTY_SETTER(CustomProcessorNodeHostObject, processorMode));
+ }
+
+ JSI_PROPERTY_GETTER(customProcessor) {
+ auto customProcessorNode = std::static_pointer_cast(node_);
+ auto customProcessorParam =
+ std::make_shared(customProcessorNode->getCustomProcessorParam());
+ return jsi::Object::createFromHostObject(runtime, customProcessorParam);
+ }
+
+ JSI_PROPERTY_GETTER(processorMode) {
+ auto customProcessorNode = std::static_pointer_cast(node_);
+ auto mode = customProcessorNode->getProcessorMode();
+ std::string modeStr = (mode == CustomProcessorNode::ProcessorMode::ProcessThrough)
+ ? "processThrough"
+ : "processInPlace";
+ return jsi::String::createFromUtf8(runtime, modeStr);
+ }
+
+ JSI_PROPERTY_SETTER(processorMode) {
+ auto customProcessorNode = std::static_pointer_cast(node_);
+ std::string modeStr = value.getString(runtime).utf8(runtime);
+
+ if (modeStr == "processThrough") {
+ customProcessorNode->setProcessorMode(CustomProcessorNode::ProcessorMode::ProcessThrough);
+ } else {
+ customProcessorNode->setProcessorMode(CustomProcessorNode::ProcessorMode::ProcessInPlace);
+ }
+ }
+};
+
+} // namespace audioapi
diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.cpp
index dada1b666..cfc999a9e 100644
--- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.cpp
+++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.cpp
@@ -284,15 +284,21 @@ void AudioParam::removeInputNode(AudioNode *node) {
}
}
-std::shared_ptr AudioParam::processARateParam(
- int framesToProcess,
- double time) {
- auto processingBus = audioBus_;
+std::shared_ptr AudioParam::calculateInputs(
+ const std::shared_ptr &processingBus,
+ int framesToProcess) {
processingBus->zero();
if (!inputNodes_.empty()) {
processInputs(processingBus, framesToProcess, true);
mixInputsBuses(processingBus);
}
+ return processingBus;
+}
+
+std::shared_ptr AudioParam::processARateParam(
+ int framesToProcess,
+ double time) {
+ auto processingBus = calculateInputs(audioBus_, framesToProcess);
for (size_t i = 0; i < framesToProcess; i++) {
auto sample = getValueAtTime(time + i / context_->getSampleRate());
processingBus->getChannel(0)->getData()[i] += sample;
@@ -302,9 +308,8 @@ std::shared_ptr AudioParam::processARateParam(
}
float AudioParam::processKRateParam(int framesToProcess, double time) {
- auto processingBus = processARateParam(framesToProcess, time);
- // processingBus is a mono bus
- return processingBus->getChannel(0)->getData()[0];
+ auto processingBus = calculateInputs(audioBus_, framesToProcess);
+ return processingBus->getChannel(0)->getData()[0] + getValueAtTime(time);
}
double AudioParam::getQueueEndTime() {
diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h
index ad931f1c9..d899518e7 100644
--- a/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h
+++ b/packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.h
@@ -63,6 +63,7 @@ class AudioParam {
void updateQueue(ParamChangeEvent &event);
void processInputs(const std::shared_ptr& outputBus, int framesToProcess, bool checkIsAlreadyProcessed);
void mixInputsBuses(const std::shared_ptr& processingBus);
+ std::shared_ptr calculateInputs(const std::shared_ptr& processingBus, int framesToProcess);
};
} // namespace audioapi
diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp
index 7df5ab3f1..2dd19d50d 100644
--- a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp
+++ b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp
@@ -2,6 +2,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -54,6 +55,14 @@ std::shared_ptr BaseAudioContext::createOscillator() {
return oscillator;
}
+std::shared_ptr BaseAudioContext::createCustomProcessor(
+ const std::string &identifier) {
+ auto customProcessor =
+ std::make_shared(this, identifier);
+ nodeManager_->addProcessingNode(customProcessor);
+ return customProcessor;
+}
+
std::shared_ptr BaseAudioContext::createGain() {
auto gain = std::make_shared(this);
nodeManager_->addProcessingNode(gain);
@@ -131,6 +140,17 @@ std::shared_ptr BaseAudioContext::decodeAudioData(
return std::make_shared(audioBus);
}
+std::shared_ptr BaseAudioContext::decodeWithPCMInBase64(
+ const std::string &data) {
+ auto audioBus = audioDecoder_->decodeWithPCMInBase64(data);
+
+ if (!audioBus) {
+ return nullptr;
+ }
+
+ return std::make_shared(audioBus);
+}
+
AudioNodeManager *BaseAudioContext::getNodeManager() {
return nodeManager_.get();
}
diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h
index c896c9cf0..9087b2dd5 100644
--- a/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h
+++ b/packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h
@@ -15,6 +15,7 @@
namespace audioapi {
class AudioBus;
+class CustomProcessorNode;
class GainNode;
class AudioBuffer;
class PeriodicWave;
@@ -41,6 +42,7 @@ class BaseAudioContext {
std::shared_ptr getDestination();
std::shared_ptr createOscillator();
+ std::shared_ptr createCustomProcessor(const std::string& identifier);
std::shared_ptr createGain();
std::shared_ptr createStereoPanner();
std::shared_ptr createBiquadFilter();
@@ -56,6 +58,7 @@ class BaseAudioContext {
std::shared_ptr decodeAudioDataSource(const std::string &path);
std::shared_ptr decodeAudioData(const void *data, size_t size);
+ std::shared_ptr decodeWithPCMInBase64(const std::string &data);
std::shared_ptr getBasicWaveForm(OscillatorType type);
[[nodiscard]] float getNyquistFrequency() const;
diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/CustomProcessorNode.cpp b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/CustomProcessorNode.cpp
new file mode 100644
index 000000000..3632a824e
--- /dev/null
+++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/CustomProcessorNode.cpp
@@ -0,0 +1,173 @@
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+namespace audioapi {
+
+// Static registries and active node tracking
+std::map()>>
+ CustomProcessorNode::s_processorFactoriesByIdentifier;
+std::unordered_map
+ CustomProcessorNode::s_controlHandlersByIdentifier;
+std::unordered_map>
+ CustomProcessorNode::s_activeNodes;
+
+// Constructor: initializes processor, tracking, and preallocated buffers
+CustomProcessorNode::CustomProcessorNode(
+ BaseAudioContext *context,
+ const std::string &identifier)
+ : AudioNode(context), processorMode_(ProcessorMode::ProcessInPlace) {
+ customProcessorParam_ = std::make_shared(
+ 1.0f, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context);
+ isInitialized_ = true;
+
+ auto it = s_processorFactoriesByIdentifier.find(identifier);
+ processor_ =
+ (it != s_processorFactoriesByIdentifier.end()) ? it->second() : nullptr;
+
+ s_activeNodes[identifier].push_back(this);
+}
+
+// Destructor: cleans up tracking for this instance
+CustomProcessorNode::~CustomProcessorNode() {
+ for (auto &pair : s_activeNodes) {
+ auto &vec = pair.second;
+ vec.erase(std::remove(vec.begin(), vec.end(), this), vec.end());
+ }
+}
+
+// Gets the modifiable parameter
+std::shared_ptr CustomProcessorNode::getCustomProcessorParam()
+ const {
+ return customProcessorParam_;
+}
+
+// Returns the current processing mode
+CustomProcessorNode::ProcessorMode CustomProcessorNode::getProcessorMode()
+ const {
+ return processorMode_;
+}
+
+// Updates the processing mode
+void CustomProcessorNode::setProcessorMode(ProcessorMode mode) {
+ processorMode_ = mode;
+}
+
+// Registers a factory for dynamic processor instantiation
+void CustomProcessorNode::registerProcessorFactory(
+ const std::string &identifier,
+ std::function()> factory) {
+ s_processorFactoriesByIdentifier[identifier] = std::move(factory);
+ notifyProcessorChanged(identifier);
+}
+
+// Removes a processor factory from registry
+void CustomProcessorNode::unregisterProcessorFactory(
+ const std::string &identifier) {
+ s_processorFactoriesByIdentifier.erase(identifier);
+ notifyProcessorChanged(identifier);
+}
+
+// Updates processor instances for all nodes using given identifier
+void CustomProcessorNode::notifyProcessorChanged(
+ const std::string &identifier) {
+ auto it = s_activeNodes.find(identifier);
+ if (it != s_activeNodes.end()) {
+ for (CustomProcessorNode *node : it->second) {
+ auto f = s_processorFactoriesByIdentifier.find(identifier);
+ node->processor_ =
+ (f != s_processorFactoriesByIdentifier.end()) ? f->second() : nullptr;
+ }
+ }
+}
+
+// Registers control handler for runtime automation
+void CustomProcessorNode::registerControlHandler(
+ const std::string &identifier,
+ GenericControlHandler handler) {
+ s_controlHandlersByIdentifier[identifier] = std::move(handler);
+}
+
+// Removes a control handler
+void CustomProcessorNode::unregisterControlHandler(
+ const std::string &identifier) {
+ s_controlHandlersByIdentifier.erase(identifier);
+}
+
+// Core processing method routed by selected mode
+void CustomProcessorNode::processNode(
+ const std::shared_ptr &processingBus,
+ int framesToProcess) {
+ if (!processor_)
+ return;
+
+ int numChannels = processingBus->getNumberOfChannels();
+ if (preallocatedOutputBuffers_.size() != static_cast(numChannels)) {
+ preallocatedOutputBuffers_.resize(numChannels);
+ }
+
+ for (int ch = 0; ch < numChannels; ++ch) {
+ if (preallocatedOutputBuffers_[ch].size() !=
+ static_cast(framesToProcess)) {
+ preallocatedOutputBuffers_[ch].resize(framesToProcess);
+ }
+ }
+
+ switch (processorMode_) {
+ case ProcessorMode::ProcessThrough:
+ processThrough(processingBus, framesToProcess);
+ break;
+ case ProcessorMode::ProcessInPlace:
+ default:
+ processInPlace(processingBus, framesToProcess);
+ break;
+ }
+}
+
+// Executes in-place processing on the shared audio buffer
+void CustomProcessorNode::processInPlace(
+ const std::shared_ptr &bus,
+ int frames) {
+ if (!processor_)
+ return;
+
+ int numChannels = bus->getNumberOfChannels();
+ std::vector channelData(numChannels);
+ for (int ch = 0; ch < numChannels; ++ch) {
+ channelData[ch] = bus->getChannel(ch)->getData();
+ }
+
+ processor_->processInPlace(channelData.data(), numChannels, frames);
+}
+
+// Executes processing using separate input and output buffers with
+// preallocation
+void CustomProcessorNode::processThrough(
+ const std::shared_ptr &bus,
+ int frames) {
+ if (!processor_)
+ return;
+
+ int numChannels = bus->getNumberOfChannels();
+ std::vector input(numChannels);
+ std::vector output(numChannels);
+
+ for (int ch = 0; ch < numChannels; ++ch) {
+ input[ch] = bus->getChannel(ch)->getData();
+ output[ch] = preallocatedOutputBuffers_[ch].data();
+ }
+
+ processor_->processThrough(input.data(), output.data(), numChannels, frames);
+
+ for (int ch = 0; ch < numChannels; ++ch) {
+ std::memcpy(
+ bus->getChannel(ch)->getData(), output[ch], sizeof(float) * frames);
+ }
+}
+
+} // namespace audioapi
diff --git a/packages/react-native-audio-api/common/cpp/audioapi/core/effects/CustomProcessorNode.h b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/CustomProcessorNode.h
new file mode 100644
index 000000000..a4369af7f
--- /dev/null
+++ b/packages/react-native-audio-api/common/cpp/audioapi/core/effects/CustomProcessorNode.h
@@ -0,0 +1,186 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include