Skip to content

Commit 83dfe60

Browse files
author
Maciej Makowski
committed
feat: naive approach streaming in beloved cpp
1 parent 42c2533 commit 83dfe60

File tree

13 files changed

+454
-31
lines changed

13 files changed

+454
-31
lines changed

apps/common-app/src/examples/AudioFile/AudioFile.tsx

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
AudioBuffer,
55
AudioContext,
66
AudioBufferSourceNode,
7+
AudioBufferStreamSourceNode,
78
AudioManager,
89
} from 'react-native-audio-api';
910

@@ -25,13 +26,16 @@ const AudioFile: FC = () => {
2526
const [isLoading, setIsLoading] = useState(false);
2627

2728
const [offset, setOffset] = useState(0);
28-
const [playbackRate, setPlaybackRate] = useState(INITIAL_RATE);
29+
const [playbackRate, setPlaybackRate] = useState(INITIAL_RATE * 1.5);
2930
const [detune, setDetune] = useState(INITIAL_DETUNE);
3031

31-
const [audioBuffer, setAudioBuffer] = useState<AudioBuffer | null>(null);
32+
const [audioBuffers, setAudioBuffers] = useState<AudioBuffer[]>([]);
3233

3334
const audioContextRef = useRef<AudioContext | null>(null);
3435
const bufferSourceRef = useRef<AudioBufferSourceNode | null>(null);
36+
const bufferStreamSourceRef = useRef<AudioBufferStreamSourceNode | null>(
37+
null
38+
);
3539

3640
const handlePlaybackRateChange = (newValue: number) => {
3741
setPlaybackRate(newValue);
@@ -55,39 +59,28 @@ const AudioFile: FC = () => {
5559
}
5660

5761
if (isPlaying) {
58-
bufferSourceRef.current?.stop(audioContextRef.current.currentTime);
62+
bufferStreamSourceRef.current?.stop(audioContextRef.current.currentTime);
5963
AudioManager.setLockScreenInfo({
6064
state: 'state_paused',
6165
});
6266
} else {
63-
if (!audioBuffer) {
67+
if (!audioBuffers) {
6468
fetchAudioBuffer();
6569
}
6670

67-
AudioManager.setLockScreenInfo({
68-
state: 'state_playing',
69-
});
71+
const now = audioContextRef.current.currentTime;
7072

71-
AudioManager.observeAudioInterruptions(true);
73+
bufferStreamSourceRef.current =
74+
audioContextRef.current.createBufferStreamSource();
75+
for (let i = 0; i < audioBuffers.length; i++) {
76+
bufferStreamSourceRef.current.enqueueAudioBuffer(audioBuffers[i]);
77+
}
7278

73-
bufferSourceRef.current = audioContextRef.current.createBufferSource({
74-
pitchCorrection: true,
75-
});
76-
bufferSourceRef.current.buffer = audioBuffer;
77-
bufferSourceRef.current.loop = true;
78-
bufferSourceRef.current.onended = (event) => {
79-
setOffset((_prev) => event.value || 0);
80-
};
81-
bufferSourceRef.current.loopStart = LOOP_START;
82-
bufferSourceRef.current.loopEnd = LOOP_END;
83-
bufferSourceRef.current.playbackRate.value = playbackRate;
84-
bufferSourceRef.current.detune.value = detune;
85-
bufferSourceRef.current.connect(audioContextRef.current.destination);
86-
87-
bufferSourceRef.current.start(
88-
audioContextRef.current.currentTime,
89-
offset
79+
bufferStreamSourceRef.current.connect(
80+
audioContextRef.current.destination
9081
);
82+
bufferStreamSourceRef.current.playbackRate.value = playbackRate;
83+
bufferStreamSourceRef.current.start(now);
9184
}
9285

9386
setIsPlaying((prev) => !prev);
@@ -106,7 +99,37 @@ const AudioFile: FC = () => {
10699
return null;
107100
});
108101

109-
setAudioBuffer(buffer);
102+
if (!buffer) {
103+
setIsLoading(false);
104+
return;
105+
}
106+
107+
const data = buffer.getChannelData(0);
108+
const buffers: AudioBuffer[] = [];
109+
110+
for (let i = 0; i < 25; i++) {
111+
const buffer1 = audioContextRef.current!.createBuffer(
112+
buffer.numberOfChannels,
113+
buffer.sampleRate,
114+
buffer.sampleRate
115+
);
116+
117+
const channelData = buffer1.getChannelData(0);
118+
119+
for (let j = 0; j < buffer.sampleRate; j++) {
120+
channelData[j] = data[j + i * buffer.sampleRate];
121+
}
122+
123+
// if (i === 1) {
124+
// for (let j = 0; j < 1000; j++) {
125+
// console.log(channelData[j]);
126+
// }
127+
// }
128+
129+
buffers.push(buffer1);
130+
}
131+
132+
setAudioBuffers(buffers);
110133

111134
setIsLoading(false);
112135
}, []);
@@ -171,7 +194,7 @@ const AudioFile: FC = () => {
171194
<Button
172195
title={isPlaying ? 'Stop' : 'Play'}
173196
onPress={handlePress}
174-
disabled={!audioBuffer}
197+
disabled={audioBuffers.length === 0}
175198
/>
176199
<Spacer.Vertical size={49} />
177200
<Slider

apps/fabric-example/ios/FabricExample.xcodeproj/project.pbxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@
260260
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
261261
CLANG_ENABLE_MODULES = YES;
262262
CURRENT_PROJECT_VERSION = 1;
263-
DEVELOPMENT_TEAM = WC59N2X3FV;
263+
DEVELOPMENT_TEAM = 852ZJU38RC;
264264
ENABLE_BITCODE = NO;
265265
INFOPLIST_FILE = FabricExample/Info.plist;
266266
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
@@ -274,7 +274,7 @@
274274
"-ObjC",
275275
"-lc++",
276276
);
277-
PRODUCT_BUNDLE_IDENTIFIER = "fdsfsdfsdcom-esegsefser.--PRODUCT-NAME-rfc1034identifier-";
277+
PRODUCT_BUNDLE_IDENTIFIER = "fdsfsdfsdcom-esegsefshshshssher.--PRODUCT-NAME-rfc1034identifier-";
278278
PRODUCT_NAME = FabricExample;
279279
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
280280
SWIFT_VERSION = 5.0;
@@ -289,7 +289,7 @@
289289
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
290290
CLANG_ENABLE_MODULES = YES;
291291
CURRENT_PROJECT_VERSION = 1;
292-
DEVELOPMENT_TEAM = WC59N2X3FV;
292+
DEVELOPMENT_TEAM = 852ZJU38RC;
293293
INFOPLIST_FILE = FabricExample/Info.plist;
294294
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
295295
LD_RUNPATH_SEARCH_PATHS = (
@@ -302,7 +302,7 @@
302302
"-ObjC",
303303
"-lc++",
304304
);
305-
PRODUCT_BUNDLE_IDENTIFIER = "fdsfsdfsdcom-esegsefser.--PRODUCT-NAME-rfc1034identifier-";
305+
PRODUCT_BUNDLE_IDENTIFIER = "fdsfsdfsdcom-esegsefshshshssher.--PRODUCT-NAME-rfc1034identifier-";
306306
PRODUCT_NAME = FabricExample;
307307
SWIFT_VERSION = 5.0;
308308
VERSIONING_SYSTEM = "apple-generic";
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#pragma once
2+
3+
#include <audioapi/HostObjects/AudioBufferHostObject.h>
4+
#include <audioapi/core/sources/AudioBufferStreamSourceNode.h>
5+
#include <audioapi/HostObjects/AudioParamHostObject.h>
6+
#include <audioapi/HostObjects/AudioScheduledSourceNodeHostObject.h>
7+
8+
#include <memory>
9+
#include <vector>
10+
11+
namespace audioapi {
12+
using namespace facebook;
13+
14+
class AudioBufferStreamSourceNodeHostObject
15+
: public AudioScheduledSourceNodeHostObject {
16+
public:
17+
explicit AudioBufferStreamSourceNodeHostObject(
18+
const std::shared_ptr<AudioBufferStreamSourceNode> &node)
19+
: AudioScheduledSourceNodeHostObject(node) {
20+
addGetters(
21+
JSI_EXPORT_PROPERTY_GETTER(AudioBufferStreamSourceNodeHostObject, detune),
22+
JSI_EXPORT_PROPERTY_GETTER(AudioBufferStreamSourceNodeHostObject, playbackRate));
23+
24+
// start method is overridden in this class
25+
functions_->erase("start");
26+
27+
addFunctions(
28+
JSI_EXPORT_FUNCTION(AudioBufferStreamSourceNodeHostObject, start),
29+
JSI_EXPORT_FUNCTION(AudioBufferStreamSourceNodeHostObject, enqueueAudioBuffer));
30+
}
31+
32+
JSI_PROPERTY_GETTER(detune) {
33+
auto audioBufferSourceNode =
34+
std::static_pointer_cast<AudioBufferStreamSourceNode>(node_);
35+
auto detune = audioBufferSourceNode->getDetuneParam();
36+
auto detuneHostObject = std::make_shared<AudioParamHostObject>(detune);
37+
return jsi::Object::createFromHostObject(runtime, detuneHostObject);
38+
}
39+
40+
JSI_PROPERTY_GETTER(playbackRate) {
41+
auto audioBufferSourceNode =
42+
std::static_pointer_cast<AudioBufferStreamSourceNode>(node_);
43+
auto playbackRate = audioBufferSourceNode->getPlaybackRateParam();
44+
auto playbackRateHostObject =
45+
std::make_shared<AudioParamHostObject>(playbackRate);
46+
return jsi::Object::createFromHostObject(runtime, playbackRateHostObject);
47+
}
48+
49+
JSI_HOST_FUNCTION(start) {
50+
auto when = args[0].getNumber();
51+
auto offset = args[1].getNumber();
52+
53+
auto audioBufferStreamSourceNode =
54+
std::static_pointer_cast<AudioBufferStreamSourceNode>(node_);
55+
56+
audioBufferStreamSourceNode->start(when, offset);
57+
58+
return jsi::Value::undefined();
59+
}
60+
61+
JSI_HOST_FUNCTION(enqueueAudioBuffer) {
62+
auto audioBufferStreamSourceNode =
63+
std::static_pointer_cast<AudioBufferStreamSourceNode>(node_);
64+
65+
auto audioBufferHostObject =
66+
args[0].getObject(runtime).asHostObject<AudioBufferHostObject>(runtime);
67+
68+
audioBufferStreamSourceNode->enqueueAudioBuffer(audioBufferHostObject->audioBuffer_);
69+
70+
return jsi::Value::undefined();
71+
}
72+
};
73+
74+
} // namespace audioapi

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <audioapi/jsi/JsiPromise.h>
55
#include <audioapi/HostObjects/AudioBufferHostObject.h>
66
#include <audioapi/HostObjects/AudioBufferSourceNodeHostObject.h>
7+
#include <audioapi/HostObjects/AudioBufferStreamSourceNodeHostObject.h>
78
#include <audioapi/HostObjects/AudioDestinationNodeHostObject.h>
89
#include <audioapi/core/BaseAudioContext.h>
910
#include <audioapi/HostObjects/BiquadFilterNodeHostObject.h>
@@ -43,6 +44,7 @@ class BaseAudioContextHostObject : public JsiHostObject {
4344
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createStereoPanner),
4445
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createBiquadFilter),
4546
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createBufferSource),
47+
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createBufferStreamSource),
4648
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createBuffer),
4749
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createPeriodicWave),
4850
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createAnalyser),
@@ -103,6 +105,13 @@ class BaseAudioContextHostObject : public JsiHostObject {
103105
return jsi::Object::createFromHostObject(runtime, bufferSourceHostObject);
104106
}
105107

108+
JSI_HOST_FUNCTION(createBufferStreamSource) {
109+
auto bufferSource = context_->createBufferStreamSource();
110+
auto bufferStreamSourceHostObject =
111+
std::make_shared<AudioBufferStreamSourceNodeHostObject>(bufferSource);
112+
return jsi::Object::createFromHostObject(runtime, bufferStreamSourceHostObject);
113+
}
114+
106115
JSI_HOST_FUNCTION(createBuffer) {
107116
auto numberOfChannels = static_cast<int>(args[0].getNumber());
108117
auto length = static_cast<size_t>(args[1].getNumber());

packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <audioapi/core/effects/StereoPannerNode.h>
77
#include <audioapi/core/sources/AudioBuffer.h>
88
#include <audioapi/core/sources/AudioBufferSourceNode.h>
9+
#include <audioapi/core/sources/AudioBufferStreamSourceNode.h>
910
#include <audioapi/core/sources/OscillatorNode.h>
1011
#include <audioapi/core/utils/AudioDecoder.h>
1112
#include <audioapi/core/utils/AudioNodeManager.h>
@@ -79,6 +80,13 @@ std::shared_ptr<AudioBufferSourceNode> BaseAudioContext::createBufferSource(
7980
return bufferSource;
8081
}
8182

83+
std::shared_ptr<AudioBufferStreamSourceNode>
84+
BaseAudioContext::createBufferStreamSource() {
85+
auto bufferSource = std::make_shared<AudioBufferStreamSourceNode>(this);
86+
nodeManager_->addSourceNode(bufferSource);
87+
return bufferSource;
88+
}
89+
8290
std::shared_ptr<AudioBuffer> BaseAudioContext::createBuffer(
8391
int numberOfChannels,
8492
size_t length,

packages/react-native-audio-api/common/cpp/audioapi/core/BaseAudioContext.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class AudioNodeManager;
2424
class BiquadFilterNode;
2525
class AudioDestinationNode;
2626
class AudioBufferSourceNode;
27+
class AudioBufferStreamSourceNode;
2728
class AudioDecoder;
2829
class AnalyserNode;
2930
class AudioEventHandlerRegistry;
@@ -44,6 +45,7 @@ class BaseAudioContext {
4445
std::shared_ptr<StereoPannerNode> createStereoPanner();
4546
std::shared_ptr<BiquadFilterNode> createBiquadFilter();
4647
std::shared_ptr<AudioBufferSourceNode> createBufferSource(bool pitchCorrection);
48+
std::shared_ptr<AudioBufferStreamSourceNode> createBufferStreamSource();
4749
static std::shared_ptr<AudioBuffer>
4850
createBuffer(int numberOfChannels, size_t length, float sampleRate);
4951
std::shared_ptr<PeriodicWave> createPeriodicWave(

packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBuffer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class AudioBuffer : public std::enable_shared_from_this<AudioBuffer> {
3535

3636
private:
3737
friend class AudioBufferSourceNode;
38+
friend class AudioBufferStreamSourceNode;
3839

3940
std::shared_ptr<AudioBus> bus_;
4041
};

0 commit comments

Comments
 (0)