react-native-audio-api 0.11.0-nightly-95f9c99-20251215 → 0.11.0-nightly-52d0b79-20251216
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/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +382 -39
- package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.h +45 -18
- package/android/src/main/cpp/audioapi/android/core/NativeAudioRecorder.hpp +9 -9
- package/android/src/main/cpp/audioapi/android/core/utils/AndroidFileWriterBackend.h +33 -0
- package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp +170 -0
- package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h +46 -0
- package/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp +0 -1
- package/android/src/main/cpp/audioapi/android/core/utils/FileOptions.cpp +83 -0
- package/android/src/main/cpp/audioapi/android/core/utils/FileOptions.h +22 -0
- package/android/src/main/cpp/audioapi/android/core/utils/MiniaudioImplementation.cpp +8 -0
- package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/FFmpegFileWriter.cpp +493 -0
- package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/FFmpegFileWriter.h +70 -0
- package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/ptrs.hpp +56 -0
- package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/utils.cpp +114 -0
- package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/utils.h +34 -0
- package/android/src/main/cpp/audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.cpp +296 -0
- package/android/src/main/cpp/audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.h +40 -0
- package/android/src/main/cpp/audioapi/android/system/NativeFileInfo.hpp +32 -0
- package/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt +2 -0
- package/android/src/main/java/com/swmansion/audioapi/system/AudioFocusListener.kt +7 -3
- package/android/src/main/java/com/swmansion/audioapi/system/CentralizedForegroundService.kt +1 -0
- package/android/src/main/java/com/swmansion/audioapi/system/NativeFileInfo.kt +18 -0
- package/android/src/main/java/com/swmansion/audioapi/system/notification/PlaybackNotification.kt +1 -0
- package/android/src/main/java/com/swmansion/audioapi/system/notification/RecordingNotificationReceiver.kt +2 -0
- package/common/cpp/audioapi/AudioAPIModuleInstaller.h +3 -11
- package/common/cpp/audioapi/HostObjects/inputs/AudioRecorderHostObject.cpp +145 -16
- package/common/cpp/audioapi/HostObjects/inputs/AudioRecorderHostObject.h +21 -6
- package/common/cpp/audioapi/core/inputs/AudioRecorder.cpp +43 -60
- package/common/cpp/audioapi/core/inputs/AudioRecorder.h +53 -33
- package/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp +42 -14
- package/common/cpp/audioapi/core/sources/RecorderAdapterNode.h +12 -9
- package/common/cpp/audioapi/core/utils/AudioFileWriter.cpp +41 -0
- package/common/cpp/audioapi/core/utils/AudioFileWriter.h +44 -0
- package/common/cpp/audioapi/core/utils/AudioRecorderCallback.cpp +101 -0
- package/common/cpp/audioapi/core/utils/AudioRecorderCallback.h +52 -0
- package/common/cpp/audioapi/utils/AudioFileProperties.cpp +92 -0
- package/common/cpp/audioapi/utils/AudioFileProperties.h +76 -0
- package/common/cpp/audioapi/utils/Result.hpp +323 -0
- package/common/cpp/audioapi/utils/UnitConversion.h +9 -0
- package/ios/audioapi/ios/AudioAPIModule.mm +9 -14
- package/ios/audioapi/ios/core/IOSAudioPlayer.h +1 -1
- package/ios/audioapi/ios/core/IOSAudioPlayer.mm +7 -6
- package/ios/audioapi/ios/core/IOSAudioRecorder.h +39 -13
- package/ios/audioapi/ios/core/IOSAudioRecorder.mm +302 -26
- package/ios/audioapi/ios/core/NativeAudioPlayer.m +7 -11
- package/ios/audioapi/ios/core/NativeAudioRecorder.h +8 -9
- package/ios/audioapi/ios/core/NativeAudioRecorder.m +70 -76
- package/ios/audioapi/ios/core/utils/AudioDecoder.mm +1 -0
- package/ios/audioapi/ios/core/utils/FileOptions.h +31 -0
- package/ios/audioapi/ios/core/utils/FileOptions.mm +195 -0
- package/ios/audioapi/ios/core/utils/IOSFileWriter.h +53 -0
- package/ios/audioapi/ios/core/utils/IOSFileWriter.mm +239 -0
- package/ios/audioapi/ios/core/utils/IOSRecorderCallback.h +47 -0
- package/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm +185 -0
- package/ios/audioapi/ios/system/AudioEngine.h +21 -16
- package/ios/audioapi/ios/system/AudioEngine.mm +138 -130
- package/ios/audioapi/ios/system/AudioSessionManager.h +19 -9
- package/ios/audioapi/ios/system/AudioSessionManager.mm +250 -215
- package/ios/audioapi/ios/system/NotificationManager.mm +24 -42
- package/lib/commonjs/api.js +82 -109
- package/lib/commonjs/api.js.map +1 -1
- package/lib/commonjs/core/AudioRecorder.js +159 -13
- package/lib/commonjs/core/AudioRecorder.js.map +1 -1
- package/lib/commonjs/specs/NativeAudioAPIModule.js.map +1 -1
- package/lib/commonjs/system/notification/PlaybackNotificationManager.js +17 -14
- package/lib/commonjs/system/notification/PlaybackNotificationManager.js.map +1 -1
- package/lib/commonjs/system/notification/RecordingNotificationManager.js +22 -19
- package/lib/commonjs/system/notification/RecordingNotificationManager.js.map +1 -1
- package/lib/commonjs/system/notification/SimpleNotificationManager.js +16 -13
- package/lib/commonjs/system/notification/SimpleNotificationManager.js.map +1 -1
- package/lib/commonjs/types.js +39 -0
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/utils/filePresets.js +43 -0
- package/lib/commonjs/utils/filePresets.js.map +1 -0
- package/lib/commonjs/web-system/notification/PlaybackNotificationManager.js +6 -3
- package/lib/commonjs/web-system/notification/PlaybackNotificationManager.js.map +1 -1
- package/lib/commonjs/web-system/notification/RecordingNotificationManager.js +6 -3
- package/lib/commonjs/web-system/notification/RecordingNotificationManager.js.map +1 -1
- package/lib/module/api.js +5 -4
- package/lib/module/api.js.map +1 -1
- package/lib/module/core/AudioRecorder.js +159 -13
- package/lib/module/core/AudioRecorder.js.map +1 -1
- package/lib/module/specs/NativeAudioAPIModule.js.map +1 -1
- package/lib/module/system/notification/PlaybackNotificationManager.js +17 -14
- package/lib/module/system/notification/PlaybackNotificationManager.js.map +1 -1
- package/lib/module/system/notification/RecordingNotificationManager.js +22 -19
- package/lib/module/system/notification/RecordingNotificationManager.js.map +1 -1
- package/lib/module/system/notification/SimpleNotificationManager.js +16 -13
- package/lib/module/system/notification/SimpleNotificationManager.js.map +1 -1
- package/lib/module/types.js +38 -1
- package/lib/module/types.js.map +1 -1
- package/lib/module/utils/filePresets.js +39 -0
- package/lib/module/utils/filePresets.js.map +1 -0
- package/lib/module/web-system/notification/PlaybackNotificationManager.js +6 -3
- package/lib/module/web-system/notification/PlaybackNotificationManager.js.map +1 -1
- package/lib/module/web-system/notification/RecordingNotificationManager.js +6 -3
- package/lib/module/web-system/notification/RecordingNotificationManager.js.map +1 -1
- package/lib/typescript/api.d.ts +5 -4
- package/lib/typescript/api.d.ts.map +1 -1
- package/lib/typescript/core/AudioRecorder.d.ts +69 -7
- package/lib/typescript/core/AudioRecorder.d.ts.map +1 -1
- package/lib/typescript/events/types.d.ts +36 -2
- package/lib/typescript/events/types.d.ts.map +1 -1
- package/lib/typescript/interfaces.d.ts +24 -4
- package/lib/typescript/interfaces.d.ts.map +1 -1
- package/lib/typescript/specs/NativeAudioAPIModule.d.ts +1 -1
- package/lib/typescript/specs/NativeAudioAPIModule.d.ts.map +1 -1
- package/lib/typescript/system/notification/PlaybackNotificationManager.d.ts +4 -3
- package/lib/typescript/system/notification/PlaybackNotificationManager.d.ts.map +1 -1
- package/lib/typescript/system/notification/RecordingNotificationManager.d.ts +4 -3
- package/lib/typescript/system/notification/RecordingNotificationManager.d.ts.map +1 -1
- package/lib/typescript/system/notification/SimpleNotificationManager.d.ts +3 -2
- package/lib/typescript/system/notification/SimpleNotificationManager.d.ts.map +1 -1
- package/lib/typescript/system/notification/types.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +79 -3
- package/lib/typescript/types.d.ts.map +1 -1
- package/lib/typescript/utils/filePresets.d.ts +9 -0
- package/lib/typescript/utils/filePresets.d.ts.map +1 -0
- package/lib/typescript/web-system/notification/PlaybackNotificationManager.d.ts +4 -3
- package/lib/typescript/web-system/notification/PlaybackNotificationManager.d.ts.map +1 -1
- package/lib/typescript/web-system/notification/RecordingNotificationManager.d.ts +4 -3
- package/lib/typescript/web-system/notification/RecordingNotificationManager.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/AudioAPIModule/globals.d.ts +1 -2
- package/src/api.ts +8 -29
- package/src/core/AudioRecorder.ts +195 -23
- package/src/events/types.ts +40 -2
- package/src/interfaces.ts +34 -5
- package/src/specs/NativeAudioAPIModule.ts +2 -2
- package/src/system/notification/PlaybackNotificationManager.ts +20 -16
- package/src/system/notification/RecordingNotificationManager.ts +26 -21
- package/src/system/notification/SimpleNotificationManager.ts +18 -13
- package/src/system/notification/types.ts +1 -0
- package/src/types.ts +89 -3
- package/src/utils/filePresets.ts +47 -0
- package/src/web-system/notification/PlaybackNotificationManager.ts +9 -5
- package/src/web-system/notification/RecordingNotificationManager.ts +9 -5
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
#include <audioapi/core/inputs/AudioRecorder.h>
|
|
6
6
|
#include <audioapi/core/sources/AudioBuffer.h>
|
|
7
7
|
#include <audioapi/events/AudioEventHandlerRegistry.h>
|
|
8
|
+
#include <audioapi/utils/AudioFileProperties.h>
|
|
8
9
|
#ifdef ANDROID
|
|
9
10
|
#include <audioapi/android/core/AndroidAudioRecorder.h>
|
|
10
11
|
#else
|
|
@@ -15,33 +16,121 @@
|
|
|
15
16
|
namespace audioapi {
|
|
16
17
|
|
|
17
18
|
AudioRecorderHostObject::AudioRecorderHostObject(
|
|
18
|
-
const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry
|
|
19
|
-
float sampleRate,
|
|
20
|
-
int bufferLength) {
|
|
19
|
+
const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry) {
|
|
21
20
|
#ifdef ANDROID
|
|
22
|
-
audioRecorder_ =
|
|
23
|
-
std::make_shared<AndroidAudioRecorder>(sampleRate, bufferLength, audioEventHandlerRegistry);
|
|
21
|
+
audioRecorder_ = std::make_shared<AndroidAudioRecorder>(audioEventHandlerRegistry);
|
|
24
22
|
#else
|
|
25
|
-
audioRecorder_ =
|
|
26
|
-
std::make_shared<IOSAudioRecorder>(sampleRate, bufferLength, audioEventHandlerRegistry);
|
|
23
|
+
audioRecorder_ = std::make_shared<IOSAudioRecorder>(audioEventHandlerRegistry);
|
|
27
24
|
#endif
|
|
28
25
|
|
|
29
|
-
addSetters(JSI_EXPORT_PROPERTY_SETTER(AudioRecorderHostObject, onAudioReady));
|
|
30
|
-
|
|
31
26
|
addFunctions(
|
|
32
27
|
JSI_EXPORT_FUNCTION(AudioRecorderHostObject, start),
|
|
33
28
|
JSI_EXPORT_FUNCTION(AudioRecorderHostObject, stop),
|
|
29
|
+
JSI_EXPORT_FUNCTION(AudioRecorderHostObject, isRecording),
|
|
30
|
+
JSI_EXPORT_FUNCTION(AudioRecorderHostObject, enableFileOutput),
|
|
31
|
+
JSI_EXPORT_FUNCTION(AudioRecorderHostObject, disableFileOutput),
|
|
32
|
+
JSI_EXPORT_FUNCTION(AudioRecorderHostObject, pause),
|
|
33
|
+
JSI_EXPORT_FUNCTION(AudioRecorderHostObject, resume),
|
|
34
34
|
JSI_EXPORT_FUNCTION(AudioRecorderHostObject, connect),
|
|
35
|
-
JSI_EXPORT_FUNCTION(AudioRecorderHostObject, disconnect)
|
|
35
|
+
JSI_EXPORT_FUNCTION(AudioRecorderHostObject, disconnect),
|
|
36
|
+
JSI_EXPORT_FUNCTION(AudioRecorderHostObject, setOnAudioReady),
|
|
37
|
+
JSI_EXPORT_FUNCTION(AudioRecorderHostObject, clearOnAudioReady),
|
|
38
|
+
JSI_EXPORT_FUNCTION(AudioRecorderHostObject, setOnError),
|
|
39
|
+
JSI_EXPORT_FUNCTION(AudioRecorderHostObject, clearOnError),
|
|
40
|
+
JSI_EXPORT_FUNCTION(AudioRecorderHostObject, getCurrentDuration));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
JSI_HOST_FUNCTION_IMPL(AudioRecorderHostObject, start) {
|
|
44
|
+
auto result = audioRecorder_->start();
|
|
45
|
+
auto jsResult = jsi::Object(runtime);
|
|
46
|
+
|
|
47
|
+
jsResult.setProperty(
|
|
48
|
+
runtime,
|
|
49
|
+
"status",
|
|
50
|
+
jsi::String::createFromUtf8(runtime, result.is_ok() ? "success" : "error"));
|
|
51
|
+
|
|
52
|
+
if (result.is_ok()) {
|
|
53
|
+
jsResult.setProperty(runtime, "path", jsi::String::createFromUtf8(runtime, result.unwrap()));
|
|
54
|
+
} else {
|
|
55
|
+
jsResult.setProperty(
|
|
56
|
+
runtime, "message", jsi::String::createFromUtf8(runtime, result.unwrap_err()));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return jsResult;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
JSI_HOST_FUNCTION_IMPL(AudioRecorderHostObject, stop) {
|
|
63
|
+
auto result = audioRecorder_->stop();
|
|
64
|
+
auto jsResult = jsi::Object(runtime);
|
|
65
|
+
|
|
66
|
+
jsResult.setProperty(
|
|
67
|
+
runtime,
|
|
68
|
+
"status",
|
|
69
|
+
jsi::String::createFromUtf8(runtime, result.is_ok() ? "success" : "error"));
|
|
70
|
+
|
|
71
|
+
if (result.is_ok()) {
|
|
72
|
+
auto info = result.unwrap();
|
|
73
|
+
|
|
74
|
+
jsResult.setProperty(runtime, "path", jsi::String::createFromUtf8(runtime, std::get<0>(info)));
|
|
75
|
+
jsResult.setProperty(runtime, "size", std::get<1>(info));
|
|
76
|
+
jsResult.setProperty(runtime, "duration", std::get<2>(info));
|
|
77
|
+
} else {
|
|
78
|
+
jsResult.setProperty(
|
|
79
|
+
runtime, "message", jsi::String::createFromUtf8(runtime, result.unwrap_err()));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return jsResult;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
JSI_HOST_FUNCTION_IMPL(AudioRecorderHostObject, isRecording) {
|
|
86
|
+
return jsi::Value(audioRecorder_->isRecording());
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
JSI_HOST_FUNCTION_IMPL(AudioRecorderHostObject, isPaused) {
|
|
90
|
+
return jsi::Value(audioRecorder_->isPaused());
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
JSI_HOST_FUNCTION_IMPL(AudioRecorderHostObject, enableFileOutput) {
|
|
94
|
+
auto fileProperties = AudioFileProperties::CreateFromJSIValue(runtime, args[0]);
|
|
95
|
+
|
|
96
|
+
auto result = audioRecorder_->enableFileOutput(fileProperties);
|
|
97
|
+
auto jsResult = jsi::Object(runtime);
|
|
98
|
+
|
|
99
|
+
jsResult.setProperty(
|
|
100
|
+
runtime,
|
|
101
|
+
"status",
|
|
102
|
+
jsi::String::createFromUtf8(runtime, result.is_ok() ? "success" : "error"));
|
|
103
|
+
|
|
104
|
+
if (result.is_ok()) {
|
|
105
|
+
jsResult.setProperty(runtime, "path", jsi::String::createFromUtf8(runtime, result.unwrap()));
|
|
106
|
+
} else {
|
|
107
|
+
jsResult.setProperty(
|
|
108
|
+
runtime, "message", jsi::String::createFromUtf8(runtime, result.unwrap_err()));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return jsResult;
|
|
36
112
|
}
|
|
37
113
|
|
|
38
|
-
|
|
39
|
-
audioRecorder_->
|
|
114
|
+
JSI_HOST_FUNCTION_IMPL(AudioRecorderHostObject, disableFileOutput) {
|
|
115
|
+
audioRecorder_->disableFileOutput();
|
|
116
|
+
return jsi::Value::undefined();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
JSI_HOST_FUNCTION_IMPL(AudioRecorderHostObject, pause) {
|
|
120
|
+
audioRecorder_->pause();
|
|
121
|
+
|
|
122
|
+
return jsi::Value::undefined();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
JSI_HOST_FUNCTION_IMPL(AudioRecorderHostObject, resume) {
|
|
126
|
+
audioRecorder_->resume();
|
|
127
|
+
return jsi::Value::undefined();
|
|
40
128
|
}
|
|
41
129
|
|
|
42
130
|
JSI_HOST_FUNCTION_IMPL(AudioRecorderHostObject, connect) {
|
|
43
131
|
auto adapterNodeHostObject =
|
|
44
132
|
args[0].getObject(runtime).getHostObject<RecorderAdapterNodeHostObject>(runtime);
|
|
133
|
+
|
|
45
134
|
audioRecorder_->connect(
|
|
46
135
|
std::static_pointer_cast<RecorderAdapterNode>(adapterNodeHostObject->node_));
|
|
47
136
|
return jsi::Value::undefined();
|
|
@@ -49,19 +138,59 @@ JSI_HOST_FUNCTION_IMPL(AudioRecorderHostObject, connect) {
|
|
|
49
138
|
|
|
50
139
|
JSI_HOST_FUNCTION_IMPL(AudioRecorderHostObject, disconnect) {
|
|
51
140
|
audioRecorder_->disconnect();
|
|
141
|
+
|
|
52
142
|
return jsi::Value::undefined();
|
|
53
143
|
}
|
|
54
144
|
|
|
55
|
-
JSI_HOST_FUNCTION_IMPL(AudioRecorderHostObject,
|
|
56
|
-
|
|
145
|
+
JSI_HOST_FUNCTION_IMPL(AudioRecorderHostObject, setOnAudioReady) {
|
|
146
|
+
auto options = args[0].getObject(runtime);
|
|
147
|
+
|
|
148
|
+
auto sampleRate = static_cast<float>(options.getProperty(runtime, "sampleRate").getNumber());
|
|
149
|
+
auto bufferLength = static_cast<size_t>(options.getProperty(runtime, "bufferLength").getNumber());
|
|
150
|
+
auto channelCount = static_cast<int>(options.getProperty(runtime, "channelCount").getNumber());
|
|
151
|
+
uint64_t callbackId =
|
|
152
|
+
std::stoull(options.getProperty(runtime, "callbackId").getString(runtime).utf8(runtime));
|
|
57
153
|
|
|
154
|
+
auto result =
|
|
155
|
+
audioRecorder_->setOnAudioReadyCallback(sampleRate, bufferLength, channelCount, callbackId);
|
|
156
|
+
auto jsResult = jsi::Object(runtime);
|
|
157
|
+
|
|
158
|
+
jsResult.setProperty(
|
|
159
|
+
runtime,
|
|
160
|
+
"status",
|
|
161
|
+
jsi::String::createFromUtf8(runtime, result.is_ok() ? "success" : "error"));
|
|
162
|
+
|
|
163
|
+
if (result.is_err()) {
|
|
164
|
+
jsResult.setProperty(
|
|
165
|
+
runtime, "message", jsi::String::createFromUtf8(runtime, result.unwrap_err()));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return jsResult;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
JSI_HOST_FUNCTION_IMPL(AudioRecorderHostObject, clearOnAudioReady) {
|
|
172
|
+
audioRecorder_->clearOnAudioReadyCallback();
|
|
58
173
|
return jsi::Value::undefined();
|
|
59
174
|
}
|
|
60
175
|
|
|
61
|
-
JSI_HOST_FUNCTION_IMPL(AudioRecorderHostObject,
|
|
62
|
-
|
|
176
|
+
JSI_HOST_FUNCTION_IMPL(AudioRecorderHostObject, setOnError) {
|
|
177
|
+
auto options = args[0].getObject(runtime);
|
|
63
178
|
|
|
179
|
+
uint64_t callbackId =
|
|
180
|
+
std::stoull(options.getProperty(runtime, "callbackId").getString(runtime).utf8(runtime));
|
|
181
|
+
|
|
182
|
+
audioRecorder_->setOnErrorCallback(callbackId);
|
|
183
|
+
return jsi::Value::undefined();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
JSI_HOST_FUNCTION_IMPL(AudioRecorderHostObject, clearOnError) {
|
|
187
|
+
audioRecorder_->clearOnErrorCallback();
|
|
64
188
|
return jsi::Value::undefined();
|
|
65
189
|
}
|
|
66
190
|
|
|
191
|
+
JSI_HOST_FUNCTION_IMPL(AudioRecorderHostObject, getCurrentDuration) {
|
|
192
|
+
double duration = audioRecorder_->getCurrentDuration();
|
|
193
|
+
return jsi::Value(duration);
|
|
194
|
+
}
|
|
195
|
+
|
|
67
196
|
} // namespace audioapi
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
#include <cstdio>
|
|
6
6
|
#include <memory>
|
|
7
|
+
#include <string>
|
|
7
8
|
#include <utility>
|
|
8
9
|
#include <vector>
|
|
9
10
|
|
|
@@ -16,18 +17,32 @@ class AudioEventHandlerRegistry;
|
|
|
16
17
|
class AudioRecorderHostObject : public JsiHostObject {
|
|
17
18
|
public:
|
|
18
19
|
explicit AudioRecorderHostObject(
|
|
19
|
-
const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry
|
|
20
|
-
float sampleRate,
|
|
21
|
-
int bufferLength);
|
|
20
|
+
const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry);
|
|
22
21
|
|
|
23
|
-
|
|
22
|
+
JSI_HOST_FUNCTION_DECL(start);
|
|
23
|
+
JSI_HOST_FUNCTION_DECL(stop);
|
|
24
|
+
JSI_HOST_FUNCTION_DECL(isRecording);
|
|
25
|
+
JSI_HOST_FUNCTION_DECL(isPaused);
|
|
26
|
+
|
|
27
|
+
JSI_HOST_FUNCTION_DECL(enableFileOutput);
|
|
28
|
+
JSI_HOST_FUNCTION_DECL(disableFileOutput);
|
|
29
|
+
|
|
30
|
+
JSI_HOST_FUNCTION_DECL(pause);
|
|
31
|
+
JSI_HOST_FUNCTION_DECL(resume);
|
|
24
32
|
|
|
25
33
|
JSI_HOST_FUNCTION_DECL(connect);
|
|
26
34
|
JSI_HOST_FUNCTION_DECL(disconnect);
|
|
27
|
-
|
|
28
|
-
JSI_HOST_FUNCTION_DECL(
|
|
35
|
+
|
|
36
|
+
JSI_HOST_FUNCTION_DECL(setOnAudioReady);
|
|
37
|
+
JSI_HOST_FUNCTION_DECL(clearOnAudioReady);
|
|
38
|
+
|
|
39
|
+
JSI_HOST_FUNCTION_DECL(setOnError);
|
|
40
|
+
JSI_HOST_FUNCTION_DECL(clearOnError);
|
|
41
|
+
|
|
42
|
+
JSI_HOST_FUNCTION_DECL(getCurrentDuration);
|
|
29
43
|
|
|
30
44
|
private:
|
|
31
45
|
std::shared_ptr<AudioRecorder> audioRecorder_;
|
|
32
46
|
};
|
|
47
|
+
|
|
33
48
|
} // namespace audioapi
|
|
@@ -1,83 +1,66 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
2
|
#include <audioapi/core/inputs/AudioRecorder.h>
|
|
3
|
-
#include <audioapi/core/
|
|
4
|
-
#include <audioapi/core/
|
|
5
|
-
#include <audioapi/events/AudioEventHandlerRegistry.h>
|
|
6
|
-
#include <audioapi/utils/AudioBus.h>
|
|
7
|
-
#include <audioapi/utils/CircularAudioArray.h>
|
|
8
|
-
#include <audioapi/utils/CircularOverflowableAudioArray.h>
|
|
9
|
-
#include <algorithm>
|
|
10
|
-
#include <memory>
|
|
11
|
-
#include <string>
|
|
12
|
-
#include <unordered_map>
|
|
3
|
+
#include <audioapi/core/utils/AudioFileWriter.h>
|
|
4
|
+
#include <audioapi/core/utils/AudioRecorderCallback.h>
|
|
13
5
|
|
|
14
6
|
namespace audioapi {
|
|
15
7
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
8
|
+
/// @brief Sets the error callback to be invoked when an error occurs during recording.
|
|
9
|
+
/// This method should be called from the JS thread only.
|
|
10
|
+
/// @param callbackId Identifier for the JS callback to be invoked.
|
|
11
|
+
void AudioRecorder::setOnErrorCallback(uint64_t callbackId) {
|
|
12
|
+
std::scoped_lock lock(callbackMutex_, fileWriterMutex_, errorCallbackMutex_);
|
|
13
|
+
|
|
14
|
+
if (usesFileOutput()) {
|
|
15
|
+
fileWriter_->setOnErrorCallback(callbackId);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (usesCallback()) {
|
|
19
|
+
dataCallback_->setOnErrorCallback(callbackId);
|
|
20
|
+
}
|
|
28
21
|
|
|
29
|
-
|
|
30
|
-
onAudioReadyCallbackId_ = callbackId;
|
|
22
|
+
errorCallbackId_.store(callbackId, std::memory_order_release);
|
|
31
23
|
}
|
|
32
24
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
25
|
+
/// @brief Clears the error callback.
|
|
26
|
+
/// If the recorder is currently active, it will stop invoking the callback immediately.
|
|
27
|
+
/// This method should be called from the JS thread only.
|
|
28
|
+
void AudioRecorder::clearOnErrorCallback() {
|
|
29
|
+
std::scoped_lock lock(callbackMutex_, fileWriterMutex_, errorCallbackMutex_);
|
|
38
30
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
31
|
+
if (usesFileOutput()) {
|
|
32
|
+
fileWriter_->clearOnErrorCallback();
|
|
33
|
+
}
|
|
42
34
|
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
"audioReady", onAudioReadyCallbackId_, body);
|
|
35
|
+
if (usesCallback()) {
|
|
36
|
+
dataCallback_->clearOnErrorCallback();
|
|
46
37
|
}
|
|
38
|
+
|
|
39
|
+
errorCallbackId_.store(0, std::memory_order_release);
|
|
47
40
|
}
|
|
48
41
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
auto availableFrames = static_cast<int>(circularBuffer_->getNumberOfAvailableFrames());
|
|
42
|
+
/// @brief Gets the current duration of the recorded audio in seconds.
|
|
43
|
+
/// @returns Duration in seconds.
|
|
44
|
+
double AudioRecorder::getCurrentDuration() const {
|
|
45
|
+
double duration = 0.0;
|
|
54
46
|
|
|
55
|
-
|
|
47
|
+
if (usesFileOutput()) {
|
|
48
|
+
duration = fileWriter_->getCurrentDuration();
|
|
49
|
+
}
|
|
56
50
|
|
|
57
|
-
|
|
51
|
+
return duration;
|
|
58
52
|
}
|
|
59
53
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
adapterNodeLock_.lock();
|
|
63
|
-
adapterNode_ = node;
|
|
64
|
-
adapterNodeLock_.unlock();
|
|
54
|
+
bool AudioRecorder::usesCallback() const {
|
|
55
|
+
return callbackOutputEnabled_.load(std::memory_order_acquire);
|
|
65
56
|
}
|
|
66
57
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
adapterNode_ = nullptr;
|
|
70
|
-
adapterNodeLock_.unlock();
|
|
58
|
+
bool AudioRecorder::usesFileOutput() const {
|
|
59
|
+
return fileOutputEnabled_.load(std::memory_order_acquire);
|
|
71
60
|
}
|
|
72
61
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (adapterNode_ != nullptr) {
|
|
76
|
-
adapterNode_->buff_->write(data, numFrames);
|
|
77
|
-
}
|
|
78
|
-
adapterNodeLock_.unlock();
|
|
79
|
-
}
|
|
80
|
-
circularBuffer_->push_back(data, numFrames);
|
|
62
|
+
bool AudioRecorder::isConnected() const {
|
|
63
|
+
return isConnected_.load(std::memory_order_acquire);
|
|
81
64
|
}
|
|
82
65
|
|
|
83
66
|
} // namespace audioapi
|
|
@@ -1,62 +1,82 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
+
#include <audioapi/utils/Result.hpp>
|
|
3
4
|
#include <atomic>
|
|
4
5
|
#include <memory>
|
|
5
6
|
#include <mutex>
|
|
7
|
+
#include <string>
|
|
8
|
+
#include <tuple>
|
|
6
9
|
|
|
7
10
|
namespace audioapi {
|
|
8
11
|
|
|
9
|
-
class RecorderAdapterNode;
|
|
10
12
|
class AudioBus;
|
|
13
|
+
class AudioFileWriter;
|
|
11
14
|
class CircularAudioArray;
|
|
12
|
-
class
|
|
15
|
+
class RecorderAdapterNode;
|
|
16
|
+
class AudioFileProperties;
|
|
17
|
+
class AudioRecorderCallback;
|
|
13
18
|
class AudioEventHandlerRegistry;
|
|
14
19
|
|
|
15
20
|
class AudioRecorder {
|
|
16
21
|
public:
|
|
22
|
+
enum class RecorderState { Idle = 0, Recording, Paused };
|
|
17
23
|
explicit AudioRecorder(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry);
|
|
21
|
-
|
|
24
|
+
const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry)
|
|
25
|
+
: audioEventHandlerRegistry_(audioEventHandlerRegistry) {}
|
|
22
26
|
virtual ~AudioRecorder() = default;
|
|
23
27
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
virtual Result<std::string, std::string> start() = 0;
|
|
29
|
+
virtual Result<std::tuple<std::string, double, double>, std::string> stop() = 0;
|
|
30
|
+
|
|
31
|
+
virtual Result<std::string, std::string> enableFileOutput(
|
|
32
|
+
std::shared_ptr<AudioFileProperties> properties) = 0;
|
|
33
|
+
virtual void disableFileOutput() = 0;
|
|
34
|
+
|
|
35
|
+
virtual void pause() = 0;
|
|
36
|
+
virtual void resume() = 0;
|
|
37
|
+
|
|
38
|
+
virtual void connect(const std::shared_ptr<RecorderAdapterNode> &node) = 0;
|
|
39
|
+
virtual void disconnect() = 0;
|
|
27
40
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
void
|
|
41
|
+
virtual Result<NoneType, std::string> setOnAudioReadyCallback(
|
|
42
|
+
float sampleRate,
|
|
43
|
+
size_t bufferLength,
|
|
44
|
+
int channelCount,
|
|
45
|
+
uint64_t callbackId) = 0;
|
|
46
|
+
virtual void clearOnAudioReadyCallback() = 0;
|
|
47
|
+
|
|
48
|
+
void setOnErrorCallback(uint64_t callbackId);
|
|
49
|
+
void clearOnErrorCallback();
|
|
34
50
|
|
|
35
|
-
|
|
36
|
-
/// # Disconnects the recorder from the adapter node.
|
|
37
|
-
///
|
|
38
|
-
/// The adapter node will no longer be used to read audio data from the recorder.
|
|
39
|
-
/// @note Last few frames of audio might be written to the buffer after disconnecting.
|
|
40
|
-
void disconnect();
|
|
51
|
+
virtual double getCurrentDuration() const;
|
|
41
52
|
|
|
42
|
-
|
|
43
|
-
|
|
53
|
+
bool usesCallback() const;
|
|
54
|
+
bool usesFileOutput() const;
|
|
55
|
+
bool isConnected() const;
|
|
56
|
+
|
|
57
|
+
virtual bool isRecording() const = 0;
|
|
58
|
+
virtual bool isPaused() const = 0;
|
|
59
|
+
virtual bool isIdle() const = 0;
|
|
44
60
|
|
|
45
61
|
protected:
|
|
46
|
-
|
|
47
|
-
int bufferLength_;
|
|
48
|
-
size_t ringBufferSize_;
|
|
62
|
+
std::atomic<RecorderState> state_{RecorderState::Idle};
|
|
49
63
|
|
|
50
|
-
std::atomic<bool>
|
|
51
|
-
std::
|
|
64
|
+
std::atomic<bool> isConnected_{false};
|
|
65
|
+
std::atomic<bool> fileOutputEnabled_{false};
|
|
66
|
+
std::atomic<bool> callbackOutputEnabled_{false};
|
|
52
67
|
|
|
53
|
-
|
|
54
|
-
std::
|
|
68
|
+
std::mutex callbackMutex_;
|
|
69
|
+
std::mutex fileWriterMutex_;
|
|
70
|
+
std::mutex errorCallbackMutex_;
|
|
71
|
+
mutable std::mutex adapterNodeMutex_;
|
|
55
72
|
|
|
56
|
-
std::
|
|
57
|
-
uint64_t onAudioReadyCallbackId_ = 0;
|
|
73
|
+
std::atomic<uint64_t> errorCallbackId_{0};
|
|
58
74
|
|
|
59
|
-
|
|
75
|
+
std::string filePath_{""};
|
|
76
|
+
std::shared_ptr<AudioFileWriter> fileWriter_ = nullptr;
|
|
77
|
+
std::shared_ptr<RecorderAdapterNode> adapterNode_ = nullptr;
|
|
78
|
+
std::shared_ptr<AudioRecorderCallback> dataCallback_ = nullptr;
|
|
79
|
+
std::shared_ptr<AudioEventHandlerRegistry> audioEventHandlerRegistry_;
|
|
60
80
|
};
|
|
61
81
|
|
|
62
82
|
} // namespace audioapi
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
#include <audioapi/core/sources/RecorderAdapterNode.h>
|
|
3
|
+
#include <audioapi/core/types/ChannelInterpretation.h>
|
|
4
|
+
#include <audioapi/core/utils/Constants.h>
|
|
3
5
|
#include <audioapi/utils/AudioArray.h>
|
|
4
6
|
#include <audioapi/utils/AudioBus.h>
|
|
5
7
|
#include <memory>
|
|
@@ -15,34 +17,60 @@ RecorderAdapterNode::RecorderAdapterNode(BaseAudioContext *context) noexcept(
|
|
|
15
17
|
isInitialized_ = false;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
void RecorderAdapterNode::init(size_t bufferSize) {
|
|
20
|
+
void RecorderAdapterNode::init(size_t bufferSize, int channelCount) {
|
|
19
21
|
if (isInitialized_) {
|
|
20
|
-
|
|
21
|
-
"RecorderAdapterNode should not be initialized more than once. Just create a new instance.");
|
|
22
|
+
return;
|
|
22
23
|
}
|
|
24
|
+
|
|
25
|
+
channelCount_ = channelCount;
|
|
26
|
+
|
|
27
|
+
buff_.resize(channelCount_);
|
|
28
|
+
|
|
29
|
+
for (size_t i = 0; i < channelCount_; ++i) {
|
|
30
|
+
buff_[i] = std::make_shared<CircularOverflowableAudioArray>(bufferSize);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// This assumes that the sample rate is the same in audio context and recorder.
|
|
34
|
+
// (recorder is not enforcing any sample rate on the system*). This means that only
|
|
35
|
+
// channel mixing might be required. To do so, we create an output bus with
|
|
36
|
+
// the desired channel count and will take advantage of the AudioBus sum method.
|
|
37
|
+
//
|
|
38
|
+
// * any allocations required by the recorder (including this method) are during recording start
|
|
39
|
+
// or after, which means that audio context has already setup the system in 99% of sane cases.
|
|
40
|
+
// But if we would like to support cases when context is created on the fly during recording,
|
|
41
|
+
// we would need to add sample rate conversion as well or other weird bullshit like resampling
|
|
42
|
+
// context output and not enforcing anything on the system output/input configuration.
|
|
43
|
+
// A lot of words for a couple of lines of implementation :shrug:
|
|
44
|
+
adapterOutputBus_ =
|
|
45
|
+
std::make_shared<AudioBus>(RENDER_QUANTUM_SIZE, channelCount_, context_->getSampleRate());
|
|
23
46
|
isInitialized_ = true;
|
|
24
|
-
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
void RecorderAdapterNode::cleanup() {
|
|
50
|
+
isInitialized_ = false;
|
|
51
|
+
buff_.clear();
|
|
52
|
+
adapterOutputBus_.reset();
|
|
25
53
|
}
|
|
26
54
|
|
|
27
55
|
std::shared_ptr<AudioBus> RecorderAdapterNode::processNode(
|
|
28
56
|
const std::shared_ptr<AudioBus> &processingBus,
|
|
29
57
|
int framesToProcess) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
for (int i = 1; i < processingBus->getNumberOfChannels(); i++) {
|
|
34
|
-
processingBus->getChannel(i)->copy(processingBus->getChannel(0), 0, framesToProcess);
|
|
58
|
+
if (!isInitialized_) {
|
|
59
|
+
processingBus->zero();
|
|
60
|
+
return processingBus;
|
|
35
61
|
}
|
|
36
62
|
|
|
63
|
+
readFrames(framesToProcess);
|
|
64
|
+
|
|
65
|
+
processingBus->sum(adapterOutputBus_.get(), ChannelInterpretation::SPEAKERS);
|
|
37
66
|
return processingBus;
|
|
38
67
|
}
|
|
39
68
|
|
|
40
|
-
void RecorderAdapterNode::readFrames(
|
|
41
|
-
|
|
69
|
+
void RecorderAdapterNode::readFrames(const size_t framesToRead) {
|
|
70
|
+
adapterOutputBus_->zero();
|
|
42
71
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
std::memset(output + readFrames, 0, (framesToRead - readFrames) * sizeof(float));
|
|
72
|
+
for (size_t channel = 0; channel < channelCount_; ++channel) {
|
|
73
|
+
buff_[channel]->read(adapterOutputBus_->getChannel(channel)->getData(), framesToRead);
|
|
46
74
|
}
|
|
47
75
|
}
|
|
48
76
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
#include <audioapi/core/inputs/AudioRecorder.h>
|
|
7
7
|
#include <audioapi/utils/CircularOverflowableAudioArray.h>
|
|
8
8
|
#include <memory>
|
|
9
|
+
#include <vector>
|
|
9
10
|
|
|
10
11
|
namespace audioapi {
|
|
11
12
|
|
|
@@ -20,26 +21,28 @@ class RecorderAdapterNode : public AudioNode {
|
|
|
20
21
|
public:
|
|
21
22
|
explicit RecorderAdapterNode(BaseAudioContext *context);
|
|
22
23
|
|
|
23
|
-
/// @brief Initialize the RecorderAdapterNode with a buffer size.
|
|
24
|
+
/// @brief Initialize the RecorderAdapterNode with a buffer size and channel count.
|
|
24
25
|
/// @note This method should be called ONLY ONCE when the buffer size is known.
|
|
25
|
-
/// @throws std::runtime_error if the node is already initialized.
|
|
26
26
|
/// @param bufferSize The size of the buffer to be used.
|
|
27
|
-
|
|
27
|
+
/// @param channelCount The number of channels.
|
|
28
|
+
void init(size_t bufferSize, int channelCount);
|
|
29
|
+
void cleanup();
|
|
30
|
+
|
|
31
|
+
int channelCount_;
|
|
32
|
+
// TODO: CircularOverflowableAudioBus
|
|
33
|
+
std::vector<std::shared_ptr<CircularOverflowableAudioArray>> buff_;
|
|
28
34
|
|
|
29
35
|
protected:
|
|
30
36
|
std::shared_ptr<AudioBus> processNode(
|
|
31
37
|
const std::shared_ptr<AudioBus> &processingBus,
|
|
32
38
|
int framesToProcess) override;
|
|
33
|
-
std::shared_ptr<
|
|
39
|
+
std::shared_ptr<AudioBus> adapterOutputBus_;
|
|
34
40
|
|
|
35
41
|
private:
|
|
36
|
-
/// @brief Read audio frames from the recorder's internal
|
|
42
|
+
/// @brief Read audio frames from the recorder's internal circular buffer into output buss.
|
|
37
43
|
/// @note If `framesToRead` is greater than the number of available frames, it will fill empty space with silence.
|
|
38
|
-
/// @param output Pointer to the output buffer.
|
|
39
44
|
/// @param framesToRead Number of frames to read.
|
|
40
|
-
void readFrames(
|
|
41
|
-
|
|
42
|
-
friend class AudioRecorder;
|
|
45
|
+
void readFrames(size_t framesToRead);
|
|
43
46
|
};
|
|
44
47
|
|
|
45
48
|
} // namespace audioapi
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#include <audioapi/core/utils/AudioFileWriter.h>
|
|
2
|
+
#include <audioapi/events/AudioEventHandlerRegistry.h>
|
|
3
|
+
#include <memory>
|
|
4
|
+
#include <string>
|
|
5
|
+
#include <unordered_map>
|
|
6
|
+
|
|
7
|
+
namespace audioapi {
|
|
8
|
+
|
|
9
|
+
AudioFileWriter::AudioFileWriter(
|
|
10
|
+
const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry,
|
|
11
|
+
const std::shared_ptr<AudioFileProperties> &fileProperties)
|
|
12
|
+
: audioEventHandlerRegistry_(audioEventHandlerRegistry), fileProperties_(fileProperties) {}
|
|
13
|
+
|
|
14
|
+
void AudioFileWriter::setOnErrorCallback(uint64_t callbackId) {
|
|
15
|
+
errorCallbackId_.store(callbackId, std::memory_order_release);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
void AudioFileWriter::clearOnErrorCallback() {
|
|
19
|
+
errorCallbackId_.store(0, std::memory_order_release);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
void AudioFileWriter::invokeOnErrorCallback(const std::string &message) {
|
|
23
|
+
uint64_t callbackId = errorCallbackId_.load(std::memory_order_acquire);
|
|
24
|
+
|
|
25
|
+
// TODO: only the line above is atomic, which means that between reading the callbackId and invoking the callback,
|
|
26
|
+
// the callback could be cleared. We need to ensure that the callback is still valid when invoking it.
|
|
27
|
+
// TL;DR: atomic szpont
|
|
28
|
+
if (audioEventHandlerRegistry_ == nullptr || callbackId == 0) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
std::unordered_map<std::string, EventValue> eventPayload = {};
|
|
33
|
+
eventPayload.insert({"message", message});
|
|
34
|
+
audioEventHandlerRegistry_->invokeHandlerWithEventBody("error", callbackId, eventPayload);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
bool AudioFileWriter::isFileOpen() {
|
|
38
|
+
return isFileOpen_.load(std::memory_order_acquire);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
} // namespace audioapi
|