Skip to content

feat: streamer node #608

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,4 @@ react-native-audio-api*.tgz
.env

compile_commands.json
openssl-prebuilt/
75 changes: 75 additions & 0 deletions apps/common-app/src/examples/Streaming/Streaming.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React, { useRef, FC, useEffect, act } from 'react';
import {
AudioContext,
StreamerNode,
GainNode
} from 'react-native-audio-api';

import { Container, Button } from '../../components';
import { View } from 'react-native';

const SAMPLE_RATE = 16000;

const Streaming: FC = () => {
const streamerRef = useRef<StreamerNode | null>(null);
const aCtxRef = useRef<AudioContext | null>(null);
const gainRef = useRef<GainNode | null>(null);

useEffect(() => {
const actx = new AudioContext({ sampleRate: SAMPLE_RATE });
aCtxRef.current = actx;
gainRef.current = actx.createGain();
streamerRef.current = actx.createStreamer();
return () => {
aCtxRef.current?.close();
streamerRef.current?.stop();
}
}, []);

const startStreaming = () => {
if (!aCtxRef.current || !gainRef.current || !streamerRef.current) {
console.error('AudioContext or gain or streamer is not initialized');
return;
}
streamerRef.current = aCtxRef.current.createStreamer();

streamerRef.current.initialize('https://stream.radioparadise.com/aac-320');
streamerRef.current.connect(gainRef.current);
gainRef.current.connect(aCtxRef.current.destination);
streamerRef.current.startStreaming();
streamerRef.current.start(aCtxRef.current.currentTime);
};

const stopStreaming = () => {
if (streamerRef.current) {
streamerRef.current.stop(aCtxRef.current?.currentTime);
// streamerRef.current.stopStreaming();
streamerRef.current.disconnect(gainRef.current!);
console.log('Streaming stopped');
} else {
console.error('StreamerNode is not initialized');
}
};


return (
<Container style={{ gap: 40 }}>
<View style={{ alignItems: 'center', justifyContent: 'center', gap: 5 }}>
<Button title="Start streaming" onPress={startStreaming} />
<Button title="Stop streaming" onPress={stopStreaming} />
<Button title="Volume to 0.5" onPress={() => {
if (gainRef.current) {
gainRef.current.gain.value = 0.5;
}
}} />
<Button title="Volume to 2.0" onPress={() => {
if (gainRef.current) {
gainRef.current.gain.value = 2.0;
}
}} />
</View>
</Container>
);
};

export default Streaming;
8 changes: 8 additions & 0 deletions apps/common-app/src/examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import AudioVisualizer from './AudioVisualizer';
import OfflineRendering from './OfflineRendering';
import Record from './Record/Record';
import PlaybackSpeed from './PlaybackSpeed/PlaybackSpeed';
import Streaming from './Streaming/Streaming';

type NavigationParamList = {
Oscillator: undefined;
Expand All @@ -22,6 +23,7 @@ type NavigationParamList = {
AudioVisualizer: undefined;
OfflineRendering: undefined;
Record: undefined;
Streamer: undefined;
};

export type ExampleKey = keyof NavigationParamList;
Expand Down Expand Up @@ -95,4 +97,10 @@ export const Examples: Example[] = [
subtitle: 'Record audio',
screen: Record,
},
{
key: 'Streamer',
title: 'Streamer',
subtitle: 'Stream audio from a URL',
screen: Streaming,
}
] as const;
2 changes: 1 addition & 1 deletion apps/fabric-example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2961,7 +2961,7 @@ SPEC CHECKSUMS:
ReactAppDependencyProvider: 3267432b637c9b38e86961b287f784ee1b08dde0
ReactCodegen: d82f538f70f00484d418803f74b5a0ea09cc8689
ReactCommon: b028d09a66e60ebd83ca59d8cc9a1216360db147
RNAudioAPI: a2e1467619b270f2b7c922528e624063b13c77c6
RNAudioAPI: baa31e005217161820afff39e03e1f208977a310
RNGestureHandler: eeb622199ef1fb3a076243131095df1c797072f0
RNReanimated: 402e6a3b84071df4da6264630a1b99962a113d2d
RNScreens: ee2abe7e0c548eed14e92742e81ed991165c56aa
Expand Down
8 changes: 8 additions & 0 deletions packages/react-native-audio-api/RNAudioAPI.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ s.pod_target_xcconfig = {
#{external_dir}/include
#{external_dir}/include/opus
#{external_dir}/include/vorbis
#{external_dir}/include/libavcodec
#{external_dir}/include/libavformat
#{external_dir}/include/libavutil
#{external_dir}/include/libswresample
].join(" "),
'OTHER_CFLAGS' => "$(inherited) #{folly_flags} #{fabric_flags} #{version_flag}"
}
Expand All @@ -62,6 +66,10 @@ s.user_target_xcconfig = {
-force_load #{lib_dir}/libvorbis.a
-force_load #{lib_dir}/libvorbisenc.a
-force_load #{lib_dir}/libvorbisfile.a
-force_load #{lib_dir}/libavcodec.a
-force_load #{lib_dir}/libavformat.a
-force_load #{lib_dir}/libavutil.a
-force_load #{lib_dir}/libswresample.a
].join(" ")
}
# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <audioapi/HostObjects/StereoPannerNodeHostObject.h>
#include <audioapi/HostObjects/AnalyserNodeHostObject.h>
#include <audioapi/HostObjects/RecorderAdapterNodeHostObject.h>
#include <audioapi/HostObjects/StreamerNodeHostObject.h>

#include <jsi/jsi.h>
#include <memory>
Expand Down Expand Up @@ -42,6 +43,7 @@ class BaseAudioContextHostObject : public JsiHostObject {
addFunctions(
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createRecorderAdapter),
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createOscillator),
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createStreamer),
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createGain),
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createStereoPanner),
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createBiquadFilter),
Expand Down Expand Up @@ -88,6 +90,13 @@ class BaseAudioContextHostObject : public JsiHostObject {
return jsi::Object::createFromHostObject(runtime, oscillatorHostObject);
}

JSI_HOST_FUNCTION(createStreamer) {
auto streamer = context_->createStreamer();
auto streamerHostObject =
std::make_shared<StreamerNodeHostObject>(streamer);
return jsi::Object::createFromHostObject(runtime, streamerHostObject);
}

JSI_HOST_FUNCTION(createGain) {
auto gain = context_->createGain();
auto gainHostObject = std::make_shared<GainNodeHostObject>(gain);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#pragma once

#include <audioapi/HostObjects/AudioScheduledSourceNodeHostObject.h>
#include <audioapi/HostObjects/AudioParamHostObject.h>
#include <audioapi/HostObjects/PeriodicWaveHostObject.h>
#include <audioapi/core/sources/StreamerNode.h>

#include <memory>
#include <string>
#include <vector>

namespace audioapi {
using namespace facebook;

class StreamerNodeHostObject : public AudioScheduledSourceNodeHostObject {
public:
explicit StreamerNodeHostObject(
const std::shared_ptr<StreamerNode> &node)
: AudioScheduledSourceNodeHostObject(node) {
addFunctions(JSI_EXPORT_FUNCTION(StreamerNodeHostObject, initialize),
JSI_EXPORT_FUNCTION(StreamerNodeHostObject, startStreaming),
JSI_EXPORT_FUNCTION(StreamerNodeHostObject, stopStreaming));
}

JSI_HOST_FUNCTION(initialize) {
auto streamerNode = std::static_pointer_cast<StreamerNode>(node_);
auto path = args[0].getString(runtime).utf8(runtime);
auto result = streamerNode->initialize(path);
return jsi::Value(result);
}

JSI_HOST_FUNCTION(startStreaming) {
auto streamerNode = std::static_pointer_cast<StreamerNode>(node_);
streamerNode->startStreaming();
return jsi::Value::undefined();
}

JSI_HOST_FUNCTION(stopStreaming) {
auto streamerNode = std::static_pointer_cast<StreamerNode>(node_);
streamerNode->stopStreaming();
return jsi::Value::undefined();
}
};
} // namespace audioapi
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <audioapi/core/sources/AudioBufferSourceNode.h>
#include <audioapi/core/sources/OscillatorNode.h>
#include <audioapi/core/sources/RecorderAdapterNode.h>
#include <audioapi/core/sources/StreamerNode.h>
#include <audioapi/core/utils/AudioDecoder.h>
#include <audioapi/core/utils/AudioNodeManager.h>
#include <audioapi/events/AudioEventHandlerRegistry.h>
Expand Down Expand Up @@ -69,6 +70,12 @@ std::shared_ptr<OscillatorNode> BaseAudioContext::createOscillator() {
return oscillator;
}

std::shared_ptr<StreamerNode> BaseAudioContext::createStreamer() {
auto streamer = std::make_shared<StreamerNode>(this);
nodeManager_->addSourceNode(streamer);
return streamer;
}

std::shared_ptr<GainNode> BaseAudioContext::createGain() {
auto gain = std::make_shared<GainNode>(this);
nodeManager_->addProcessingNode(gain);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class AnalyserNode;
class AudioEventHandlerRegistry;
class IAudioEventHandlerRegistry;
class RecorderAdapterNode;
class StreamerNode;

class BaseAudioContext {
public:
Expand All @@ -45,6 +46,7 @@ class BaseAudioContext {

std::shared_ptr<RecorderAdapterNode> createRecorderAdapter();
std::shared_ptr<OscillatorNode> createOscillator();
std::shared_ptr<StreamerNode> createStreamer();
std::shared_ptr<GainNode> createGain();
std::shared_ptr<StereoPannerNode> createStereoPanner();
std::shared_ptr<BiquadFilterNode> createBiquadFilter();
Expand Down
Loading
Loading