react-native-audio-api 0.12.1 → 0.12.2
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/README.md +8 -3
- package/RNAudioAPI.podspec +0 -2
- package/android/src/main/cpp/audioapi/CMakeLists.txt +8 -2
- package/android/src/main/cpp/audioapi/android/AudioAPIModule.cpp +7 -29
- package/android/src/main/cpp/audioapi/android/JniEventPayloadParser.cpp +83 -0
- package/android/src/main/cpp/audioapi/android/JniEventPayloadParser.h +14 -0
- package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +4 -3
- package/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +1 -0
- package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp +37 -21
- package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h +1 -1
- package/common/cpp/audioapi/AudioAPIModuleInstaller.h +21 -0
- package/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.cpp +26 -4
- package/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.h +1 -0
- package/common/cpp/audioapi/HostObjects/sources/AudioFileSourceNodeHostObject.cpp +2 -2
- package/common/cpp/audioapi/HostObjects/utils/AudioDecoderHostObject.cpp +3 -3
- package/common/cpp/audioapi/HostObjects/utils/AudioFileUtilsHostObject.cpp +60 -0
- package/common/cpp/audioapi/HostObjects/utils/AudioFileUtilsHostObject.h +24 -0
- package/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h +2 -2
- package/common/cpp/audioapi/core/OfflineAudioContext.cpp +0 -1
- package/common/cpp/audioapi/core/OfflineAudioContext.h +0 -1
- package/common/cpp/audioapi/core/effects/ConvolverNode.cpp +4 -10
- package/common/cpp/audioapi/core/effects/ConvolverNode.h +0 -4
- package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp +13 -11
- package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h +5 -9
- package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp +47 -139
- package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h +11 -8
- package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +29 -114
- package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h +6 -8
- package/common/cpp/audioapi/core/sources/AudioFileSourceNode.cpp +9 -11
- package/common/cpp/audioapi/core/sources/AudioFileSourceNode.h +1 -1
- package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +2 -2
- package/common/cpp/audioapi/core/types/AudioFormat.h +3 -1
- package/common/cpp/audioapi/core/utils/AudioDecoder.cpp +120 -91
- package/common/cpp/audioapi/core/utils/AudioDecoder.h +24 -101
- package/common/cpp/audioapi/core/utils/AudioFileConcatenator.cpp +862 -0
- package/common/cpp/audioapi/core/utils/AudioFileConcatenator.h +164 -0
- package/common/cpp/audioapi/core/utils/AudioFileWriter.cpp +2 -4
- package/common/cpp/audioapi/core/utils/AudioRecorderCallback.cpp +7 -12
- package/common/cpp/audioapi/core/utils/AudioRecorderCallback.h +2 -0
- package/common/cpp/audioapi/core/utils/buffer/BufferProcessingDirection.h +6 -0
- package/common/cpp/audioapi/core/utils/buffer/BufferProcessorBase.cpp +110 -0
- package/common/cpp/audioapi/core/utils/buffer/BufferProcessorBase.h +75 -0
- package/common/cpp/audioapi/core/utils/buffer/QueueBufferProcessor.cpp +129 -0
- package/common/cpp/audioapi/core/utils/buffer/QueueBufferProcessor.h +55 -0
- package/common/cpp/audioapi/core/utils/buffer/SingleBufferProcessor.cpp +95 -0
- package/common/cpp/audioapi/core/utils/buffer/SingleBufferProcessor.h +52 -0
- package/common/cpp/audioapi/events/AudioEventHandlerRegistry.cpp +65 -157
- package/common/cpp/audioapi/events/AudioEventHandlerRegistry.h +52 -33
- package/common/cpp/audioapi/events/AudioEventPayload.h +87 -0
- package/common/cpp/audioapi/events/IAudioEventHandlerRegistry.h +12 -12
- package/common/cpp/audioapi/libs/decoding/IncrementalAudioDecoder.h +12 -10
- package/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp +152 -78
- package/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.h +6 -6
- package/common/cpp/audioapi/libs/miniaudio/MiniAudioDecoding.cpp +34 -20
- package/common/cpp/audioapi/libs/miniaudio/MiniAudioDecoding.h +4 -4
- package/common/cpp/audioapi/utils/AudioArray.hpp +6 -1
- package/common/cpp/audioapi/utils/CrossThreadEventScheduler.hpp +1 -1
- package/common/cpp/audioapi/utils/TaskOffloader.hpp +3 -5
- package/ios/audioapi/ios/AudioAPIModule.h +2 -1
- package/ios/audioapi/ios/AudioAPIModule.mm +4 -25
- package/ios/audioapi/ios/core/IOSAudioPlayer.h +16 -2
- package/ios/audioapi/ios/core/IOSAudioPlayer.mm +90 -24
- package/ios/audioapi/ios/core/IOSAudioRecorder.mm +1 -1
- package/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm +64 -20
- package/ios/audioapi/ios/system/AudioSessionManager.mm +18 -7
- package/ios/audioapi/ios/system/SystemNotificationManager.mm +22 -22
- package/ios/audioapi/ios/system/notification/PlaybackNotification.mm +10 -13
- package/lib/commonjs/AudioAPIModule/AudioAPIModule.js +1 -1
- package/lib/commonjs/AudioAPIModule/AudioAPIModule.js.map +1 -1
- package/lib/commonjs/api.js +8 -0
- package/lib/commonjs/api.js.map +1 -1
- package/lib/commonjs/api.web.js +5 -0
- package/lib/commonjs/api.web.js.map +1 -1
- package/lib/commonjs/core/AudioFileUtils.js +35 -0
- package/lib/commonjs/core/AudioFileUtils.js.map +1 -0
- package/lib/commonjs/mock/index.js +15 -1
- package/lib/commonjs/mock/index.js.map +1 -1
- package/lib/module/AudioAPIModule/AudioAPIModule.js +1 -1
- package/lib/module/AudioAPIModule/AudioAPIModule.js.map +1 -1
- package/lib/module/api.js +1 -0
- package/lib/module/api.js.map +1 -1
- package/lib/module/api.web.js +3 -0
- package/lib/module/api.web.js.map +1 -1
- package/lib/module/core/AudioFileUtils.js +31 -0
- package/lib/module/core/AudioFileUtils.js.map +1 -0
- package/lib/module/mock/index.js +14 -1
- package/lib/module/mock/index.js.map +1 -1
- package/lib/typescript/AudioAPIModule/AudioAPIModule.d.ts.map +1 -1
- package/lib/typescript/api.d.ts +1 -0
- 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/AudioFileUtils.d.ts +2 -0
- package/lib/typescript/core/AudioFileUtils.d.ts.map +1 -0
- package/lib/typescript/interfaces.d.ts +3 -0
- package/lib/typescript/interfaces.d.ts.map +1 -1
- package/lib/typescript/mock/index.d.ts +3 -1
- package/lib/typescript/mock/index.d.ts.map +1 -1
- package/package.json +3 -3
- package/scripts/download-prebuilt-binaries.sh +34 -1
- package/src/AudioAPIModule/AudioAPIModule.ts +1 -0
- package/src/AudioAPIModule/globals.d.ts +3 -0
- package/src/api.ts +1 -0
- package/src/api.web.ts +7 -0
- package/src/core/AudioFileUtils.ts +49 -0
- package/src/interfaces.ts +7 -0
- package/src/mock/index.ts +29 -0
package/README.md
CHANGED
|
@@ -28,9 +28,6 @@ check out the [Getting Started](https://docs.swmansion.com/react-native-audio-ap
|
|
|
28
28
|
- **DynamicCompressorNode 〽️**<br />
|
|
29
29
|
Reduce the volume of loud sounds and boost quieter nodes to balance the audio signal, avoid clipping or distorted sounds
|
|
30
30
|
|
|
31
|
-
- **Audio tag 🏷️**<br />
|
|
32
|
-
Simple ability to play and buffer audio, with all of the most commonly used functions, same as on the web, without the need to create and manipulate an audio graph.
|
|
33
|
-
|
|
34
31
|
- **MIDI support 🎸**<br />
|
|
35
32
|
Complementary lib for react-native-audio-api, that will allow to communicate with MIDI devices or read/write MIDI files.
|
|
36
33
|
|
|
@@ -40,6 +37,14 @@ check out the [Getting Started](https://docs.swmansion.com/react-native-audio-ap
|
|
|
40
37
|
- **Noise Cancellation 🦇**<br />
|
|
41
38
|
System-based active noise and echo cancellation support
|
|
42
39
|
|
|
40
|
+
### <a href="https://github.com/software-mansion/react-native-audio-api/releases/tag/0.12.0"><img src="https://img.shields.io/badge/Released_in-0.12.0-green" /></a>
|
|
41
|
+
|
|
42
|
+
- **Audio tag 🏷️**<br />
|
|
43
|
+
Simple ability to play and buffer audio, with all of the most commonly used functions, same as on the web, without the need to create and manipulate an audio graph.
|
|
44
|
+
|
|
45
|
+
- **Recording rotation 🎤**<br />
|
|
46
|
+
Ability to chunk your recording into smaller files, increasing resilience to unpredictable events.
|
|
47
|
+
|
|
43
48
|
### <a href="https://github.com/software-mansion/react-native-audio-api/releases/tag/0.11.0"><img src="https://img.shields.io/badge/Released_in-0.11.0-green" /></a>
|
|
44
49
|
|
|
45
50
|
- **Recording to file 📼**<br />
|
package/RNAudioAPI.podspec
CHANGED
|
@@ -131,8 +131,6 @@ Pod::Spec.new do |s|
|
|
|
131
131
|
-force_load #{lib_dir}/libvorbis.a
|
|
132
132
|
-force_load #{lib_dir}/libvorbisenc.a
|
|
133
133
|
-force_load #{lib_dir}/libvorbisfile.a
|
|
134
|
-
-force_load #{lib_dir}/libssl.a
|
|
135
|
-
-force_load #{lib_dir}/libcrypto.a
|
|
136
134
|
].join(" "),
|
|
137
135
|
}
|
|
138
136
|
# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
|
|
@@ -16,6 +16,14 @@ set_source_files_properties(
|
|
|
16
16
|
COMPILE_FLAGS "-O3"
|
|
17
17
|
)
|
|
18
18
|
|
|
19
|
+
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
|
|
20
|
+
set_source_files_properties(
|
|
21
|
+
"${COMMON_CPP_DIR}/audioapi/dsp/r8brain/fft/pffft_double.c"
|
|
22
|
+
PROPERTIES
|
|
23
|
+
COMPILE_FLAGS "-Wno-#pragma-messages"
|
|
24
|
+
)
|
|
25
|
+
endif()
|
|
26
|
+
|
|
19
27
|
set(INCLUDE_DIR ${COMMON_CPP_DIR}/audioapi/external/include)
|
|
20
28
|
set(FFMPEG_INCLUDE_DIR ${COMMON_CPP_DIR}/audioapi/external/include_ffmpeg)
|
|
21
29
|
set(EXTERNAL_DIR ${COMMON_CPP_DIR}/audioapi/external/android)
|
|
@@ -132,8 +140,6 @@ target_link_libraries(react-native-audio-api
|
|
|
132
140
|
vorbis
|
|
133
141
|
vorbisenc
|
|
134
142
|
vorbisfile
|
|
135
|
-
crypto
|
|
136
|
-
ssl
|
|
137
143
|
z
|
|
138
144
|
)
|
|
139
145
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#include <audioapi/android/AudioAPIModule.h>
|
|
2
|
+
#include <audioapi/android/JniEventPayloadParser.h>
|
|
2
3
|
#include <memory>
|
|
3
|
-
#include <string>
|
|
4
|
-
#include <unordered_map>
|
|
5
4
|
|
|
6
5
|
namespace audioapi {
|
|
7
6
|
|
|
@@ -66,34 +65,13 @@ void AudioAPIModule::injectJSIBindings() {
|
|
|
66
65
|
void AudioAPIModule::invokeHandlerWithEventNameAndEventBody(
|
|
67
66
|
jint eventOrdinal,
|
|
68
67
|
jni::alias_ref<jni::JMap<jstring, jobject>> eventBody) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
for (const auto &entry : *eventBody) {
|
|
72
|
-
std::string name = entry.first->toStdString();
|
|
73
|
-
auto value = entry.second;
|
|
74
|
-
|
|
75
|
-
if (value->isInstanceOf(jni::JString::javaClassStatic())) {
|
|
76
|
-
body[name] = jni::static_ref_cast<jni::JString>(value)->toStdString();
|
|
77
|
-
} else if (value->isInstanceOf(jni::JInteger::javaClassStatic())) {
|
|
78
|
-
body[name] = jni::static_ref_cast<jni::JInteger>(value)->value();
|
|
79
|
-
} else if (value->isInstanceOf(jni::JDouble::javaClassStatic())) {
|
|
80
|
-
body[name] = jni::static_ref_cast<jni::JDouble>(value)->value();
|
|
81
|
-
} else if (value->isInstanceOf(jni::JFloat::javaClassStatic())) {
|
|
82
|
-
body[name] = jni::static_ref_cast<jni::JFloat>(value)->value();
|
|
83
|
-
} else if (value->isInstanceOf(jni::JBoolean::javaClassStatic())) {
|
|
84
|
-
auto booleanValue = jni::static_ref_cast<jni::JBoolean>(value)->value();
|
|
85
|
-
|
|
86
|
-
if (booleanValue) {
|
|
87
|
-
body[name] = true;
|
|
88
|
-
} else {
|
|
89
|
-
body[name] = false;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
68
|
+
if (audioEventHandlerRegistry_ == nullptr) {
|
|
69
|
+
return;
|
|
92
70
|
}
|
|
93
71
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
72
|
+
auto event = static_cast<AudioEvent>(eventOrdinal);
|
|
73
|
+
audioEventHandlerRegistry_->dispatchEvent(
|
|
74
|
+
event, kBroadcastListenerId, buildPayloadFromJniMap(event, eventBody));
|
|
98
75
|
}
|
|
76
|
+
|
|
99
77
|
} // namespace audioapi
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#include <audioapi/android/JniEventPayloadParser.h>
|
|
2
|
+
#include <string>
|
|
3
|
+
|
|
4
|
+
namespace audioapi {
|
|
5
|
+
|
|
6
|
+
using namespace facebook;
|
|
7
|
+
|
|
8
|
+
jni::local_ref<jobject> jniMapGet(
|
|
9
|
+
const jni::alias_ref<jni::JMap<jstring, jobject>> &map,
|
|
10
|
+
const char *key) {
|
|
11
|
+
if (!map) {
|
|
12
|
+
return nullptr;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static auto getMethod =
|
|
16
|
+
jni::JMap<jstring, jobject>::javaClassStatic()->getMethod<jobject(jobject)>("get");
|
|
17
|
+
|
|
18
|
+
auto jKey = jni::make_jstring(key);
|
|
19
|
+
return getMethod(map, jKey.get());
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
double jniGetDouble(const jni::alias_ref<jni::JMap<jstring, jobject>> &map, const char *key) {
|
|
23
|
+
auto val = jniMapGet(map, key);
|
|
24
|
+
if (!val) {
|
|
25
|
+
return 0.0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (val->isInstanceOf(jni::JDouble::javaClassStatic())) {
|
|
29
|
+
return jni::static_ref_cast<jni::JDouble>(val)->value();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (val->isInstanceOf(jni::JFloat::javaClassStatic())) {
|
|
33
|
+
return static_cast<double>(jni::static_ref_cast<jni::JFloat>(val)->value());
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (val->isInstanceOf(jni::JInteger::javaClassStatic())) {
|
|
37
|
+
return static_cast<double>(jni::static_ref_cast<jni::JInteger>(val)->value());
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (val->isInstanceOf(jni::JLong::javaClassStatic())) {
|
|
41
|
+
return static_cast<double>(jni::static_ref_cast<jni::JLong>(val)->value());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return 0.0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
std::string jniGetString(const jni::alias_ref<jni::JMap<jstring, jobject>> &map, const char *key) {
|
|
48
|
+
auto val = jniMapGet(map, key);
|
|
49
|
+
if (val && val->isInstanceOf(jni::JString::javaClassStatic())) {
|
|
50
|
+
return jni::static_ref_cast<jni::JString>(val)->toStdString();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
bool jniGetBool(const jni::alias_ref<jni::JMap<jstring, jobject>> &map, const char *key) {
|
|
57
|
+
auto val = jniMapGet(map, key);
|
|
58
|
+
if (val && val->isInstanceOf(jni::JBoolean::javaClassStatic())) {
|
|
59
|
+
return static_cast<bool>(jni::static_ref_cast<jni::JBoolean>(val)->value());
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
AudioEventPayload buildPayloadFromJniMap(
|
|
65
|
+
AudioEvent event,
|
|
66
|
+
const jni::alias_ref<jni::JMap<jstring, jobject>> &map) {
|
|
67
|
+
switch (event) {
|
|
68
|
+
case AudioEvent::INTERRUPTION:
|
|
69
|
+
return InterruptionPayload{
|
|
70
|
+
.type = jniGetString(map, "type"), .shouldResume = jniGetBool(map, "shouldResume")};
|
|
71
|
+
|
|
72
|
+
case AudioEvent::VOLUME_CHANGE:
|
|
73
|
+
case AudioEvent::PLAYBACK_NOTIFICATION_SKIP_FORWARD:
|
|
74
|
+
case AudioEvent::PLAYBACK_NOTIFICATION_SKIP_BACKWARD:
|
|
75
|
+
case AudioEvent::PLAYBACK_NOTIFICATION_SEEK_TO:
|
|
76
|
+
return DoubleValuePayload{.value = jniGetDouble(map, "value")};
|
|
77
|
+
|
|
78
|
+
default:
|
|
79
|
+
return EmptyPayload{};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
} // namespace audioapi
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <audioapi/events/AudioEvent.h>
|
|
4
|
+
#include <audioapi/events/AudioEventPayload.h>
|
|
5
|
+
#include <fbjni/fbjni.h>
|
|
6
|
+
|
|
7
|
+
namespace audioapi {
|
|
8
|
+
|
|
9
|
+
/// @brief Builds AudioEventPayload from a JNI map passed from Java/Kotlin.
|
|
10
|
+
AudioEventPayload buildPayloadFromJniMap(
|
|
11
|
+
AudioEvent event,
|
|
12
|
+
const facebook::jni::alias_ref<facebook::jni::JMap<jstring, jobject>> &map);
|
|
13
|
+
|
|
14
|
+
} // namespace audioapi
|
|
@@ -575,9 +575,10 @@ void AndroidAudioRecorder::onErrorAfterClose(oboe::AudioStream *stream, oboe::Re
|
|
|
575
575
|
}
|
|
576
576
|
|
|
577
577
|
std::string message = "Android recorder error: " + streamResult.unwrap_err();
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
578
|
+
audioEventHandlerRegistry_->dispatchEvent(
|
|
579
|
+
AudioEvent::RECORDER_ERROR,
|
|
580
|
+
callbackId,
|
|
581
|
+
StringPayload{.name = "message", .reason = std::move(message)});
|
|
581
582
|
return;
|
|
582
583
|
}
|
|
583
584
|
|
|
@@ -31,6 +31,7 @@ bool AudioPlayer::openAudioStream() {
|
|
|
31
31
|
->setPerformanceMode(PerformanceMode::None)
|
|
32
32
|
->setChannelCount(channelCount_)
|
|
33
33
|
->setSampleRateConversionQuality(SampleRateConversionQuality::Medium)
|
|
34
|
+
->setFramesPerDataCallback(RENDER_QUANTUM_SIZE)
|
|
34
35
|
->setDataCallback(this)
|
|
35
36
|
->setSampleRate(static_cast<int>(sampleRate_))
|
|
36
37
|
->setErrorCallback(this);
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
#include <audioapi/utils/CircularArray.hpp>
|
|
8
8
|
|
|
9
9
|
#include <algorithm>
|
|
10
|
+
#include <cstring>
|
|
10
11
|
#include <memory>
|
|
11
12
|
#include <string>
|
|
12
13
|
#include <unordered_map>
|
|
@@ -35,20 +36,7 @@ AndroidRecorderCallback::AndroidRecorderCallback(
|
|
|
35
36
|
callbackId) {}
|
|
36
37
|
|
|
37
38
|
AndroidRecorderCallback::~AndroidRecorderCallback() {
|
|
38
|
-
|
|
39
|
-
ma_data_converter_uninit(converter_.get(), nullptr);
|
|
40
|
-
converter_.reset();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (processingBuffer_ != nullptr) {
|
|
44
|
-
ma_free(processingBuffer_, nullptr);
|
|
45
|
-
processingBuffer_ = nullptr;
|
|
46
|
-
processingBufferLength_ = 0;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
for (size_t i = 0; i < circularBuffer_.size(); ++i) {
|
|
50
|
-
circularBuffer_[i]->zero();
|
|
51
|
-
}
|
|
39
|
+
cleanup();
|
|
52
40
|
}
|
|
53
41
|
|
|
54
42
|
/// @brief Prepares the recorder callback by initializing the data converter and allocating necessary buffers.
|
|
@@ -111,6 +99,10 @@ Result<NoneType, std::string> AndroidRecorderCallback::prepare(
|
|
|
111
99
|
}
|
|
112
100
|
|
|
113
101
|
void AndroidRecorderCallback::cleanup() {
|
|
102
|
+
std::scoped_lock audioLock(destructionAudioGuard_);
|
|
103
|
+
// join the worker
|
|
104
|
+
offloader_.reset();
|
|
105
|
+
|
|
114
106
|
if (circularBuffer_[0]->getNumberOfAvailableFrames() > 0) {
|
|
115
107
|
emitAudioData(true);
|
|
116
108
|
}
|
|
@@ -126,10 +118,9 @@ void AndroidRecorderCallback::cleanup() {
|
|
|
126
118
|
processingBufferLength_ = 0;
|
|
127
119
|
}
|
|
128
120
|
|
|
129
|
-
for (
|
|
130
|
-
|
|
121
|
+
for (const auto &arr : circularBuffer_) {
|
|
122
|
+
arr->zero();
|
|
131
123
|
}
|
|
132
|
-
offloader_.reset();
|
|
133
124
|
}
|
|
134
125
|
|
|
135
126
|
/// @brief Receives audio data from the recorder, processes it (resampling and deinterleaving if necessary),
|
|
@@ -137,11 +128,27 @@ void AndroidRecorderCallback::cleanup() {
|
|
|
137
128
|
/// @param data Pointer to the incoming audio data.
|
|
138
129
|
/// @param numFrames Number of frames in the incoming audio data.
|
|
139
130
|
void AndroidRecorderCallback::receiveAudioData(void *data, int numFrames) {
|
|
131
|
+
// if we wait here, we are in the middle of the destruction
|
|
132
|
+
std::scoped_lock lock(destructionAudioGuard_);
|
|
133
|
+
if (offloader_ == nullptr) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
140
136
|
if (!isInitialized_.load(std::memory_order_acquire)) {
|
|
141
137
|
return;
|
|
142
138
|
}
|
|
143
139
|
|
|
144
|
-
|
|
140
|
+
// Oboe owns `data` only for the duration of this synchronous callback.
|
|
141
|
+
// Copy into an owned buffer before handing off to the worker thread; the
|
|
142
|
+
// consumer in taskOffloaderFunction frees it.
|
|
143
|
+
size_t bytes =
|
|
144
|
+
static_cast<size_t>(numFrames) * streamChannelCount_ * ma_get_bytes_per_sample(ma_format_f32);
|
|
145
|
+
void *owned = ma_malloc(bytes, nullptr);
|
|
146
|
+
if (owned == nullptr) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
std::memcpy(owned, data, bytes);
|
|
150
|
+
|
|
151
|
+
offloader_->getSender()->send({.data = owned, .numFrames = numFrames});
|
|
145
152
|
}
|
|
146
153
|
|
|
147
154
|
/// @brief Deinterleaves the audio data and pushes it into the circular buffer.
|
|
@@ -151,7 +158,7 @@ void AndroidRecorderCallback::deinterleaveAndPushAudioData(void *data, int numFr
|
|
|
151
158
|
auto *inputData = static_cast<float *>(data);
|
|
152
159
|
deinterleavingBuffer_->deinterleaveFrom(inputData, numFrames);
|
|
153
160
|
|
|
154
|
-
for (
|
|
161
|
+
for (int ch = 0; ch < channelCount_; ++ch) {
|
|
155
162
|
circularBuffer_[ch]->push_back(*deinterleavingBuffer_->getChannel(ch), numFrames);
|
|
156
163
|
}
|
|
157
164
|
}
|
|
@@ -160,16 +167,23 @@ void AndroidRecorderCallback::deinterleaveAndPushAudioData(void *data, int numFr
|
|
|
160
167
|
/// processes it (resampling and deinterleaving if necessary), and pushes it into the circular buffer.
|
|
161
168
|
void AndroidRecorderCallback::taskOffloaderFunction(CallbackData callbackData) {
|
|
162
169
|
auto [data, numFrames] = callbackData;
|
|
170
|
+
|
|
171
|
+
// The TaskOffloader destructor sends a default-constructed CallbackData
|
|
172
|
+
// (data == nullptr) to unblock the receiver; ignore it here.
|
|
173
|
+
if (data == nullptr) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
163
177
|
ma_uint64 inputFrameCount = numFrames;
|
|
164
178
|
ma_uint64 outputFrameCount = 0;
|
|
165
179
|
|
|
166
|
-
if (
|
|
167
|
-
streamChannelCount_ == channelCount_) {
|
|
180
|
+
if (streamSampleRate_ == sampleRate_ && streamChannelCount_ == channelCount_) {
|
|
168
181
|
deinterleaveAndPushAudioData(data, numFrames);
|
|
169
182
|
|
|
170
183
|
if (circularBuffer_[0]->getNumberOfAvailableFrames() >= bufferLength_) {
|
|
171
184
|
emitAudioData();
|
|
172
185
|
}
|
|
186
|
+
ma_free(data, nullptr);
|
|
173
187
|
return;
|
|
174
188
|
}
|
|
175
189
|
|
|
@@ -184,6 +198,8 @@ void AndroidRecorderCallback::taskOffloaderFunction(CallbackData callbackData) {
|
|
|
184
198
|
if (circularBuffer_[0]->getNumberOfAvailableFrames() >= bufferLength_) {
|
|
185
199
|
emitAudioData();
|
|
186
200
|
}
|
|
201
|
+
|
|
202
|
+
ma_free(data, nullptr);
|
|
187
203
|
}
|
|
188
204
|
|
|
189
205
|
} // namespace audioapi
|
|
@@ -29,7 +29,7 @@ class AndroidRecorderCallback : public AudioRecorderCallback {
|
|
|
29
29
|
|
|
30
30
|
Result<NoneType, std::string>
|
|
31
31
|
prepare(float streamSampleRate, int streamChannelCount, size_t maxInputBufferLength);
|
|
32
|
-
void cleanup()
|
|
32
|
+
void cleanup() final;
|
|
33
33
|
|
|
34
34
|
void receiveAudioData(void *data, int numFrames);
|
|
35
35
|
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
#include <audioapi/HostObjects/inputs/AudioRecorderHostObject.h>
|
|
6
6
|
#include <audioapi/HostObjects/sources/AudioBufferHostObject.h>
|
|
7
7
|
#include <audioapi/HostObjects/utils/AudioDecoderHostObject.h>
|
|
8
|
+
#include <audioapi/HostObjects/utils/AudioFileUtilsHostObject.h>
|
|
8
9
|
#include <audioapi/HostObjects/utils/AudioStretcherHostObject.h>
|
|
9
10
|
#include <audioapi/core/AudioContext.h>
|
|
10
11
|
#include <audioapi/core/OfflineAudioContext.h>
|
|
@@ -37,6 +38,7 @@ class AudioAPIModuleInstaller {
|
|
|
37
38
|
jsiRuntime, jsCallInvoker, audioEventHandlerRegistry, uiRuntime);
|
|
38
39
|
auto createAudioBuffer = getCreateAudioBufferFunction(jsiRuntime);
|
|
39
40
|
auto createAudioDecoder = getCreateAudioDecoderFunction(jsiRuntime, jsCallInvoker);
|
|
41
|
+
auto createAudioFileUtils = getCreateAudioFileUtilsFunction(jsiRuntime, jsCallInvoker);
|
|
40
42
|
auto createAudioStretcher = getCreateAudioStretcherFunction(jsiRuntime, jsCallInvoker);
|
|
41
43
|
|
|
42
44
|
jsiRuntime->global().setProperty(*jsiRuntime, "createAudioContext", createAudioContext);
|
|
@@ -45,6 +47,7 @@ class AudioAPIModuleInstaller {
|
|
|
45
47
|
*jsiRuntime, "createOfflineAudioContext", createOfflineAudioContext);
|
|
46
48
|
jsiRuntime->global().setProperty(*jsiRuntime, "createAudioBuffer", createAudioBuffer);
|
|
47
49
|
jsiRuntime->global().setProperty(*jsiRuntime, "createAudioDecoder", createAudioDecoder);
|
|
50
|
+
jsiRuntime->global().setProperty(*jsiRuntime, "createAudioFileUtils", createAudioFileUtils);
|
|
48
51
|
jsiRuntime->global().setProperty(*jsiRuntime, "createAudioStretcher", createAudioStretcher);
|
|
49
52
|
|
|
50
53
|
auto audioEventHandlerRegistryHostObject =
|
|
@@ -185,6 +188,24 @@ class AudioAPIModuleInstaller {
|
|
|
185
188
|
});
|
|
186
189
|
}
|
|
187
190
|
|
|
191
|
+
static jsi::Function getCreateAudioFileUtilsFunction(
|
|
192
|
+
jsi::Runtime *jsiRuntime,
|
|
193
|
+
const std::shared_ptr<react::CallInvoker> &jsCallInvoker) {
|
|
194
|
+
return jsi::Function::createFromHostFunction(
|
|
195
|
+
*jsiRuntime,
|
|
196
|
+
jsi::PropNameID::forAscii(*jsiRuntime, "createAudioFileUtils"),
|
|
197
|
+
0,
|
|
198
|
+
[jsCallInvoker](
|
|
199
|
+
jsi::Runtime &runtime,
|
|
200
|
+
const jsi::Value &thisValue,
|
|
201
|
+
const jsi::Value *args,
|
|
202
|
+
size_t count) -> jsi::Value {
|
|
203
|
+
auto audioFileUtilsHostObject =
|
|
204
|
+
std::make_shared<AudioFileUtilsHostObject>(&runtime, jsCallInvoker);
|
|
205
|
+
return jsi::Object::createFromHostObject(runtime, audioFileUtilsHostObject);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
188
209
|
static jsi::Function getCreateAudioBufferFunction(jsi::Runtime *jsiRuntime) {
|
|
189
210
|
return jsi::Function::createFromHostFunction(
|
|
190
211
|
*jsiRuntime,
|
|
@@ -68,9 +68,24 @@ JSI_HOST_FUNCTION_IMPL(AudioBufferQueueSourceNodeHostObject, enqueueBuffer) {
|
|
|
68
68
|
auto audioBufferHostObject =
|
|
69
69
|
args[0].getObject(runtime).asHostObject<AudioBufferHostObject>(runtime);
|
|
70
70
|
// TODO: add optimized memory management for buffer changes, e.g.
|
|
71
|
-
//
|
|
71
|
+
// when the same buffer is reused across threads and
|
|
72
72
|
// buffer modification is not allowed on JS thread
|
|
73
|
-
|
|
73
|
+
|
|
74
|
+
auto swapBuffer = false; // whether to swap internal node buffer with the new buffer
|
|
75
|
+
if (!channelCountSet_) {
|
|
76
|
+
channelCount_ = static_cast<int>(audioBufferHostObject->audioBuffer_->getNumberOfChannels());
|
|
77
|
+
channelCountSet_ = true;
|
|
78
|
+
swapBuffer = true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// first buffer defines channel count, rest of them is mixed to channel count of the first buffer
|
|
82
|
+
auto copiedBuffer = std::make_shared<AudioBuffer>(
|
|
83
|
+
audioBufferHostObject->audioBuffer_->getSize(),
|
|
84
|
+
channelCount_,
|
|
85
|
+
audioBufferHostObject->audioBuffer_->getSampleRate());
|
|
86
|
+
|
|
87
|
+
copiedBuffer->sum(*audioBufferHostObject->audioBuffer_);
|
|
88
|
+
|
|
74
89
|
std::shared_ptr<AudioBuffer> tailBuffer = nullptr;
|
|
75
90
|
|
|
76
91
|
if (pitchCorrection_ && !stretchHasBeenInit_) {
|
|
@@ -84,8 +99,15 @@ JSI_HOST_FUNCTION_IMPL(AudioBufferQueueSourceNodeHostObject, enqueueBuffer) {
|
|
|
84
99
|
stretchHasBeenInit_ = true;
|
|
85
100
|
}
|
|
86
101
|
|
|
87
|
-
auto event = [audioBufferQueueSourceNode,
|
|
88
|
-
|
|
102
|
+
auto event = [audioBufferQueueSourceNode,
|
|
103
|
+
copiedBuffer,
|
|
104
|
+
bufferId = bufferId_,
|
|
105
|
+
tailBuffer,
|
|
106
|
+
swapBuffer,
|
|
107
|
+
channelCount = channelCount_](BaseAudioContext &) {
|
|
108
|
+
if (swapBuffer) {
|
|
109
|
+
audioBufferQueueSourceNode->setChannelCount(static_cast<int>(channelCount));
|
|
110
|
+
}
|
|
89
111
|
audioBufferQueueSourceNode->enqueueBuffer(copiedBuffer, bufferId, tailBuffer);
|
|
90
112
|
};
|
|
91
113
|
audioBufferQueueSourceNode->scheduleAudioEvent(std::move(event));
|
|
@@ -30,6 +30,7 @@ class AudioBufferQueueSourceNodeHostObject : public AudioBufferBaseSourceNodeHos
|
|
|
30
30
|
size_t bufferId_ = 0;
|
|
31
31
|
uint64_t onBufferEndedCallbackId_ = 0;
|
|
32
32
|
bool stretchHasBeenInit_ = false;
|
|
33
|
+
bool channelCountSet_ = false;
|
|
33
34
|
|
|
34
35
|
void setOnBufferEndedCallbackId(uint64_t callbackId);
|
|
35
36
|
};
|
|
@@ -14,8 +14,8 @@ AudioFileSourceNodeHostObject::AudioFileSourceNodeHostObject(
|
|
|
14
14
|
const AudioFileSourceOptions &options)
|
|
15
15
|
: AudioScheduledSourceNodeHostObject(context->createFileSource(options), options),
|
|
16
16
|
loop_(options.loop),
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
duration_(std::static_pointer_cast<AudioFileSourceNode>(node_)->getDuration()),
|
|
18
|
+
volume_(options.volume) {
|
|
19
19
|
addGetters(
|
|
20
20
|
JSI_EXPORT_PROPERTY_GETTER(AudioFileSourceNodeHostObject, volume),
|
|
21
21
|
JSI_EXPORT_PROPERTY_GETTER(AudioFileSourceNodeHostObject, loop),
|
|
@@ -28,7 +28,7 @@ JSI_HOST_FUNCTION_IMPL(AudioDecoderHostObject, decodeWithMemoryBlock) {
|
|
|
28
28
|
auto sampleRate = static_cast<float>(args[1].getNumber());
|
|
29
29
|
|
|
30
30
|
auto promise = promiseVendor_->createAsyncPromise([data, size, sampleRate]() -> PromiseResolver {
|
|
31
|
-
auto result =
|
|
31
|
+
auto result = audiodecoder::decodeWithMemoryBlock(data, size, sampleRate);
|
|
32
32
|
|
|
33
33
|
if (result.is_err()) {
|
|
34
34
|
return [result = std::move(result)](
|
|
@@ -54,7 +54,7 @@ JSI_HOST_FUNCTION_IMPL(AudioDecoderHostObject, decodeWithFilePath) {
|
|
|
54
54
|
auto sampleRate = static_cast<float>(args[1].getNumber());
|
|
55
55
|
|
|
56
56
|
auto promise = promiseVendor_->createAsyncPromise([sourcePath, sampleRate]() -> PromiseResolver {
|
|
57
|
-
auto result =
|
|
57
|
+
auto result = audiodecoder::decodeWithFilePath(sourcePath, sampleRate);
|
|
58
58
|
|
|
59
59
|
if (result.is_err()) {
|
|
60
60
|
return [result = std::move(result)](
|
|
@@ -84,7 +84,7 @@ JSI_HOST_FUNCTION_IMPL(AudioDecoderHostObject, decodeWithPCMInBase64) {
|
|
|
84
84
|
|
|
85
85
|
auto promise = promiseVendor_->createAsyncPromise(
|
|
86
86
|
[b64, inputSampleRate, inputChannelCount, interleaved]() -> PromiseResolver {
|
|
87
|
-
auto result =
|
|
87
|
+
auto result = audiodecoder::decodeWithPCMInBase64(
|
|
88
88
|
b64, inputSampleRate, inputChannelCount, interleaved);
|
|
89
89
|
|
|
90
90
|
if (result.is_err()) {
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#include <audioapi/HostObjects/utils/AudioFileUtilsHostObject.h>
|
|
2
|
+
#include <audioapi/core/utils/AudioFileConcatenator.h>
|
|
3
|
+
#include <audioapi/jsi/JsiPromise.h>
|
|
4
|
+
|
|
5
|
+
#include <jsi/jsi.h>
|
|
6
|
+
#include <memory>
|
|
7
|
+
#include <string>
|
|
8
|
+
#include <utility>
|
|
9
|
+
#include <vector>
|
|
10
|
+
|
|
11
|
+
namespace audioapi {
|
|
12
|
+
|
|
13
|
+
AudioFileUtilsHostObject::AudioFileUtilsHostObject(
|
|
14
|
+
jsi::Runtime *runtime,
|
|
15
|
+
const std::shared_ptr<react::CallInvoker> &callInvoker) {
|
|
16
|
+
promiseVendor_ = std::make_shared<PromiseVendor>(runtime, callInvoker);
|
|
17
|
+
addFunctions(JSI_EXPORT_FUNCTION(AudioFileUtilsHostObject, concatAudioFiles));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
JSI_HOST_FUNCTION_IMPL(AudioFileUtilsHostObject, concatAudioFiles) {
|
|
21
|
+
if (count < 2 || !args[0].isObject() || !args[1].isString()) {
|
|
22
|
+
throw jsi::JSError(runtime, "concatAudioFiles expects input paths and an output path.");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
auto inputPathArray = args[0].asObject(runtime).asArray(runtime);
|
|
26
|
+
const auto inputPathCount = inputPathArray.size(runtime);
|
|
27
|
+
std::vector<std::string> inputPaths;
|
|
28
|
+
inputPaths.reserve(inputPathCount);
|
|
29
|
+
|
|
30
|
+
for (size_t i = 0; i < inputPathCount; ++i) {
|
|
31
|
+
auto value = inputPathArray.getValueAtIndex(runtime, i);
|
|
32
|
+
if (!value.isString()) {
|
|
33
|
+
throw jsi::JSError(runtime, "concatAudioFiles input paths must be strings.");
|
|
34
|
+
}
|
|
35
|
+
inputPaths.push_back(value.asString(runtime).utf8(runtime));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
auto outputPath = args[1].asString(runtime).utf8(runtime);
|
|
39
|
+
|
|
40
|
+
auto promise = promiseVendor_->createAsyncPromise(
|
|
41
|
+
[inputPaths = std::move(inputPaths), outputPath]() -> PromiseResolver {
|
|
42
|
+
auto result = audioapi::concatAudioFiles(inputPaths, outputPath);
|
|
43
|
+
|
|
44
|
+
if (result.is_err()) {
|
|
45
|
+
return [result = std::move(result)](
|
|
46
|
+
jsi::Runtime &runtime) -> std::variant<jsi::Value, std::string> {
|
|
47
|
+
return result.unwrap_err();
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return [outputPath = std::move(outputPath)](
|
|
52
|
+
jsi::Runtime &runtime) -> std::variant<jsi::Value, std::string> {
|
|
53
|
+
return jsi::String::createFromUtf8(runtime, outputPath);
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return promise;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
} // namespace audioapi
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <audioapi/jsi/JsiHostObject.h>
|
|
4
|
+
#include <audioapi/jsi/JsiPromise.h>
|
|
5
|
+
|
|
6
|
+
#include <jsi/jsi.h>
|
|
7
|
+
#include <memory>
|
|
8
|
+
|
|
9
|
+
namespace audioapi {
|
|
10
|
+
using namespace facebook;
|
|
11
|
+
|
|
12
|
+
class AudioFileUtilsHostObject : public JsiHostObject {
|
|
13
|
+
public:
|
|
14
|
+
explicit AudioFileUtilsHostObject(
|
|
15
|
+
jsi::Runtime *runtime,
|
|
16
|
+
const std::shared_ptr<react::CallInvoker> &callInvoker);
|
|
17
|
+
|
|
18
|
+
JSI_HOST_FUNCTION_DECL(concatAudioFiles);
|
|
19
|
+
|
|
20
|
+
private:
|
|
21
|
+
std::shared_ptr<PromiseVendor> promiseVendor_;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
} // namespace audioapi
|
|
@@ -309,14 +309,14 @@ inline AudioFileSourceOptions parseAudioFileSourceOptions(
|
|
|
309
309
|
if (sourceValue.isString()) {
|
|
310
310
|
options.filePath = sourceValue.asString(runtime).utf8(runtime);
|
|
311
311
|
options.requiresFFmpeg =
|
|
312
|
-
|
|
312
|
+
audiodecoder::pathHasExtension(options.filePath, {".mp4", ".m4a", ".aac"});
|
|
313
313
|
} else if (sourceValue.isObject()) {
|
|
314
314
|
auto sourceObj = sourceValue.asObject(runtime);
|
|
315
315
|
if (sourceObj.isArrayBuffer(runtime)) {
|
|
316
316
|
auto arrayBuffer = sourceObj.getArrayBuffer(runtime);
|
|
317
317
|
auto *data = arrayBuffer.data(runtime);
|
|
318
318
|
auto size = arrayBuffer.size(runtime);
|
|
319
|
-
auto format =
|
|
319
|
+
auto format = audiodecoder::detectAudioFormat(data, size);
|
|
320
320
|
options.requiresFFmpeg =
|
|
321
321
|
format == AudioFormat::MP4 || format == AudioFormat::M4A || format == AudioFormat::AAC;
|
|
322
322
|
options.data = std::vector<uint8_t>(data, data + size);
|
|
@@ -23,7 +23,6 @@ OfflineAudioContext::OfflineAudioContext(
|
|
|
23
23
|
const RuntimeRegistry &runtimeRegistry)
|
|
24
24
|
: BaseAudioContext(sampleRate, audioEventHandlerRegistry, runtimeRegistry),
|
|
25
25
|
length_(length),
|
|
26
|
-
numberOfChannels_(numberOfChannels),
|
|
27
26
|
currentSampleFrame_(0),
|
|
28
27
|
audioBuffer_(
|
|
29
28
|
std::make_shared<DSPAudioBuffer>(RENDER_QUANTUM_SIZE, numberOfChannels, sampleRate)),
|
|
@@ -88,21 +88,15 @@ void ConvolverNode::onInputDisabled() {
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
std::shared_ptr<DSPAudioBuffer> ConvolverNode::processInputs(
|
|
92
|
-
const std::shared_ptr<DSPAudioBuffer> &outputBuffer,
|
|
93
|
-
int framesToProcess,
|
|
94
|
-
bool checkIsAlreadyProcessed) {
|
|
95
|
-
if (internalBufferIndex_ < framesToProcess) {
|
|
96
|
-
return AudioNode::processInputs(outputBuffer, RENDER_QUANTUM_SIZE, false);
|
|
97
|
-
}
|
|
98
|
-
return AudioNode::processInputs(outputBuffer, 0, false);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
91
|
// processing pipeline: processingBuffer -> intermediateBuffer_ -> audioBuffer_ (mixing
|
|
102
92
|
// with intermediateBuffer_)
|
|
103
93
|
std::shared_ptr<DSPAudioBuffer> ConvolverNode::processNode(
|
|
104
94
|
const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
|
|
105
95
|
int framesToProcess) {
|
|
96
|
+
if (processingBuffer->getSize() != RENDER_QUANTUM_SIZE) {
|
|
97
|
+
printf(
|
|
98
|
+
"[AUDIOAPI WARN] convolver requires 128 buffer size for each render quantum, otherwise quality of convolution is very poor\n");
|
|
99
|
+
}
|
|
106
100
|
if (signalledToStop_) {
|
|
107
101
|
if (remainingSegments_ > 0) {
|
|
108
102
|
remainingSegments_--;
|