Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
56f0f58
feat: ai slop long files playback node
maciejmakowski2003 Mar 17, 2026
429310a
feat: file playback
mdydek Mar 17, 2026
feaecf6
feat: working ffmpeg files playback
mdydek Mar 18, 2026
a54bbe3
feat: alpha integration with audio tag
mdydek Mar 20, 2026
0e1c08a
feat: basic integration with audio tag
mdydek Mar 20, 2026
1935fba
fix: tests
mdydek Mar 20, 2026
9b0e748
fix: testsV2
mdydek Mar 20, 2026
df70d91
feat: loop controls state
mdydek Mar 20, 2026
25e4a63
feat: working beta
mdydek Mar 23, 2026
979b34d
feat: offload seek to a separate thread
mdydek Mar 23, 2026
923537a
chore: removed AudioContent.tsx
mdydek Mar 23, 2026
d8534b8
chore: removed .swift file
mdydek Mar 23, 2026
35bbbac
chore: original .swift file
mdydek Mar 23, 2026
045450a
chore: renamed example
mdydek Mar 24, 2026
62bb5ad
refactor: cleanup
mdydek Mar 24, 2026
485eca6
refactor: changed spsc strategies
mdydek Mar 25, 2026
2440c8f
refactor: improvements on verbosity
mdydek Mar 25, 2026
13f5a37
feat: static icons try
mdydek Mar 26, 2026
586cbb9
feat: added ffmpeg flag to options
mdydek Mar 26, 2026
a626a1e
feat: remove unnecessary icon dependency
mdydek Mar 27, 2026
e618442
refactor: common decoders
mdydek Mar 27, 2026
2d0e82d
Merge branch 'main' into feat/long-file-playback
mdydek Mar 27, 2026
907c558
feat: node as audio scheduled source node
mdydek Mar 27, 2026
79aa893
refactor: comments
mdydek Mar 31, 2026
822a342
refactor: adjusted api approach
mdydek Mar 31, 2026
71b95ea
refactor: react cleanup
mdydek Apr 1, 2026
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
56 changes: 56 additions & 0 deletions apps/common-app/src/examples/AudioTag/AudioTag.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React, { useRef } from 'react';
import { Button, View } from 'react-native';
import { Audio, AudioTagHandle } from 'react-native-audio-api/development/react';

import { Container } from '../../components';

// const DEMO_AUDIO_URL = 'https://filesamples.com/samples/audio/m4a/sample4.m4a';
const DEMO_AUDIO_URL = 'https://filesamples.com/samples/audio/mp3/sample4.mp3';

const AudioTag: React.FC = () => {
const audioRef = useRef<AudioTagHandle>(null);

// const handlePlay = () => {
// audioRef.current?.play();
// };

// const handlePause = () => {
// audioRef.current?.pause();
// };

// const handleSeekToTime = (time: number) => {
// console.log('handleSeekToTime', time);
// audioRef.current?.seekToTime(time);
// };

// const handleSetVolume = (volume: number) => {
// audioRef.current?.setVolume(volume);
// };

// const handleSetMuted = (muted: boolean) => {
// audioRef.current?.setMuted(muted);
// };

return (
<Container disablePadding>
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<View style={{ width: '90%' }}>
<Audio source={DEMO_AUDIO_URL} ref={audioRef} controls
onLoadStart={() => console.log('onLoadStart')}
onLoad={() => console.log('onLoad')}
onError={(error) => console.log('onError', error)}
onPositionChange={(seconds) =>
console.log('onPositionChange', seconds)
}
onEnded={() => console.log('onEnded')}
onPlay={() => console.log('onPlay')}
onPause={() => console.log('onPause')}
onVolumeChange={(volume) => console.log('onVolumeChange', volume)}
/>
</View>
</View>
</Container>
);
};

export default AudioTag;
1 change: 1 addition & 0 deletions apps/common-app/src/examples/AudioTag/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './AudioTag';
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 @@ -12,6 +12,7 @@ import PlaybackSpeed from './PlaybackSpeed/PlaybackSpeed';
import Record from './Record/Record';
import Streaming from './Streaming/Streaming';
import Worklets from './Worklets/Worklets';
import AudioStream from './AudioTag/AudioTag';

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

export type ExampleKey = keyof NavigationParamList;
Expand Down Expand Up @@ -110,4 +112,10 @@ export const Examples: Example[] = [
Icon: icons.Radio,
screen: Streaming,
},
{
key: 'AudioTag',
title: 'Audio Tag',
Icon: icons.Tag,
screen: AudioStream,
}
] as const;
22 changes: 22 additions & 0 deletions apps/fabric-example/metro.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,28 @@ const appsRoot = path.resolve(monorepoRoot, 'apps');
const config = {
projectRoot: __dirname,
watchFolders: [monorepoRoot, appsRoot],
/* we are rewriting requests because due to monorepo structure, the assets are found with '../../../' prefix
and we redirect them to the correct path without relative prefixes */
server: {
rewriteRequestUrl: (url) => {
if (!url.startsWith('/assets/../../')) {
return url;
}

const queryIndex = url.indexOf('?');
const pathname = queryIndex >= 0 ? url.substring(0, queryIndex) : url;
const query = queryIndex >= 0 ? url.substring(queryIndex) : '';
const separator = query ? '&' : '?';

const relPath = pathname.startsWith('/assets/')
? pathname.substring('/assets/'.length)
: `../../${pathname}`;

const rewrittenUrl = `/assets${query}${separator}unstable_path=${encodeURIComponent(relPath)}`;

return rewrittenUrl;
},
},
};

module.exports = mergeConfig(getDefaultConfig(__dirname), config);
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <audioapi/HostObjects/sources/AudioBufferHostObject.h>
#include <audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.h>
#include <audioapi/HostObjects/sources/AudioBufferSourceNodeHostObject.h>
#include <audioapi/HostObjects/sources/AudioFileSourceNodeHostObject.h>
#include <audioapi/HostObjects/sources/ConstantSourceNodeHostObject.h>
#include <audioapi/HostObjects/sources/OscillatorNodeHostObject.h>
#include <audioapi/HostObjects/sources/RecorderAdapterNodeHostObject.h>
Expand All @@ -22,6 +23,7 @@
#include <audioapi/HostObjects/utils/JsEnumParser.h>
#include <audioapi/HostObjects/utils/NodeOptionsParser.h>
#include <audioapi/core/BaseAudioContext.h>
#include <audioapi/core/utils/AudioDecoder.h>

#include <memory>
#include <vector>
Expand Down Expand Up @@ -58,6 +60,7 @@ BaseAudioContextHostObject::BaseAudioContextHostObject(
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createBiquadFilter),
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createIIRFilter),
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createBufferSource),
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createFileSource),
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createBufferQueueSource),
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createPeriodicWave),
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createConvolver),
Expand Down Expand Up @@ -250,6 +253,25 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createBufferSource) {
return jsi::Object::createFromHostObject(runtime, bufferSourceHostObject);
}

JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createFileSource) {
auto makeFileSourceHostObject = [&](AudioFileSourceOptions opts) -> jsi::Value {
#if RN_AUDIO_API_FFMPEG_DISABLED
if (opts.requiresFFmpeg) {
return jsi::Value::undefined();
}
#endif // RN_AUDIO_API_FFMPEG_DISABLED
const auto fileSourceHostObject =
std::make_shared<AudioFileSourceNodeHostObject>(context_, opts);
return jsi::Object::createFromHostObject(runtime, fileSourceHostObject);
};

const auto options = args[0].asObject(runtime);

const auto fileSourceOptions =
audioapi::option_parser::parseAudioFileSourceOptions(runtime, options);
return makeFileSourceHostObject(fileSourceOptions);
}

JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createBufferQueueSource) {
const auto options = args[0].asObject(runtime);
const auto baseAudioBufferSourceOptions =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class BaseAudioContextHostObject : public JsiHostObject {
JSI_HOST_FUNCTION_DECL(createBiquadFilter);
JSI_HOST_FUNCTION_DECL(createIIRFilter);
JSI_HOST_FUNCTION_DECL(createBufferSource);
JSI_HOST_FUNCTION_DECL(createFileSource);
JSI_HOST_FUNCTION_DECL(createBufferQueueSource);
JSI_HOST_FUNCTION_DECL(createPeriodicWave);
JSI_HOST_FUNCTION_DECL(createAnalyser);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#include <audioapi/HostObjects/sources/AudioFileSourceNodeHostObject.h>

#include <audioapi/core/BaseAudioContext.h>
#include <audioapi/core/sources/AudioFileSourceNode.h>
#include <audioapi/types/NodeOptions.h>
#include <memory>
#include <utility>
#include "audioapi/HostObjects/sources/AudioScheduledSourceNodeHostObject.h"

namespace audioapi {

AudioFileSourceNodeHostObject::AudioFileSourceNodeHostObject(
const std::shared_ptr<BaseAudioContext> &context,
const AudioFileSourceOptions &options)
: AudioScheduledSourceNodeHostObject(context->createFileSource(options), options),
loop_(options.loop),
volume_(options.volume),
duration_(std::static_pointer_cast<AudioFileSourceNode>(node_)->getDuration()) {
addGetters(
JSI_EXPORT_PROPERTY_GETTER(AudioFileSourceNodeHostObject, volume),
JSI_EXPORT_PROPERTY_GETTER(AudioFileSourceNodeHostObject, loop),
JSI_EXPORT_PROPERTY_GETTER(AudioFileSourceNodeHostObject, currentTime),
JSI_EXPORT_PROPERTY_GETTER(AudioFileSourceNodeHostObject, duration));
addSetters(
JSI_EXPORT_PROPERTY_SETTER(AudioFileSourceNodeHostObject, onPositionChanged),
JSI_EXPORT_PROPERTY_SETTER(AudioFileSourceNodeHostObject, onEnded),
JSI_EXPORT_PROPERTY_SETTER(AudioFileSourceNodeHostObject, volume),
JSI_EXPORT_PROPERTY_SETTER(AudioFileSourceNodeHostObject, loop));

addFunctions(
JSI_EXPORT_FUNCTION(AudioFileSourceNodeHostObject, pause),
JSI_EXPORT_FUNCTION(AudioFileSourceNodeHostObject, start),
JSI_EXPORT_FUNCTION(AudioFileSourceNodeHostObject, seekToTime));
}

AudioFileSourceNodeHostObject::~AudioFileSourceNodeHostObject() {
setOnPositionChangedCallbackId(0);
setOnEndedCallbackId(0);
}

JSI_PROPERTY_GETTER_IMPL(AudioFileSourceNodeHostObject, volume) {
return {volume_};
}

JSI_PROPERTY_SETTER_IMPL(AudioFileSourceNodeHostObject, volume) {
auto node = std::static_pointer_cast<AudioFileSourceNode>(node_);
volume_ = static_cast<float>(value.getNumber());
auto event = [node, volume = this->volume_](BaseAudioContext &ctx) {
node->setVolume(volume);
};
node->scheduleAudioEvent(std::move(event));
}

JSI_PROPERTY_GETTER_IMPL(AudioFileSourceNodeHostObject, loop) {
return {loop_};
}

JSI_PROPERTY_SETTER_IMPL(AudioFileSourceNodeHostObject, loop) {
auto node = std::static_pointer_cast<AudioFileSourceNode>(node_);
loop_ = value.getBool();
auto event = [node, loop = this->loop_](BaseAudioContext &ctx) {
node->setLoop(loop);
};
node->scheduleAudioEvent(std::move(event));
}

JSI_PROPERTY_GETTER_IMPL(AudioFileSourceNodeHostObject, currentTime) {
auto node = std::static_pointer_cast<AudioFileSourceNode>(node_);
return {node->getCurrentTime()};
}

JSI_PROPERTY_GETTER_IMPL(AudioFileSourceNodeHostObject, duration) {
return {duration_};
}

JSI_HOST_FUNCTION_IMPL(AudioFileSourceNodeHostObject, pause) {
auto audioFileSourceNode = std::static_pointer_cast<AudioFileSourceNode>(node_);
auto event = [audioFileSourceNode](BaseAudioContext &ctx) {
audioFileSourceNode->pause();
};
audioFileSourceNode->scheduleAudioEvent(std::move(event));
return jsi::Value::undefined();
}

JSI_HOST_FUNCTION_IMPL(AudioFileSourceNodeHostObject, seekToTime) {
auto audioFileSourceNode = std::static_pointer_cast<AudioFileSourceNode>(node_);
if (count < 1 || !args[0].isNumber()) {
return jsi::Value::undefined();
}
const double t = args[0].getNumber();

auto event = [audioFileSourceNode, t](BaseAudioContext &) {
audioFileSourceNode->seekToTime(t);
};
audioFileSourceNode->scheduleAudioEvent(std::move(event));

return jsi::Value::undefined();
}

JSI_PROPERTY_SETTER_IMPL(AudioFileSourceNodeHostObject, onPositionChanged) {
auto callbackId = std::stoull(value.getString(runtime).utf8(runtime));
setOnPositionChangedCallbackId(callbackId);
}

void AudioFileSourceNodeHostObject::setOnPositionChangedCallbackId(uint64_t callbackId) {
auto sourceNode = std::static_pointer_cast<AudioFileSourceNode>(node_);

auto event = [sourceNode, callbackId](BaseAudioContext &) {
sourceNode->setOnPositionChangedCallbackId(callbackId);
};

sourceNode->unregisterOnPositionChangedCallback(onPositionChangedCallbackId_);
sourceNode->scheduleAudioEvent(std::move(event));
onPositionChangedCallbackId_ = callbackId;
}

void AudioFileSourceNodeHostObject::setOnEndedCallbackId(uint64_t callbackId) {
auto sourceNode = std::static_pointer_cast<AudioFileSourceNode>(node_);

auto event = [sourceNode, callbackId](BaseAudioContext &) {
sourceNode->setOnEndedCallbackId(callbackId);
};

sourceNode->unregisterOnEndedCallback(onEndedCallbackId_);
sourceNode->scheduleAudioEvent(std::move(event));
onEndedCallbackId_ = callbackId;
}

} // namespace audioapi
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once

#include <audioapi/HostObjects/sources/AudioScheduledSourceNodeHostObject.h>
#include <memory>

namespace audioapi {
using namespace facebook;

struct AudioFileSourceOptions;
class BaseAudioContext;

class AudioFileSourceNodeHostObject : public AudioScheduledSourceNodeHostObject {
public:
explicit AudioFileSourceNodeHostObject(
const std::shared_ptr<BaseAudioContext> &context,
const AudioFileSourceOptions &options);

~AudioFileSourceNodeHostObject() override;

JSI_PROPERTY_GETTER_DECL(volume);
JSI_PROPERTY_GETTER_DECL(loop);
JSI_PROPERTY_GETTER_DECL(currentTime);
JSI_PROPERTY_GETTER_DECL(duration);

JSI_PROPERTY_SETTER_DECL(volume);
JSI_PROPERTY_SETTER_DECL(loop);
JSI_PROPERTY_SETTER_DECL(onPositionChanged);

JSI_HOST_FUNCTION_DECL(pause);
JSI_HOST_FUNCTION_DECL(seekToStart);
JSI_HOST_FUNCTION_DECL(seekToTime);

private:
uint64_t onPositionChangedCallbackId_ = 0;
uint64_t onEndedCallbackId_ = 0;

void setOnPositionChangedCallbackId(uint64_t callbackId);
void setOnEndedCallbackId(uint64_t callbackId);

bool loop_;
double duration_;
float volume_;
};

} // namespace audioapi
Loading
Loading