diff --git a/.gitignore b/.gitignore
index 6d08e317..94a48503 100644
--- a/.gitignore
+++ b/.gitignore
@@ -83,3 +83,4 @@ react-native-audio-api*.tgz
# Android
.kotlin
+.env
diff --git a/apps/common-app/src/examples/AudioFile/AudioFile.tsx b/apps/common-app/src/examples/AudioFile/AudioFile.tsx
index 91b861ac..3a189a9a 100644
--- a/apps/common-app/src/examples/AudioFile/AudioFile.tsx
+++ b/apps/common-app/src/examples/AudioFile/AudioFile.tsx
@@ -1,12 +1,16 @@
-import React from 'react';
+import React, { useCallback } from 'react';
import { useState, useRef, useEffect, FC } from 'react';
import { Container, Button } from '../../components';
import {
+ AudioBuffer,
AudioContext,
AudioBufferSourceNode,
- AudioBuffer,
} from 'react-native-audio-api';
+import { ActivityIndicator } from 'react-native';
+
+const assetUrl =
+ 'https://audio-ssl.itunes.apple.com/apple-assets-us-std-000001/AudioPreview18/v4/9c/db/54/9cdb54b3-5c52-3063-b1ad-abe42955edb5/mzaf_520282131402737225.plus.aac.p.m4a';
const AudioFile: FC = () => {
const [isPlaying, setIsPlaying] = useState(false);
@@ -22,28 +26,28 @@ const AudioFile: FC = () => {
audioBufferSourceNodeRef.current =
audioContextRef.current.createBufferSource();
- audioBufferSourceNodeRef.current.buffer;
+
audioBufferSourceNodeRef.current.connect(
audioContextRef.current.destination
);
};
- const fetchAudioBuffer = async () => {
+ const fetchAudioBuffer = useCallback(async () => {
if (!audioContextRef.current) {
audioContextRef.current = new AudioContext();
}
- setAudioBuffer(
- // audioContextRef.current.decodeAudioDataSource(
- // '/Users/maciejmakowski/projects/react-native-audio-api/apps/common-app/src/examples/AudioFile/runaway_kanye_west.mp3'
- // )
- audioContextRef.current.decodeAudioDataSource(
- 'https://audio-ssl.itunes.apple.com/apple-assets-us-std-000001/AudioPreview18/v4/9c/db/54/9cdb54b3-5c52-3063-b1ad-abe42955edb5/mzaf_520282131402737225.plus.aac.p.m4a'
- )
- );
- };
+ const buffer =
+ await audioContextRef.current.decodeAudioDataSource(assetUrl);
+
+ setAudioBuffer(buffer);
+ }, []);
const handlePress = () => {
+ if (!audioBuffer) {
+ return;
+ }
+
if (isPlaying) {
audioBufferSourceNodeRef.current?.stop();
} else {
@@ -65,11 +69,12 @@ const AudioFile: FC = () => {
return () => {
audioContextRef.current?.close();
};
- }, []);
+ }, [fetchAudioBuffer]);
return (
+ {!audioBuffer && }
);
};
diff --git a/apps/fabric-example/ios/Podfile.lock b/apps/fabric-example/ios/Podfile.lock
index 3543d9b3..4944dd17 100644
--- a/apps/fabric-example/ios/Podfile.lock
+++ b/apps/fabric-example/ios/Podfile.lock
@@ -2063,8 +2063,8 @@ SPEC CHECKSUMS:
RNReanimated: 77242c6d67416988a2fd9f5cf574bb3e60016362
RNScreens: e389d6a6a66a4f0d3662924ecae803073ccce8ec
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
- Yoga: f8ec45ce98bba1bc93dd28f2ee37215180e6d2b6
+ Yoga: 1d66db49f38fd9e576a1d7c3b081e46ab4c28b9e
PODFILE CHECKSUM: 75ad38075e71875257a2590065853ea6a608b897
-COCOAPODS: 1.15.2
+COCOAPODS: 1.16.2
diff --git a/packages/react-native-audio-api/common/cpp/AudioAPIInstaller/AudioAPIInstallerHostObject.cpp b/packages/react-native-audio-api/common/cpp/AudioAPIInstaller/AudioAPIInstallerHostObject.cpp
index 14e1f91f..0d8bf874 100644
--- a/packages/react-native-audio-api/common/cpp/AudioAPIInstaller/AudioAPIInstallerHostObject.cpp
+++ b/packages/react-native-audio-api/common/cpp/AudioAPIInstaller/AudioAPIInstallerHostObject.cpp
@@ -4,8 +4,10 @@ namespace audioapi {
using namespace facebook;
AudioAPIInstallerHostObject::AudioAPIInstallerHostObject(
- const std::shared_ptr &wrapper)
- : wrapper_(wrapper) {}
+ const std::shared_ptr &wrapper, jsi::Runtime* runtime, const std::shared_ptr &jsInvoker)
+ : wrapper_(wrapper) {
+ promiseVendor_ = std::make_shared(runtime, jsInvoker);
+ }
std::vector AudioAPIInstallerHostObject::getPropertyNames(
jsi::Runtime &runtime) {
@@ -32,7 +34,7 @@ jsi::Value AudioAPIInstallerHostObject::get(
size_t count) -> jsi::Value {
auto audioContext = wrapper_->createAudioContext();
auto audioContextHostObject =
- AudioContextHostObject::createFromWrapper(audioContext);
+ AudioContextHostObject::createFromWrapper(audioContext, promiseVendor_);
return jsi::Object::createFromHostObject(
runtime, audioContextHostObject);
});
diff --git a/packages/react-native-audio-api/common/cpp/AudioAPIInstaller/AudioAPIInstallerHostObject.h b/packages/react-native-audio-api/common/cpp/AudioAPIInstaller/AudioAPIInstallerHostObject.h
index 58511696..bfe8255b 100644
--- a/packages/react-native-audio-api/common/cpp/AudioAPIInstaller/AudioAPIInstallerHostObject.h
+++ b/packages/react-native-audio-api/common/cpp/AudioAPIInstaller/AudioAPIInstallerHostObject.h
@@ -1,12 +1,15 @@
#pragma once
+#include
#include
#include
#include
#include
+
#include "AudioAPIInstallerWrapper.h"
#include "AudioContextHostObject.h"
+#include "JsiPromise.h"
namespace audioapi {
using namespace facebook;
@@ -16,7 +19,7 @@ class AudioAPIInstallerWrapper;
class AudioAPIInstallerHostObject : public jsi::HostObject {
public:
explicit AudioAPIInstallerHostObject(
- const std::shared_ptr &wrapper);
+ const std::shared_ptr &wrapper, jsi::Runtime* runtime, const std::shared_ptr &jsInvoker);
#ifdef ANDROID
static void createAndInstallFromWrapper(
@@ -41,5 +44,6 @@ class AudioAPIInstallerHostObject : public jsi::HostObject {
private:
std::shared_ptr wrapper_;
+ std::shared_ptr promiseVendor_;
};
} // namespace audioapi
diff --git a/packages/react-native-audio-api/common/cpp/HostObjects/AudioBufferHostObject.cpp b/packages/react-native-audio-api/common/cpp/HostObjects/AudioBufferHostObject.cpp
index 5272b333..34e5577c 100644
--- a/packages/react-native-audio-api/common/cpp/HostObjects/AudioBufferHostObject.cpp
+++ b/packages/react-native-audio-api/common/cpp/HostObjects/AudioBufferHostObject.cpp
@@ -128,6 +128,12 @@ jsi::Value AudioBufferHostObject::get(
});
}
+ // `decodeAudioData` is a method that returns a promise to AudioBufferHostObject
+ // It seems that async/await checks for the presence of `then` method on the object
+ if (propName == "then") {
+ return jsi::Value::undefined();
+ }
+
throw std::runtime_error("Not yet implemented!");
}
diff --git a/packages/react-native-audio-api/common/cpp/HostObjects/AudioContextHostObject.cpp b/packages/react-native-audio-api/common/cpp/HostObjects/AudioContextHostObject.cpp
index 0a2cdb51..9a986a3c 100644
--- a/packages/react-native-audio-api/common/cpp/HostObjects/AudioContextHostObject.cpp
+++ b/packages/react-native-audio-api/common/cpp/HostObjects/AudioContextHostObject.cpp
@@ -4,8 +4,8 @@ namespace audioapi {
using namespace facebook;
AudioContextHostObject::AudioContextHostObject(
- const std::shared_ptr &wrapper)
- : BaseAudioContextHostObject(wrapper) {}
+ const std::shared_ptr &wrapper, std::shared_ptr promiseVendor)
+ : BaseAudioContextHostObject(wrapper, promiseVendor) {}
std::vector AudioContextHostObject::getPropertyNames(
jsi::Runtime &runtime) {
diff --git a/packages/react-native-audio-api/common/cpp/HostObjects/AudioContextHostObject.h b/packages/react-native-audio-api/common/cpp/HostObjects/AudioContextHostObject.h
index 37b46d37..fd5038c4 100644
--- a/packages/react-native-audio-api/common/cpp/HostObjects/AudioContextHostObject.h
+++ b/packages/react-native-audio-api/common/cpp/HostObjects/AudioContextHostObject.h
@@ -4,6 +4,7 @@
#include
#include
+#include"JsiPromise.h"
#include "AudioContextWrapper.h"
#include "BaseAudioContextHostObject.h"
@@ -13,7 +14,7 @@ using namespace facebook;
class AudioContextHostObject : public BaseAudioContextHostObject {
public:
explicit AudioContextHostObject(
- const std::shared_ptr &wrapper);
+ const std::shared_ptr &wrapper, std::shared_ptr promiseVendor);
jsi::Value get(jsi::Runtime &runtime, const jsi::PropNameID &name) override;
@@ -25,8 +26,8 @@ class AudioContextHostObject : public BaseAudioContextHostObject {
std::vector getPropertyNames(jsi::Runtime &rt) override;
static std::shared_ptr createFromWrapper(
- const std::shared_ptr &wrapper) {
- return std::make_shared(wrapper);
+ const std::shared_ptr &wrapper, std::shared_ptr promiseVendor) {
+ return std::make_shared(wrapper, promiseVendor);
}
private:
diff --git a/packages/react-native-audio-api/common/cpp/HostObjects/BaseAudioContextHostObject.cpp b/packages/react-native-audio-api/common/cpp/HostObjects/BaseAudioContextHostObject.cpp
index aa9a7911..c0adb011 100644
--- a/packages/react-native-audio-api/common/cpp/HostObjects/BaseAudioContextHostObject.cpp
+++ b/packages/react-native-audio-api/common/cpp/HostObjects/BaseAudioContextHostObject.cpp
@@ -1,11 +1,13 @@
+#include
+
#include "BaseAudioContextHostObject.h"
namespace audioapi {
using namespace facebook;
BaseAudioContextHostObject::BaseAudioContextHostObject(
- const std::shared_ptr &wrapper)
- : wrapper_(wrapper) {
+ const std::shared_ptr &wrapper, std::shared_ptr promiseVendor)
+ : wrapper_(wrapper), promiseVendor_(promiseVendor) {
auto destinationNodeWrapper = wrapper_->getDestination();
destination_ =
AudioDestinationNodeHostObject::createFromWrapper(destinationNodeWrapper);
@@ -203,22 +205,25 @@ jsi::Value BaseAudioContextHostObject::get(
}
if (propName == "decodeAudioDataSource") {
- return jsi::Function::createFromHostFunction(
- runtime,
- propNameId,
- 1,
- [this](
- jsi::Runtime &runtime,
- const jsi::Value &thisValue,
- const jsi::Value *arguments,
- size_t count) -> jsi::Value {
- std::string source = arguments[0].getString(runtime).utf8(runtime);
- auto buffer = wrapper_->decodeAudioDataSource(source);
- auto audioBufferHostObject =
- AudioBufferHostObject::createFromWrapper(buffer);
- return jsi::Object::createFromHostObject(
- runtime, audioBufferHostObject);
- });
+ auto decode = [this](jsi::Runtime& runtime,
+ const jsi::Value&,
+ const jsi::Value* arguments,
+ size_t count) -> jsi::Value {
+ auto sourcePath = arguments[0].getString(runtime).utf8(runtime);
+
+ auto promise = promiseVendor_->createPromise([this, &runtime, sourcePath](std::shared_ptr promise) {
+ std::thread([this, &runtime, sourcePath, promise = std::move(promise)]() {
+ auto results = wrapper_->decodeAudioDataSource(sourcePath);
+ auto audioBufferHostObject = AudioBufferHostObject::createFromWrapper(results);
+
+ promise->resolve(jsi::Object::createFromHostObject(runtime, audioBufferHostObject));
+ }).detach();
+ });
+
+ return promise;
+ };
+
+ return jsi::Function::createFromHostFunction(runtime, propNameId, 1, decode);
}
throw std::runtime_error("Not yet implemented!");
diff --git a/packages/react-native-audio-api/common/cpp/HostObjects/BaseAudioContextHostObject.h b/packages/react-native-audio-api/common/cpp/HostObjects/BaseAudioContextHostObject.h
index e70851bd..fed6d4a3 100644
--- a/packages/react-native-audio-api/common/cpp/HostObjects/BaseAudioContextHostObject.h
+++ b/packages/react-native-audio-api/common/cpp/HostObjects/BaseAudioContextHostObject.h
@@ -5,6 +5,7 @@
#include
#include
+#include "JsiPromise.h"
#include "AudioBufferHostObject.h"
#include "AudioBufferSourceNodeHostObject.h"
#include "AudioDestinationNodeHostObject.h"
@@ -21,7 +22,7 @@ using namespace facebook;
class BaseAudioContextHostObject : public jsi::HostObject {
public:
explicit BaseAudioContextHostObject(
- const std::shared_ptr &wrapper);
+ const std::shared_ptr &wrapper, std::shared_ptr promiseVendor);
jsi::Value get(jsi::Runtime &runtime, const jsi::PropNameID &name) override;
@@ -35,5 +36,6 @@ class BaseAudioContextHostObject : public jsi::HostObject {
protected:
std::shared_ptr wrapper_;
std::shared_ptr destination_;
+ std::shared_ptr promiseVendor_;
};
} // namespace audioapi
diff --git a/packages/react-native-audio-api/common/cpp/utils/JsiPromise.cpp b/packages/react-native-audio-api/common/cpp/utils/JsiPromise.cpp
new file mode 100644
index 00000000..fc968b4a
--- /dev/null
+++ b/packages/react-native-audio-api/common/cpp/utils/JsiPromise.cpp
@@ -0,0 +1,58 @@
+#include "JsiPromise.h"
+
+#include
+#include
+#include
+
+namespace JsiPromise {
+
+using namespace facebook;
+
+jsi::Value PromiseVendor::createPromise(std::function)> func) {
+ if (_runtime == nullptr) {
+ throw new std::runtime_error("Runtime was null!");
+ }
+ auto& runtime = *_runtime;
+ auto callInvoker = _callInvoker;
+
+ // get Promise constructor
+ auto promiseCtor = runtime.global().getPropertyAsFunction(runtime, "Promise");
+
+ // create a "run" function (first Promise arg)
+ auto runPromise = jsi::Function::createFromHostFunction(runtime,
+ jsi::PropNameID::forUtf8(runtime, "runPromise"),
+ 2,
+ [callInvoker, func](jsi::Runtime& runtime,
+ const jsi::Value& thisValue,
+ const jsi::Value* arguments,
+ size_t count) -> jsi::Value {
+ auto resolveLocal = arguments[0].asObject(runtime).asFunction(runtime);
+ auto resolve = std::make_shared(std::move(resolveLocal));
+ auto rejectLocal = arguments[1].asObject(runtime).asFunction(runtime);
+ auto reject = std::make_shared(std::move(rejectLocal));
+
+ auto resolveWrapper = [resolve, &runtime, callInvoker](jsi::Value value) -> void {
+ auto valueShared = std::make_shared(std::move(value));
+ callInvoker->invokeAsync([resolve, &runtime, valueShared]() -> void {
+ resolve->call(runtime, *valueShared);
+ });
+ };
+ auto rejectWrapper = [reject, &runtime, callInvoker](const std::string& errorMessage) -> void {
+ auto error = jsi::JSError(runtime, errorMessage);
+ auto errorShared = std::make_shared(error);
+ callInvoker->invokeAsync([reject, &runtime, errorShared]() -> void {
+ reject->call(runtime, errorShared->value());
+ });
+ };
+
+ auto promise = std::make_shared(resolveWrapper, rejectWrapper);
+ func(promise);
+
+ return jsi::Value::undefined();
+ });
+
+ // return new Promise((resolve, reject) => ...)
+ return promiseCtor.callAsConstructor(runtime, runPromise);
+}
+
+}
diff --git a/packages/react-native-audio-api/common/cpp/utils/JsiPromise.h b/packages/react-native-audio-api/common/cpp/utils/JsiPromise.h
new file mode 100644
index 00000000..e4b71d71
--- /dev/null
+++ b/packages/react-native-audio-api/common/cpp/utils/JsiPromise.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include
+#include
+
+namespace JsiPromise {
+
+using namespace facebook;
+
+class Promise {
+public:
+ Promise(std::function resolve, std::function reject): _resolve(std::move(resolve)), _reject(std::move(reject)) {}
+public:
+ bool isResolved;
+ void resolve(jsi::Value&& value) {
+ _resolve(std::forward(value));
+ }
+ void reject(const std::string& errorMessage) {
+ _reject(errorMessage);
+ }
+
+private:
+ std::function _resolve;
+ std::function _reject;
+};
+
+class PromiseVendor {
+public:
+ PromiseVendor(jsi::Runtime* runtime, std::shared_ptr callInvoker): _runtime(runtime), _callInvoker(callInvoker) {}
+
+public:
+ jsi::Value createPromise(std::function)> func);
+
+private:
+ jsi::Runtime* _runtime;
+ std::shared_ptr _callInvoker;
+};
+
+}
diff --git a/packages/react-native-audio-api/common/cpp/wrappers/BaseAudioContextWrapper.cpp b/packages/react-native-audio-api/common/cpp/wrappers/BaseAudioContextWrapper.cpp
index 30f3d4aa..5f6a3ce4 100644
--- a/packages/react-native-audio-api/common/cpp/wrappers/BaseAudioContextWrapper.cpp
+++ b/packages/react-native-audio-api/common/cpp/wrappers/BaseAudioContextWrapper.cpp
@@ -79,4 +79,5 @@ BaseAudioContextWrapper::decodeAudioDataSource(const std::string &source) {
return std::make_shared(
context_->decodeAudioDataSource(source));
}
+
} // namespace audioapi
diff --git a/packages/react-native-audio-api/ios/AudioAPIModule.mm b/packages/react-native-audio-api/ios/AudioAPIModule.mm
index 5df3d30e..e554e8a9 100644
--- a/packages/react-native-audio-api/ios/AudioAPIModule.mm
+++ b/packages/react-native-audio-api/ios/AudioAPIModule.mm
@@ -1,6 +1,8 @@
#import "AudioAPIModule.h"
+#import
#import
+#import
#import
#import
@@ -33,7 +35,7 @@ @implementation AudioAPIModule
auto &runtime = *jsRuntime;
auto wrapper = std::make_shared();
- auto hostObject = std::make_shared(wrapper);
+ auto hostObject = std::make_shared(wrapper, jsRuntime, cxxBridge.jsCallInvoker);
auto object = jsi::Object::createFromHostObject(runtime, hostObject);
runtime.global().setProperty(runtime, "__AudioAPIInstaller", std::move(object));
diff --git a/packages/react-native-audio-api/src/core/BaseAudioContext.ts b/packages/react-native-audio-api/src/core/BaseAudioContext.ts
index 197acd72..96559914 100644
--- a/packages/react-native-audio-api/src/core/BaseAudioContext.ts
+++ b/packages/react-native-audio-api/src/core/BaseAudioContext.ts
@@ -96,9 +96,13 @@ export default class BaseAudioContext {
);
}
- decodeAudioDataSource(source: AudioSource | number): AudioBuffer {
- return new AudioBuffer(
- this.context.decodeAudioDataSource(resolveAudioSource(source))
+ async decodeAudioDataSource(
+ source: AudioSource | number
+ ): Promise {
+ const buffer = await this.context.decodeAudioDataSource(
+ resolveAudioSource(source)
);
+
+ return new AudioBuffer(buffer);
}
}
diff --git a/packages/react-native-audio-api/src/interfaces.ts b/packages/react-native-audio-api/src/interfaces.ts
index 5144785a..9cd5ac6b 100644
--- a/packages/react-native-audio-api/src/interfaces.ts
+++ b/packages/react-native-audio-api/src/interfaces.ts
@@ -27,7 +27,7 @@ export interface IBaseAudioContext {
imag: number[],
disableNormalization: boolean
) => IPeriodicWave;
- decodeAudioDataSource: (source: string) => IAudioBuffer;
+ decodeAudioDataSource: (source: string) => Promise;
}
export interface IAudioContext extends IBaseAudioContext {