Skip to content
Closed
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
aff8dac
feat: prepare headers for indexed connect
miloszwielgus Oct 10, 2025
fb20be1
feat: implement NodeConnections
miloszwielgus Oct 16, 2025
445194d
feat: adjusted cpp layer to indexed connect
miloszwielgus Oct 16, 2025
9221b15
feat: implement channel merger and splitter cpp layer
miloszwielgus Oct 16, 2025
612464b
feat: implement jsi layer for merger/splitter and indexed connect
miloszwielgus Oct 16, 2025
0e05c4e
fix: lint
miloszwielgus Oct 16, 2025
e09fcd8
feat: implement ts layer
miloszwielgus Oct 16, 2025
e5bc9e2
feat: implement example for testing
miloszwielgus Oct 16, 2025
f617171
fix: adjust AudioParam to indexed connect
miloszwielgus Oct 16, 2025
0fa83a5
fix: revert to originall SterePanner impl with small adjustment
miloszwielgus Oct 16, 2025
3b7aab0
fix: delete pointless resizes, lint
miloszwielgus Oct 16, 2025
8ce140f
feat: errors thrown closer to spec
miloszwielgus Oct 16, 2025
ecdd102
fix: lint
miloszwielgus Oct 16, 2025
c7b7f97
feat: web support for merger/splitter
miloszwielgus Oct 17, 2025
e72cf31
Merge remote-tracking branch 'main' into indexed-connect
miloszwielgus Oct 17, 2025
5ad73dd
fix: correctly initialize input processing buses
miloszwielgus Oct 20, 2025
21c98ca
fix: make processAudio void, comments, lint
miloszwielgus Oct 20, 2025
0035e95
fix: de-ref the example
miloszwielgus Oct 20, 2025
cc6e877
fix: delete unsued methods
miloszwielgus Oct 20, 2025
0e9a73b
feat: docs for indexed connect
miloszwielgus Oct 20, 2025
6c13ae9
fix: use RENDER_QUANTUM_SIZE to create needed buses, instead of curre…
miloszwielgus Oct 22, 2025
35a18dc
fix: nits
miloszwielgus Oct 23, 2025
de2f27e
fix: fixed internal summing / mixing
miloszwielgus Oct 23, 2025
e5b55f5
feat: add ocillator to the example
miloszwielgus Oct 23, 2025
5da3bc7
feat: docs for splitter/merger
miloszwielgus Oct 23, 2025
cab643e
refactor: make processInputAtIndex more readable
miloszwielgus Oct 23, 2025
8bb4b0e
feat: implement InvalidAccessError propagation on disconnect
miloszwielgus Oct 24, 2025
cae36e7
fix: nits, styling
miloszwielgus Oct 27, 2025
badbff5
Merge remote-tracking branch 'upstream/main' into indexed-connect
miloszwielgus Oct 27, 2025
1614fd0
Merge remote-tracking branch 'main' into indexed-connect
miloszwielgus Oct 27, 2025
5065d4c
feat: update web-audio-api coverage
miloszwielgus Oct 27, 2025
49a392b
fix: reallocate output bus if channel count does not match
miloszwielgus Oct 28, 2025
0cd0b3e
feat: merger/mergerless example
miloszwielgus Oct 28, 2025
1444374
fix: got rid of unnecesary getters in host objects
miloszwielgus Oct 28, 2025
4bbc411
fix: converage in alphabetical order
miloszwielgus Oct 28, 2025
a93ddfd
fix: unsigned -> unsigned int
miloszwielgus Oct 30, 2025
c288e4d
refactor: move connection types to a separate file
miloszwielgus Oct 30, 2025
cc10d04
fix: nitpicks
miloszwielgus Nov 3, 2025
ca71ad2
fix: comments in NodeConnections.h, lint
miloszwielgus Nov 3, 2025
aa2c248
fix: implicit bool casts, unsafe method delete
miloszwielgus Nov 13, 2025
86b3e71
chore: merge remote-tracking branch main into indexed-connect
miloszwielgus Nov 13, 2025
fc7d761
fix: temp fix to see if convolver works
miloszwielgus Nov 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
243 changes: 243 additions & 0 deletions apps/common-app/src/examples/MergerSplitter/MergerSplitter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
import React, { useRef, useState, useEffect, FC, useMemo } from 'react';
import { Alert, ActivityIndicator } from 'react-native';
import {
AudioContext,
AudioBufferSourceNode,
AudioBuffer,
} from 'react-native-audio-api';

import { Container, Slider, Spacer, Button } from '../../components';

// test url pointing to my public repo, to be changed / deleted
const AUDIO_URL =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

possibly could be added to audiodocs/static/audio with informative description -> what is it, number of channels, SR

'https://github.com/miloszwielgus/test-files/raw/refs/heads/main/example-true-5-1.ogg';

const INITIAL_GAIN = 0.5;
const labelWidth = 100;

const SplitterMerger: FC = () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inconsistent file name with component name

const [isPlaying, setIsPlaying] = useState(false);
const [isLoading, setIsLoading] = useState(false);

const [gain1, setGain1] = useState(INITIAL_GAIN);
const [gain2, setGain2] = useState(INITIAL_GAIN);
const [gain3, setGain3] = useState(INITIAL_GAIN);
const [gain4, setGain4] = useState(INITIAL_GAIN);
const [gain5, setGain5] = useState(INITIAL_GAIN);
const [gain6, setGain6] = useState(INITIAL_GAIN);

const {
context,
splitter,
merger,
gainNode1,
gainNode2,
gainNode3,
gainNode4,
gainNode5,
gainNode6,
} = useMemo(() => {
const ctx = new AudioContext();

const splitNode = ctx.createChannelSplitter(6);
const mergeNode = ctx.createChannelMerger(6);

const g1 = ctx.createGain();
g1.gain.value = INITIAL_GAIN;
const g2 = ctx.createGain();
g2.gain.value = INITIAL_GAIN;
const g3 = ctx.createGain();
g3.gain.value = INITIAL_GAIN;
const g4 = ctx.createGain();
g4.gain.value = INITIAL_GAIN;
const g5 = ctx.createGain();
g5.gain.value = INITIAL_GAIN;
const g6 = ctx.createGain();
g6.gain.value = INITIAL_GAIN;

splitNode.connect(g1, 0, 0);
g1.connect(mergeNode, 0, 0);
splitNode.connect(g2, 1, 0);
g2.connect(mergeNode, 0, 1);
splitNode.connect(g3, 2, 0);
g3.connect(mergeNode, 0, 2);
splitNode.connect(g4, 3, 0);
g4.connect(mergeNode, 0, 3);
splitNode.connect(g5, 4, 0);
g5.connect(mergeNode, 0, 4);
splitNode.connect(g6, 5, 0);
g6.connect(mergeNode, 0, 5);

mergeNode.connect(ctx.destination);

return {
context: ctx,
splitter: splitNode,
merger: mergeNode,
gainNode1: g1,
gainNode2: g2,
gainNode3: g3,
gainNode4: g4,
gainNode5: g5,
gainNode6: g6,
};
}, []);

const [audioBuffer, setAudioBuffer] = useState<AudioBuffer | null>(null);

const sourceNodeRef = useRef<AudioBufferSourceNode | null>(null);

useEffect(() => {
const fetchAndDecodeAudio = async () => {
setIsLoading(true);
try {
const response = await fetch(AUDIO_URL);
const arrayBuffer = await response.arrayBuffer();
const buffer = await context.decodeAudioData(arrayBuffer);
setAudioBuffer(buffer);
} catch (error) {
console.error('Failed to fetch or decode audio:', error);
Alert.alert(
'Error',
'Could not load the audio file. Check network and URL.'
);
} finally {
setIsLoading(false);
}
};

fetchAndDecodeAudio();

return () => {
if (sourceNodeRef.current) {
sourceNodeRef.current.stop(0);
sourceNodeRef.current.disconnect();
sourceNodeRef.current = null;
}
context.close();
};
}, [context]);

const handlePlayPause = () => {
if (isPlaying) {
if (sourceNodeRef.current) {
sourceNodeRef.current.stop(0);
sourceNodeRef.current.disconnect();
sourceNodeRef.current = null;
}
setIsPlaying(false);
} else {
if (!audioBuffer) return;

const sourceNode = context.createBufferSource();
sourceNode.buffer = audioBuffer;
sourceNode.loop = true;

sourceNode.connect(splitter);

sourceNode.start(0);
sourceNodeRef.current = sourceNode;
setIsPlaying(true);
}
};

const handleGain1Change = (value: number) => {
setGain1(value);
gainNode1.gain.value = value;
};
const handleGain2Change = (value: number) => {
setGain2(value);
gainNode2.gain.value = value;
};
const handleGain3Change = (value: number) => {
setGain3(value);
gainNode3.gain.value = value;
};
const handleGain4Change = (value: number) => {
setGain4(value);
gainNode4.gain.value = value;
};
const handleGain5Change = (value: number) => {
setGain5(value);
gainNode5.gain.value = value;
};
const handleGain6Change = (value: number) => {
setGain6(value);
gainNode6.gain.value = value;
};

return (
<Container centered>
{isLoading && <ActivityIndicator size="large" color="#FFFFFF" />}
<Button
onPress={handlePlayPause}
title={isPlaying ? 'Stop' : 'Play'}
disabled={isLoading || !audioBuffer}
/>
<Spacer.Vertical size={30} />

<Slider
label="Gain Path 1"
value={gain1}
onValueChange={handleGain1Change}
min={0.0}
max={1.5}
step={0.01}
minLabelWidth={labelWidth}
/>
<Spacer.Vertical size={15} />
<Slider
label="Gain Path 2"
value={gain2}
onValueChange={handleGain2Change}
min={0.0}
max={1.5}
step={0.01}
minLabelWidth={labelWidth}
/>
<Spacer.Vertical size={15} />
<Slider
label="Gain Path 3"
value={gain3}
onValueChange={handleGain3Change}
min={0.0}
max={1.5}
step={0.01}
minLabelWidth={labelWidth}
/>
<Spacer.Vertical size={15} />
<Slider
label="Gain Path 4"
value={gain4}
onValueChange={handleGain4Change}
min={0.0}
max={1.5}
step={0.01}
minLabelWidth={labelWidth}
/>
<Spacer.Vertical size={15} />
<Slider
label="Gain Path 5"
value={gain5}
onValueChange={handleGain5Change}
min={0.0}
max={1.5}
step={0.01}
minLabelWidth={labelWidth}
/>
<Spacer.Vertical size={15} />
<Slider
label="Gain Path 6"
value={gain6}
onValueChange={handleGain6Change}
min={0.0}
max={1.5}
step={0.01}
minLabelWidth={labelWidth}
/>
<Spacer.Vertical size={30} />
</Container>
);
};

export default SplitterMerger;
1 change: 1 addition & 0 deletions apps/common-app/src/examples/MergerSplitter/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './MergerSplitter';
10 changes: 9 additions & 1 deletion apps/common-app/src/examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Record from './Record/Record';
import PlaybackSpeed from './PlaybackSpeed/PlaybackSpeed';
import Worklets from './Worklets/Worklets';
import Streaming from './Streaming/Streaming';
import SplitterMerger from './MergerSplitter/MergerSplitter';

type NavigationParamList = {
Oscillator: undefined;
Expand All @@ -25,6 +26,7 @@ type NavigationParamList = {
Record: undefined;
Worklets: undefined;
Streamer: undefined;
SplitterMerger: undefined;
};

export type ExampleKey = keyof NavigationParamList;
Expand Down Expand Up @@ -103,5 +105,11 @@ export const Examples: Example[] = [
title: 'Streamer',
subtitle: 'Stream audio from a URL',
screen: Streaming,
}
},
{
key: 'SplitterMerger',
title: 'Channel Splitter and Merger',
subtitle: 'Split and merge audio channels',
screen: SplitterMerger,
},
] as const;
12 changes: 6 additions & 6 deletions apps/fabric-example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2342,7 +2342,7 @@ PODS:
- React-perflogger (= 0.81.0)
- React-utils (= 0.81.0)
- SocketRocket
- RNAudioAPI (0.9.0):
- RNAudioAPI (0.10.0):
- boost
- DoubleConversion
- fast_float
Expand All @@ -2368,10 +2368,10 @@ PODS:
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- RNAudioAPI/audioapi (= 0.9.0)
- RNAudioAPI/audioapi (= 0.10.0)
- SocketRocket
- Yoga
- RNAudioAPI/audioapi (0.9.0):
- RNAudioAPI/audioapi (0.10.0):
- boost
- DoubleConversion
- fast_float
Expand All @@ -2397,10 +2397,10 @@ PODS:
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- RNAudioAPI/audioapi/ios (= 0.9.0)
- RNAudioAPI/audioapi/ios (= 0.10.0)
- SocketRocket
- Yoga
- RNAudioAPI/audioapi/ios (0.9.0):
- RNAudioAPI/audioapi/ios (0.10.0):
- boost
- DoubleConversion
- fast_float
Expand Down Expand Up @@ -3081,7 +3081,7 @@ SPEC CHECKSUMS:
ReactAppDependencyProvider: c91900fa724baee992f01c05eeb4c9e01a807f78
ReactCodegen: 8125d6ee06ea06f48f156cbddec5c2ca576d62e6
ReactCommon: 116d6ee71679243698620d8cd9a9042541e44aa6
RNAudioAPI: d86d59666cfd7544d3cb0030239dc698404fe117
RNAudioAPI: 16890c10ec7749f97ebea03082411dec7d859849
RNGestureHandler: 3a73f098d74712952870e948b3d9cf7b6cae9961
RNReanimated: a035264789d1f64cb5adba7085d6aac6e9ec70a7
RNScreens: 6ced6ae8a526512a6eef6e28c2286e1fc2d378c3
Expand Down
Loading