react-native-audio-api 0.5.5 → 0.6.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/RNAudioAPI.podspec +1 -1
- package/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +0 -20
- package/android/src/main/cpp/audioapi/android/core/AudioPlayer.h +0 -2
- package/android/src/main/java/com/swmansion/audioapi/AudioAPIPackage.kt +13 -0
- package/android/src/main/java/com/swmansion/audioapi/AudioManagerModule.kt +59 -0
- package/android/src/oldarch/NativeAudioManagerModuleSpec.java +99 -0
- package/common/cpp/audioapi/AudioAPIModuleInstaller.h +30 -6
- package/common/cpp/audioapi/HostObjects/OfflineAudioContextHostObject.h +70 -0
- package/common/cpp/audioapi/core/AudioContext.cpp +1 -12
- package/common/cpp/audioapi/core/AudioContext.h +0 -1
- package/common/cpp/audioapi/core/OfflineAudioContext.cpp +117 -0
- package/common/cpp/audioapi/core/OfflineAudioContext.h +40 -0
- package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +3 -3
- package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +28 -2
- package/common/cpp/audioapi/core/utils/AudioNodeDestructor.cpp +53 -0
- package/common/cpp/audioapi/core/utils/AudioNodeDestructor.h +33 -0
- package/common/cpp/audioapi/core/utils/AudioNodeManager.cpp +13 -10
- package/common/cpp/audioapi/core/utils/AudioNodeManager.h +3 -0
- package/common/cpp/audioapi/libs/signalsmith-stretch/fft-accelerate.h +326 -0
- package/common/cpp/audioapi/libs/signalsmith-stretch/fft.h +1257 -413
- package/common/cpp/audioapi/libs/signalsmith-stretch/signalsmith-stretch.h +398 -232
- package/common/cpp/audioapi/libs/signalsmith-stretch/stft.h +625 -0
- package/ios/audioapi/ios/AudioAPIModule.mm +2 -3
- package/ios/audioapi/ios/AudioManagerModule.h +18 -0
- package/ios/audioapi/ios/AudioManagerModule.mm +92 -0
- package/ios/audioapi/ios/core/AudioPlayer.h +4 -12
- package/ios/audioapi/ios/core/AudioPlayer.m +26 -108
- package/ios/audioapi/ios/core/IOSAudioPlayer.h +1 -3
- package/ios/audioapi/ios/core/IOSAudioPlayer.mm +4 -28
- package/ios/audioapi/ios/system/AudioEngine.h +23 -0
- package/ios/audioapi/ios/system/AudioEngine.mm +137 -0
- package/ios/audioapi/ios/system/AudioSessionManager.h +22 -0
- package/ios/audioapi/ios/system/AudioSessionManager.mm +183 -0
- package/ios/audioapi/ios/system/LockScreenManager.h +23 -0
- package/ios/audioapi/ios/system/LockScreenManager.mm +299 -0
- package/ios/audioapi/ios/system/NotificationManager.h +16 -0
- package/ios/audioapi/ios/system/NotificationManager.mm +151 -0
- package/lib/module/api.js +3 -1
- package/lib/module/api.js.map +1 -1
- package/lib/module/api.web.js +1 -0
- package/lib/module/api.web.js.map +1 -1
- package/lib/module/core/AudioContext.js +2 -1
- package/lib/module/core/AudioContext.js.map +1 -1
- package/lib/module/core/OfflineAudioContext.js +57 -0
- package/lib/module/core/OfflineAudioContext.js.map +1 -0
- package/lib/module/specs/NativeAudioManagerModule.js +31 -0
- package/lib/module/specs/NativeAudioManagerModule.js.map +1 -0
- package/lib/module/specs/index.js +6 -0
- package/lib/module/specs/index.js.map +1 -0
- package/lib/module/system/AudioManager.js +66 -0
- package/lib/module/system/AudioManager.js.map +1 -0
- package/lib/module/system/index.js +4 -0
- package/lib/module/system/index.js.map +1 -0
- package/lib/module/system/types.js +2 -0
- package/lib/module/system/types.js.map +1 -0
- package/lib/module/web-core/OfflineAudioContext.js +90 -0
- package/lib/module/web-core/OfflineAudioContext.js.map +1 -0
- package/lib/typescript/api.d.ts +4 -1
- package/lib/typescript/api.d.ts.map +1 -1
- package/lib/typescript/api.web.d.ts +1 -0
- package/lib/typescript/api.web.d.ts.map +1 -1
- package/lib/typescript/core/AudioContext.d.ts.map +1 -1
- package/lib/typescript/core/OfflineAudioContext.d.ts +14 -0
- package/lib/typescript/core/OfflineAudioContext.d.ts.map +1 -0
- package/lib/typescript/interfaces.d.ts +6 -0
- package/lib/typescript/interfaces.d.ts.map +1 -1
- package/lib/typescript/specs/NativeAudioManagerModule.d.ts +13 -0
- package/lib/typescript/specs/NativeAudioManagerModule.d.ts.map +1 -0
- package/lib/typescript/specs/index.d.ts +4 -0
- package/lib/typescript/specs/index.d.ts.map +1 -0
- package/lib/typescript/system/AudioManager.d.ts +12 -0
- package/lib/typescript/system/AudioManager.d.ts.map +1 -0
- package/lib/typescript/system/index.d.ts +2 -0
- package/lib/typescript/system/index.d.ts.map +1 -0
- package/lib/typescript/system/types.d.ts +28 -0
- package/lib/typescript/system/types.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +5 -0
- package/lib/typescript/types.d.ts.map +1 -1
- package/lib/typescript/web-core/OfflineAudioContext.d.ts +34 -0
- package/lib/typescript/web-core/OfflineAudioContext.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/api.ts +12 -2
- package/src/api.web.ts +1 -0
- package/src/core/AudioContext.ts +6 -1
- package/src/core/OfflineAudioContext.ts +94 -0
- package/src/interfaces.ts +11 -0
- package/src/specs/NativeAudioManagerModule.ts +51 -0
- package/src/specs/index.ts +6 -0
- package/src/system/AudioManager.ts +122 -0
- package/src/system/index.ts +1 -0
- package/src/system/types.ts +68 -0
- package/src/types.ts +6 -0
- package/src/web-core/OfflineAudioContext.tsx +163 -0
- package/common/cpp/audioapi/libs/signalsmith-stretch/delay.h +0 -715
- package/common/cpp/audioapi/libs/signalsmith-stretch/perf.h +0 -82
- package/common/cpp/audioapi/libs/signalsmith-stretch/spectral.h +0 -493
package/RNAudioAPI.podspec
CHANGED
|
@@ -31,7 +31,7 @@ Pod::Spec.new do |s|
|
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
s.ios.frameworks = 'CoreFoundation', 'CoreAudio', 'AudioToolbox', 'Accelerate'
|
|
34
|
+
s.ios.frameworks = 'CoreFoundation', 'CoreAudio', 'AudioToolbox', 'Accelerate', 'MediaPlayer', 'AVFoundation'
|
|
35
35
|
|
|
36
36
|
s.compiler_flags = "#{folly_flags}"
|
|
37
37
|
|
|
@@ -6,26 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
namespace audioapi {
|
|
8
8
|
|
|
9
|
-
AudioPlayer::AudioPlayer(
|
|
10
|
-
const std::function<void(std::shared_ptr<AudioBus>, int)> &renderAudio)
|
|
11
|
-
: renderAudio_(renderAudio), channelCount_(2) {
|
|
12
|
-
AudioStreamBuilder builder;
|
|
13
|
-
|
|
14
|
-
builder.setSharingMode(SharingMode::Exclusive)
|
|
15
|
-
->setFormat(AudioFormat::Float)
|
|
16
|
-
->setFormatConversionAllowed(true)
|
|
17
|
-
->setPerformanceMode(PerformanceMode::None)
|
|
18
|
-
->setChannelCount(channelCount_)
|
|
19
|
-
->setSampleRateConversionQuality(SampleRateConversionQuality::Medium)
|
|
20
|
-
->setDataCallback(this)
|
|
21
|
-
->openStream(mStream_);
|
|
22
|
-
|
|
23
|
-
sampleRate_ = static_cast<float>(mStream_->getSampleRate());
|
|
24
|
-
mBus_ = std::make_shared<AudioBus>(
|
|
25
|
-
RENDER_QUANTUM_SIZE, channelCount_, sampleRate_);
|
|
26
|
-
isInitialized_ = true;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
9
|
AudioPlayer::AudioPlayer(
|
|
30
10
|
const std::function<void(std::shared_ptr<AudioBus>, int)> &renderAudio,
|
|
31
11
|
float sampleRate)
|
|
@@ -14,8 +14,6 @@ class AudioBus;
|
|
|
14
14
|
|
|
15
15
|
class AudioPlayer : public AudioStreamDataCallback {
|
|
16
16
|
public:
|
|
17
|
-
explicit AudioPlayer(
|
|
18
|
-
const std::function<void(std::shared_ptr<AudioBus>, int)> &renderAudio);
|
|
19
17
|
AudioPlayer(
|
|
20
18
|
const std::function<void(std::shared_ptr<AudioBus>, int)> &renderAudio,
|
|
21
19
|
float sampleRate);
|
|
@@ -10,6 +10,7 @@ import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
|
10
10
|
@ReactModuleList(
|
|
11
11
|
nativeModules = [
|
|
12
12
|
AudioAPIModule::class,
|
|
13
|
+
AudioManagerModule::class,
|
|
13
14
|
],
|
|
14
15
|
)
|
|
15
16
|
class AudioAPIPackage : BaseReactPackage() {
|
|
@@ -19,6 +20,7 @@ class AudioAPIPackage : BaseReactPackage() {
|
|
|
19
20
|
): NativeModule? {
|
|
20
21
|
when (name) {
|
|
21
22
|
AudioAPIModule.NAME -> return AudioAPIModule(reactContext)
|
|
23
|
+
AudioManagerModule.NAME -> return AudioManagerModule(reactContext)
|
|
22
24
|
}
|
|
23
25
|
return null
|
|
24
26
|
}
|
|
@@ -37,6 +39,17 @@ class AudioAPIPackage : BaseReactPackage() {
|
|
|
37
39
|
isCxxModule = false,
|
|
38
40
|
isTurboModule = isTurboModule,
|
|
39
41
|
)
|
|
42
|
+
|
|
43
|
+
moduleInfos[AudioManagerModule.NAME] =
|
|
44
|
+
ReactModuleInfo(
|
|
45
|
+
AudioManagerModule.NAME,
|
|
46
|
+
AudioManagerModule.NAME,
|
|
47
|
+
canOverrideExistingModule = true,
|
|
48
|
+
needsEagerInit = false,
|
|
49
|
+
hasConstants = true,
|
|
50
|
+
isCxxModule = false,
|
|
51
|
+
isTurboModule = false,
|
|
52
|
+
)
|
|
40
53
|
moduleInfos
|
|
41
54
|
}
|
|
42
55
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
package com.swmansion.audioapi
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.media.AudioManager
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
7
|
+
import com.facebook.react.bridge.ReactMethod
|
|
8
|
+
import com.facebook.react.bridge.ReadableArray
|
|
9
|
+
import com.facebook.react.bridge.ReadableMap
|
|
10
|
+
|
|
11
|
+
class AudioManagerModule(
|
|
12
|
+
reactContext: ReactApplicationContext,
|
|
13
|
+
) : ReactContextBaseJavaModule(reactContext) {
|
|
14
|
+
companion object {
|
|
15
|
+
const val NAME = "AudioManagerModule"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
private val audioManager: AudioManager = reactContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
|
19
|
+
|
|
20
|
+
init {
|
|
21
|
+
try {
|
|
22
|
+
System.loadLibrary("react-native-audio-api")
|
|
23
|
+
} catch (exception: UnsatisfiedLinkError) {
|
|
24
|
+
throw RuntimeException("Could not load native module AudioAPIModule", exception)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@ReactMethod
|
|
29
|
+
fun setLockScreenInfo(info: ReadableMap?) {
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@ReactMethod
|
|
33
|
+
fun resetLockScreenInfo() {
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@ReactMethod
|
|
37
|
+
fun enableRemoteCommand(
|
|
38
|
+
name: String?,
|
|
39
|
+
enabled: Boolean,
|
|
40
|
+
) {
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@ReactMethod
|
|
44
|
+
fun setAudioSessionOptions(
|
|
45
|
+
category: String?,
|
|
46
|
+
mode: String?,
|
|
47
|
+
options: ReadableArray?,
|
|
48
|
+
active: Boolean,
|
|
49
|
+
) {
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
53
|
+
fun getDevicePreferredSampleRate(): Double {
|
|
54
|
+
val sampleRate = this.audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE)
|
|
55
|
+
return sampleRate.toDouble()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
override fun getName(): String = NAME
|
|
59
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
|
4
|
+
*
|
|
5
|
+
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
|
6
|
+
* once the code is regenerated.
|
|
7
|
+
*
|
|
8
|
+
* @generated by codegen project: GenerateModuleJavaSpec.js
|
|
9
|
+
*
|
|
10
|
+
* @nolint
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
package com.swmansion.audioapi;
|
|
14
|
+
|
|
15
|
+
import com.facebook.proguard.annotations.DoNotStrip;
|
|
16
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
17
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
18
|
+
import com.facebook.react.bridge.ReactMethod;
|
|
19
|
+
import com.facebook.react.bridge.ReadableArray;
|
|
20
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
21
|
+
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
|
|
22
|
+
import javax.annotation.Nonnull;
|
|
23
|
+
|
|
24
|
+
public abstract class NativeAudioManagerModuleSpec extends ReactContextBaseJavaModule implements TurboModule {
|
|
25
|
+
public static final String NAME = "AudioManagerModule";
|
|
26
|
+
|
|
27
|
+
public NativeAudioManagerModuleSpec(ReactApplicationContext reactContext) {
|
|
28
|
+
super(reactContext);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@Override
|
|
32
|
+
public @Nonnull String getName() {
|
|
33
|
+
return NAME;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
protected final void emitOnRemotePlay() {
|
|
37
|
+
mEventEmitterCallback.invoke("onRemotePlay");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
protected final void emitOnRemotePause() {
|
|
41
|
+
mEventEmitterCallback.invoke("onRemotePause");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
protected final void emitOnStop() {
|
|
45
|
+
mEventEmitterCallback.invoke("onStop");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
protected final void emitOnTogglePlayPause() {
|
|
49
|
+
mEventEmitterCallback.invoke("onTogglePlayPause");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
protected final void emitOnChangePlaybackRate(double value) {
|
|
53
|
+
mEventEmitterCallback.invoke("onChangePlaybackRate", value);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
protected final void emitOnNextTrack() {
|
|
57
|
+
mEventEmitterCallback.invoke("onNextTrack");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
protected final void emitOnPreviousTrack() {
|
|
61
|
+
mEventEmitterCallback.invoke("onPreviousTrack");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
protected final void emitOnSkipForward(double value) {
|
|
65
|
+
mEventEmitterCallback.invoke("onSkipForward", value);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
protected final void emitOnSkipBackward(double value) {
|
|
69
|
+
mEventEmitterCallback.invoke("onSkipBackward", value);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
protected final void emitOnSeekForward() {
|
|
73
|
+
mEventEmitterCallback.invoke("onSeekForward");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
protected final void emitOnSeekBackward() {
|
|
77
|
+
mEventEmitterCallback.invoke("onSeekBackward");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
protected final void emitOnChangePlaybackPosition(double value) {
|
|
81
|
+
mEventEmitterCallback.invoke("onChangePlaybackPosition", value);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@ReactMethod
|
|
85
|
+
@DoNotStrip
|
|
86
|
+
public abstract void setLockScreenInfo(ReadableMap info);
|
|
87
|
+
|
|
88
|
+
@ReactMethod
|
|
89
|
+
@DoNotStrip
|
|
90
|
+
public abstract void resetLockScreenInfo();
|
|
91
|
+
|
|
92
|
+
@ReactMethod
|
|
93
|
+
@DoNotStrip
|
|
94
|
+
public abstract void setSessionOptions(String category, String mode, ReadableArray options);
|
|
95
|
+
|
|
96
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
97
|
+
@DoNotStrip
|
|
98
|
+
public abstract double getDevicePreferredSampleRate();
|
|
99
|
+
}
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
#include <audioapi/jsi/JsiPromise.h>
|
|
4
4
|
#include <audioapi/core/AudioContext.h>
|
|
5
|
+
#include <audioapi/core/OfflineAudioContext.h>
|
|
5
6
|
#include <audioapi/HostObjects/AudioContextHostObject.h>
|
|
7
|
+
#include <audioapi/HostObjects/OfflineAudioContextHostObject.h>
|
|
6
8
|
|
|
7
9
|
#include <memory>
|
|
8
10
|
|
|
@@ -14,8 +16,11 @@ class AudioAPIModuleInstaller {
|
|
|
14
16
|
public:
|
|
15
17
|
static void injectJSIBindings(jsi::Runtime *jsiRuntime, const std::shared_ptr<react::CallInvoker> &jsCallInvoker) {
|
|
16
18
|
auto createAudioContext = getCreateAudioContextFunction(jsiRuntime, jsCallInvoker);
|
|
19
|
+
auto createOfflineAudioContext = getCreateOfflineAudioContextFunction(jsiRuntime, jsCallInvoker);
|
|
17
20
|
jsiRuntime->global().setProperty(
|
|
18
21
|
*jsiRuntime, "createAudioContext", createAudioContext);
|
|
22
|
+
jsiRuntime->global().setProperty(
|
|
23
|
+
*jsiRuntime, "createOfflineAudioContext", createOfflineAudioContext);
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
private:
|
|
@@ -30,12 +35,8 @@ class AudioAPIModuleInstaller {
|
|
|
30
35
|
const jsi::Value *args,
|
|
31
36
|
size_t count) -> jsi::Value {
|
|
32
37
|
std::shared_ptr<AudioContext> audioContext;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
} else {
|
|
36
|
-
auto sampleRate = static_cast<float>(args[0].getNumber());
|
|
37
|
-
audioContext = std::make_shared<AudioContext>(sampleRate);
|
|
38
|
-
}
|
|
38
|
+
auto sampleRate = static_cast<float>(args[0].getNumber());
|
|
39
|
+
audioContext = std::make_shared<AudioContext>(sampleRate);
|
|
39
40
|
|
|
40
41
|
auto audioContextHostObject = std::make_shared<AudioContextHostObject>(
|
|
41
42
|
audioContext, &runtime, jsCallInvoker);
|
|
@@ -44,6 +45,29 @@ class AudioAPIModuleInstaller {
|
|
|
44
45
|
runtime, audioContextHostObject);
|
|
45
46
|
});
|
|
46
47
|
}
|
|
48
|
+
|
|
49
|
+
static jsi::Function getCreateOfflineAudioContextFunction(jsi::Runtime *jsiRuntime, const std::shared_ptr<react::CallInvoker> &jsCallInvoker) {
|
|
50
|
+
return jsi::Function::createFromHostFunction(
|
|
51
|
+
*jsiRuntime,
|
|
52
|
+
jsi::PropNameID::forAscii(*jsiRuntime, "createOfflineAudioContext"),
|
|
53
|
+
0,
|
|
54
|
+
[jsiRuntime, jsCallInvoker](
|
|
55
|
+
jsi::Runtime &runtime,
|
|
56
|
+
const jsi::Value &thisValue,
|
|
57
|
+
const jsi::Value *args,
|
|
58
|
+
size_t count) -> jsi::Value {
|
|
59
|
+
auto numberOfChannels = static_cast<int>(args[0].getNumber());
|
|
60
|
+
auto length = static_cast<size_t>(args[1].getNumber());
|
|
61
|
+
auto sampleRate = static_cast<float>(args[2].getNumber());
|
|
62
|
+
|
|
63
|
+
std::shared_ptr<OfflineAudioContext> offlineAudioContext = std::make_shared<OfflineAudioContext>(numberOfChannels, length, sampleRate);
|
|
64
|
+
auto audioContextHostObject = std::make_shared<OfflineAudioContextHostObject>(
|
|
65
|
+
offlineAudioContext, jsiRuntime, jsCallInvoker);
|
|
66
|
+
|
|
67
|
+
return jsi::Object::createFromHostObject(
|
|
68
|
+
runtime, audioContextHostObject);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
47
71
|
};
|
|
48
72
|
|
|
49
73
|
} // namespace audioapi
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <audioapi/core/OfflineAudioContext.h>
|
|
4
|
+
#include <audioapi/HostObjects/BaseAudioContextHostObject.h>
|
|
5
|
+
|
|
6
|
+
#include <jsi/jsi.h>
|
|
7
|
+
#include <memory>
|
|
8
|
+
#include <utility>
|
|
9
|
+
#include <vector>
|
|
10
|
+
|
|
11
|
+
namespace audioapi {
|
|
12
|
+
using namespace facebook;
|
|
13
|
+
|
|
14
|
+
class OfflineAudioContextHostObject : public BaseAudioContextHostObject {
|
|
15
|
+
public:
|
|
16
|
+
explicit OfflineAudioContextHostObject(
|
|
17
|
+
const std::shared_ptr<OfflineAudioContext> &offlineAudioContext,
|
|
18
|
+
jsi::Runtime *runtime,
|
|
19
|
+
const std::shared_ptr<react::CallInvoker> &callInvoker)
|
|
20
|
+
: BaseAudioContextHostObject(offlineAudioContext, runtime, callInvoker) {
|
|
21
|
+
addFunctions(
|
|
22
|
+
JSI_EXPORT_FUNCTION(OfflineAudioContextHostObject, resume),
|
|
23
|
+
JSI_EXPORT_FUNCTION(OfflineAudioContextHostObject, suspend),
|
|
24
|
+
JSI_EXPORT_FUNCTION(OfflineAudioContextHostObject, startRendering));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
JSI_HOST_FUNCTION(resume) {
|
|
28
|
+
auto promise = promiseVendor_->createPromise([this](const std::shared_ptr<Promise>& promise) {
|
|
29
|
+
auto audioContext = std::static_pointer_cast<OfflineAudioContext>(context_);
|
|
30
|
+
audioContext->resume();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return promise;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
JSI_HOST_FUNCTION(suspend) {
|
|
37
|
+
double when = args[0].getNumber();
|
|
38
|
+
|
|
39
|
+
auto promise = promiseVendor_->createPromise([this, when](const std::shared_ptr<Promise>& promise) {
|
|
40
|
+
auto audioContext = std::static_pointer_cast<OfflineAudioContext>(context_);
|
|
41
|
+
OfflineAudioContextSuspendCallback callback = [promise]() {
|
|
42
|
+
promise->resolve([](jsi::Runtime &runtime) {
|
|
43
|
+
return jsi::Value::undefined();
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
audioContext->suspend(when, callback);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return promise;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
JSI_HOST_FUNCTION(startRendering) {
|
|
53
|
+
auto promise = promiseVendor_->createPromise([this](const std::shared_ptr<Promise>& promise) {
|
|
54
|
+
auto audioContext = std::static_pointer_cast<OfflineAudioContext>(context_);
|
|
55
|
+
|
|
56
|
+
OfflineAudioContextResultCallback callback =
|
|
57
|
+
[promise](const std::shared_ptr<AudioBuffer>& audioBuffer) -> void {
|
|
58
|
+
auto audioBufferHostObject = std::make_shared<AudioBufferHostObject>(audioBuffer);
|
|
59
|
+
promise->resolve([audioBufferHostObject = std::move(audioBufferHostObject)](jsi::Runtime &runtime) {
|
|
60
|
+
return jsi::Object::createFromHostObject(runtime, audioBufferHostObject);
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
audioContext->startRendering(callback);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
return promise;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
} // namespace audioapi
|
|
@@ -10,18 +10,6 @@
|
|
|
10
10
|
#include <audioapi/core/utils/AudioNodeManager.h>
|
|
11
11
|
|
|
12
12
|
namespace audioapi {
|
|
13
|
-
AudioContext::AudioContext() : BaseAudioContext() {
|
|
14
|
-
#ifdef ANDROID
|
|
15
|
-
audioPlayer_ = std::make_shared<AudioPlayer>(this->renderAudio());
|
|
16
|
-
#else
|
|
17
|
-
audioPlayer_ = std::make_shared<IOSAudioPlayer>(this->renderAudio());
|
|
18
|
-
#endif
|
|
19
|
-
sampleRate_ = audioPlayer_->getSampleRate();
|
|
20
|
-
audioDecoder_ = std::make_shared<AudioDecoder>(sampleRate_);
|
|
21
|
-
|
|
22
|
-
audioPlayer_->start();
|
|
23
|
-
}
|
|
24
|
-
|
|
25
13
|
AudioContext::AudioContext(float sampleRate) : BaseAudioContext() {
|
|
26
14
|
#ifdef ANDROID
|
|
27
15
|
audioPlayer_ = std::make_shared<AudioPlayer>(this->renderAudio(), sampleRate);
|
|
@@ -29,6 +17,7 @@ AudioContext::AudioContext(float sampleRate) : BaseAudioContext() {
|
|
|
29
17
|
audioPlayer_ =
|
|
30
18
|
std::make_shared<IOSAudioPlayer>(this->renderAudio(), sampleRate);
|
|
31
19
|
#endif
|
|
20
|
+
|
|
32
21
|
sampleRate_ = audioPlayer_->getSampleRate();
|
|
33
22
|
audioDecoder_ = std::make_shared<AudioDecoder>(sampleRate_);
|
|
34
23
|
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#include "OfflineAudioContext.h"
|
|
2
|
+
|
|
3
|
+
#include <audioapi/core/AudioContext.h>
|
|
4
|
+
#include <audioapi/core/Constants.h>
|
|
5
|
+
#include <audioapi/core/destinations/AudioDestinationNode.h>
|
|
6
|
+
#include <audioapi/core/sources/AudioBuffer.h>
|
|
7
|
+
#include <audioapi/core/utils/AudioDecoder.h>
|
|
8
|
+
#include <audioapi/core/utils/AudioNodeManager.h>
|
|
9
|
+
#include <audioapi/core/utils/Locker.h>
|
|
10
|
+
#include <audioapi/utils/AudioArray.h>
|
|
11
|
+
#include <audioapi/utils/AudioBus.h>
|
|
12
|
+
|
|
13
|
+
#include <algorithm>
|
|
14
|
+
#include <cassert>
|
|
15
|
+
#include <iostream>
|
|
16
|
+
#include <thread>
|
|
17
|
+
#include <utility>
|
|
18
|
+
|
|
19
|
+
namespace audioapi {
|
|
20
|
+
|
|
21
|
+
OfflineAudioContext::OfflineAudioContext(
|
|
22
|
+
int numberOfChannels,
|
|
23
|
+
size_t length,
|
|
24
|
+
float sampleRate)
|
|
25
|
+
: BaseAudioContext(),
|
|
26
|
+
length_(length),
|
|
27
|
+
numberOfChannels_(numberOfChannels),
|
|
28
|
+
currentSampleFrame_(0) {
|
|
29
|
+
sampleRate_ = sampleRate;
|
|
30
|
+
audioDecoder_ = std::make_shared<AudioDecoder>(sampleRate_);
|
|
31
|
+
resultBus_ = std::make_shared<AudioBus>(
|
|
32
|
+
static_cast<int>(length_), numberOfChannels_, sampleRate_);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
OfflineAudioContext::~OfflineAudioContext() {
|
|
36
|
+
nodeManager_->cleanup();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
void OfflineAudioContext::resume() {
|
|
40
|
+
Locker locker(mutex_);
|
|
41
|
+
|
|
42
|
+
if (state_ == ContextState::RUNNING) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
renderAudio();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
void OfflineAudioContext::suspend(
|
|
50
|
+
double when,
|
|
51
|
+
const std::function<void()> &callback) {
|
|
52
|
+
Locker locker(mutex_);
|
|
53
|
+
|
|
54
|
+
// we can only suspend once per render quantum at the end of the quantum
|
|
55
|
+
// first quantum is [0, RENDER_QUANTUM_SIZE)
|
|
56
|
+
auto frame = static_cast<size_t>(when * sampleRate_);
|
|
57
|
+
frame = RENDER_QUANTUM_SIZE *
|
|
58
|
+
((frame + RENDER_QUANTUM_SIZE - 1) / RENDER_QUANTUM_SIZE);
|
|
59
|
+
|
|
60
|
+
if (scheduledSuspends_.find(frame) != scheduledSuspends_.end()) {
|
|
61
|
+
throw std::runtime_error(
|
|
62
|
+
"cannot schedule more than one suspend at frame " +
|
|
63
|
+
std::to_string(frame) + " (" + std::to_string(when) + " seconds)");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
scheduledSuspends_.emplace(frame, callback);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
void OfflineAudioContext::renderAudio() {
|
|
70
|
+
state_ = ContextState::RUNNING;
|
|
71
|
+
std::thread([this]() {
|
|
72
|
+
auto audioBus = std::make_shared<AudioBus>(
|
|
73
|
+
RENDER_QUANTUM_SIZE, numberOfChannels_, sampleRate_);
|
|
74
|
+
|
|
75
|
+
while (currentSampleFrame_ < length_) {
|
|
76
|
+
Locker locker(mutex_);
|
|
77
|
+
int framesToProcess = std::min(
|
|
78
|
+
static_cast<int>(length_ - currentSampleFrame_), RENDER_QUANTUM_SIZE);
|
|
79
|
+
|
|
80
|
+
destination_->renderAudio(audioBus, framesToProcess);
|
|
81
|
+
|
|
82
|
+
for (int i = 0; i < framesToProcess; i++) {
|
|
83
|
+
for (int channel = 0; channel < numberOfChannels_; channel += 1) {
|
|
84
|
+
resultBus_->getChannel(channel)->getData()[currentSampleFrame_ + i] =
|
|
85
|
+
audioBus->getChannel(channel)->getData()[i];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
currentSampleFrame_ += framesToProcess;
|
|
90
|
+
|
|
91
|
+
// Execute scheduled suspend if exists
|
|
92
|
+
auto suspend = scheduledSuspends_.find(currentSampleFrame_);
|
|
93
|
+
if (suspend != scheduledSuspends_.end()) {
|
|
94
|
+
assert(currentSampleFrame_ < length_);
|
|
95
|
+
auto callback = suspend->second;
|
|
96
|
+
scheduledSuspends_.erase(currentSampleFrame_);
|
|
97
|
+
state_ = ContextState::SUSPENDED;
|
|
98
|
+
callback();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Rendering completed
|
|
104
|
+
auto buffer = std::make_shared<AudioBuffer>(resultBus_);
|
|
105
|
+
resultCallback_(buffer);
|
|
106
|
+
}).detach();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
void OfflineAudioContext::startRendering(
|
|
110
|
+
OfflineAudioContextResultCallback callback) {
|
|
111
|
+
Locker locker(mutex_);
|
|
112
|
+
|
|
113
|
+
resultCallback_ = std::move(callback);
|
|
114
|
+
renderAudio();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
} // namespace audioapi
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "BaseAudioContext.h"
|
|
4
|
+
|
|
5
|
+
#include <mutex>
|
|
6
|
+
#include <map>
|
|
7
|
+
#include <unordered_map>
|
|
8
|
+
#include <memory>
|
|
9
|
+
|
|
10
|
+
namespace audioapi {
|
|
11
|
+
|
|
12
|
+
using OfflineAudioContextSuspendCallback = std::function<void()>;
|
|
13
|
+
using OfflineAudioContextResultCallback = std::function<void(std::shared_ptr<AudioBuffer>)>;
|
|
14
|
+
|
|
15
|
+
class OfflineAudioContext : public BaseAudioContext {
|
|
16
|
+
public:
|
|
17
|
+
explicit OfflineAudioContext(int numberOfChannels, size_t length, float sampleRate);
|
|
18
|
+
~OfflineAudioContext() override;
|
|
19
|
+
|
|
20
|
+
void resume();
|
|
21
|
+
void suspend(double when, const OfflineAudioContextSuspendCallback& callback);
|
|
22
|
+
|
|
23
|
+
void startRendering(OfflineAudioContextResultCallback callback);
|
|
24
|
+
|
|
25
|
+
private:
|
|
26
|
+
std::mutex mutex_;
|
|
27
|
+
|
|
28
|
+
std::unordered_map<size_t, OfflineAudioContextSuspendCallback> scheduledSuspends_;
|
|
29
|
+
OfflineAudioContextResultCallback resultCallback_;
|
|
30
|
+
|
|
31
|
+
size_t length_;
|
|
32
|
+
int numberOfChannels_;
|
|
33
|
+
size_t currentSampleFrame_;
|
|
34
|
+
|
|
35
|
+
std::shared_ptr<AudioBus> resultBus_;
|
|
36
|
+
|
|
37
|
+
void renderAudio();
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
} // namespace audioapi
|
|
@@ -101,7 +101,7 @@ void AudioBufferSourceNode::setBuffer(
|
|
|
101
101
|
|
|
102
102
|
loopEnd_ = buffer_->getDuration();
|
|
103
103
|
|
|
104
|
-
stretch_->presetDefault(channelCount_, buffer_->getSampleRate());
|
|
104
|
+
stretch_->presetDefault(channelCount_, buffer_->getSampleRate(), true);
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
void AudioBufferSourceNode::start(double when, double offset, double duration) {
|
|
@@ -181,7 +181,7 @@ void AudioBufferSourceNode::processWithoutPitchCorrection(
|
|
|
181
181
|
auto computedPlaybackRate = getComputedPlaybackRateValue();
|
|
182
182
|
updatePlaybackInfo(processingBus, framesToProcess, startOffset, offsetLength);
|
|
183
183
|
|
|
184
|
-
if (computedPlaybackRate == 0.0f || !isPlaying()) {
|
|
184
|
+
if (computedPlaybackRate == 0.0f || (!isPlaying() && !isStopScheduled())) {
|
|
185
185
|
processingBus->zero();
|
|
186
186
|
return;
|
|
187
187
|
}
|
|
@@ -215,7 +215,7 @@ void AudioBufferSourceNode::processWithPitchCorrection(
|
|
|
215
215
|
updatePlaybackInfo(
|
|
216
216
|
playbackRateBus_, framesNeededToStretch, startOffset, offsetLength);
|
|
217
217
|
|
|
218
|
-
if (playbackRate == 0.0f || !isPlaying()) {
|
|
218
|
+
if (playbackRate == 0.0f || (!isPlaying() && !isStopScheduled())) {
|
|
219
219
|
processingBus->zero();
|
|
220
220
|
return;
|
|
221
221
|
}
|
|
@@ -71,7 +71,7 @@ void AudioScheduledSourceNode::updatePlaybackInfo(
|
|
|
71
71
|
std::max(dsp::timeToSampleFrame(startTime_, sampleRate), firstFrame);
|
|
72
72
|
size_t stopFrame = stopTime_ == -1.0
|
|
73
73
|
? std::numeric_limits<size_t>::max()
|
|
74
|
-
:
|
|
74
|
+
: dsp::timeToSampleFrame(stopTime_, sampleRate);
|
|
75
75
|
|
|
76
76
|
if (isUnscheduled() || isFinished()) {
|
|
77
77
|
startOffset = 0;
|
|
@@ -93,7 +93,18 @@ void AudioScheduledSourceNode::updatePlaybackInfo(
|
|
|
93
93
|
startOffset = std::max(startFrame, firstFrame) - firstFrame > 0
|
|
94
94
|
? std::max(startFrame, firstFrame) - firstFrame
|
|
95
95
|
: 0;
|
|
96
|
-
nonSilentFramesToProcess =
|
|
96
|
+
nonSilentFramesToProcess =
|
|
97
|
+
std::max(std::min(lastFrame, stopFrame), startFrame) - startFrame;
|
|
98
|
+
|
|
99
|
+
assert(startOffset <= framesToProcess);
|
|
100
|
+
assert(nonSilentFramesToProcess <= framesToProcess);
|
|
101
|
+
|
|
102
|
+
// stop will happen in the same render quantum
|
|
103
|
+
if (stopFrame < lastFrame && stopFrame >= firstFrame) {
|
|
104
|
+
playbackState_ = PlaybackState::STOP_SCHEDULED;
|
|
105
|
+
processingBus->zero(stopFrame - firstFrame, lastFrame - stopFrame);
|
|
106
|
+
}
|
|
107
|
+
|
|
97
108
|
processingBus->zero(0, startOffset);
|
|
98
109
|
return;
|
|
99
110
|
}
|
|
@@ -106,10 +117,25 @@ void AudioScheduledSourceNode::updatePlaybackInfo(
|
|
|
106
117
|
playbackState_ = PlaybackState::STOP_SCHEDULED;
|
|
107
118
|
startOffset = 0;
|
|
108
119
|
nonSilentFramesToProcess = stopFrame - firstFrame;
|
|
120
|
+
|
|
121
|
+
assert(startOffset <= framesToProcess);
|
|
122
|
+
assert(nonSilentFramesToProcess <= framesToProcess);
|
|
123
|
+
|
|
109
124
|
processingBus->zero(stopFrame - firstFrame, lastFrame - stopFrame);
|
|
110
125
|
return;
|
|
111
126
|
}
|
|
112
127
|
|
|
128
|
+
// mark as finished in first silent render quantum
|
|
129
|
+
if (stopFrame < firstFrame) {
|
|
130
|
+
startOffset = 0;
|
|
131
|
+
nonSilentFramesToProcess = 0;
|
|
132
|
+
|
|
133
|
+
playbackState_ = PlaybackState::STOP_SCHEDULED;
|
|
134
|
+
handleStopScheduled();
|
|
135
|
+
playbackState_ = PlaybackState::FINISHED;
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
113
139
|
// normal "mid-buffer" playback
|
|
114
140
|
startOffset = 0;
|
|
115
141
|
nonSilentFramesToProcess = framesToProcess;
|