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.
Files changed (137) hide show
  1. package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +382 -39
  2. package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.h +45 -18
  3. package/android/src/main/cpp/audioapi/android/core/NativeAudioRecorder.hpp +9 -9
  4. package/android/src/main/cpp/audioapi/android/core/utils/AndroidFileWriterBackend.h +33 -0
  5. package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp +170 -0
  6. package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h +46 -0
  7. package/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp +0 -1
  8. package/android/src/main/cpp/audioapi/android/core/utils/FileOptions.cpp +83 -0
  9. package/android/src/main/cpp/audioapi/android/core/utils/FileOptions.h +22 -0
  10. package/android/src/main/cpp/audioapi/android/core/utils/MiniaudioImplementation.cpp +8 -0
  11. package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/FFmpegFileWriter.cpp +493 -0
  12. package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/FFmpegFileWriter.h +70 -0
  13. package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/ptrs.hpp +56 -0
  14. package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/utils.cpp +114 -0
  15. package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/utils.h +34 -0
  16. package/android/src/main/cpp/audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.cpp +296 -0
  17. package/android/src/main/cpp/audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.h +40 -0
  18. package/android/src/main/cpp/audioapi/android/system/NativeFileInfo.hpp +32 -0
  19. package/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt +2 -0
  20. package/android/src/main/java/com/swmansion/audioapi/system/AudioFocusListener.kt +7 -3
  21. package/android/src/main/java/com/swmansion/audioapi/system/CentralizedForegroundService.kt +1 -0
  22. package/android/src/main/java/com/swmansion/audioapi/system/NativeFileInfo.kt +18 -0
  23. package/android/src/main/java/com/swmansion/audioapi/system/notification/PlaybackNotification.kt +1 -0
  24. package/android/src/main/java/com/swmansion/audioapi/system/notification/RecordingNotificationReceiver.kt +2 -0
  25. package/common/cpp/audioapi/AudioAPIModuleInstaller.h +3 -11
  26. package/common/cpp/audioapi/HostObjects/inputs/AudioRecorderHostObject.cpp +145 -16
  27. package/common/cpp/audioapi/HostObjects/inputs/AudioRecorderHostObject.h +21 -6
  28. package/common/cpp/audioapi/core/inputs/AudioRecorder.cpp +43 -60
  29. package/common/cpp/audioapi/core/inputs/AudioRecorder.h +53 -33
  30. package/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp +42 -14
  31. package/common/cpp/audioapi/core/sources/RecorderAdapterNode.h +12 -9
  32. package/common/cpp/audioapi/core/utils/AudioFileWriter.cpp +41 -0
  33. package/common/cpp/audioapi/core/utils/AudioFileWriter.h +44 -0
  34. package/common/cpp/audioapi/core/utils/AudioRecorderCallback.cpp +101 -0
  35. package/common/cpp/audioapi/core/utils/AudioRecorderCallback.h +52 -0
  36. package/common/cpp/audioapi/utils/AudioFileProperties.cpp +92 -0
  37. package/common/cpp/audioapi/utils/AudioFileProperties.h +76 -0
  38. package/common/cpp/audioapi/utils/Result.hpp +323 -0
  39. package/common/cpp/audioapi/utils/UnitConversion.h +9 -0
  40. package/ios/audioapi/ios/AudioAPIModule.mm +9 -14
  41. package/ios/audioapi/ios/core/IOSAudioPlayer.h +1 -1
  42. package/ios/audioapi/ios/core/IOSAudioPlayer.mm +7 -6
  43. package/ios/audioapi/ios/core/IOSAudioRecorder.h +39 -13
  44. package/ios/audioapi/ios/core/IOSAudioRecorder.mm +302 -26
  45. package/ios/audioapi/ios/core/NativeAudioPlayer.m +7 -11
  46. package/ios/audioapi/ios/core/NativeAudioRecorder.h +8 -9
  47. package/ios/audioapi/ios/core/NativeAudioRecorder.m +70 -76
  48. package/ios/audioapi/ios/core/utils/AudioDecoder.mm +1 -0
  49. package/ios/audioapi/ios/core/utils/FileOptions.h +31 -0
  50. package/ios/audioapi/ios/core/utils/FileOptions.mm +195 -0
  51. package/ios/audioapi/ios/core/utils/IOSFileWriter.h +53 -0
  52. package/ios/audioapi/ios/core/utils/IOSFileWriter.mm +239 -0
  53. package/ios/audioapi/ios/core/utils/IOSRecorderCallback.h +47 -0
  54. package/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm +185 -0
  55. package/ios/audioapi/ios/system/AudioEngine.h +21 -16
  56. package/ios/audioapi/ios/system/AudioEngine.mm +138 -130
  57. package/ios/audioapi/ios/system/AudioSessionManager.h +19 -9
  58. package/ios/audioapi/ios/system/AudioSessionManager.mm +250 -215
  59. package/ios/audioapi/ios/system/NotificationManager.mm +24 -42
  60. package/lib/commonjs/api.js +82 -109
  61. package/lib/commonjs/api.js.map +1 -1
  62. package/lib/commonjs/core/AudioRecorder.js +159 -13
  63. package/lib/commonjs/core/AudioRecorder.js.map +1 -1
  64. package/lib/commonjs/specs/NativeAudioAPIModule.js.map +1 -1
  65. package/lib/commonjs/system/notification/PlaybackNotificationManager.js +17 -14
  66. package/lib/commonjs/system/notification/PlaybackNotificationManager.js.map +1 -1
  67. package/lib/commonjs/system/notification/RecordingNotificationManager.js +22 -19
  68. package/lib/commonjs/system/notification/RecordingNotificationManager.js.map +1 -1
  69. package/lib/commonjs/system/notification/SimpleNotificationManager.js +16 -13
  70. package/lib/commonjs/system/notification/SimpleNotificationManager.js.map +1 -1
  71. package/lib/commonjs/types.js +39 -0
  72. package/lib/commonjs/types.js.map +1 -1
  73. package/lib/commonjs/utils/filePresets.js +43 -0
  74. package/lib/commonjs/utils/filePresets.js.map +1 -0
  75. package/lib/commonjs/web-system/notification/PlaybackNotificationManager.js +6 -3
  76. package/lib/commonjs/web-system/notification/PlaybackNotificationManager.js.map +1 -1
  77. package/lib/commonjs/web-system/notification/RecordingNotificationManager.js +6 -3
  78. package/lib/commonjs/web-system/notification/RecordingNotificationManager.js.map +1 -1
  79. package/lib/module/api.js +5 -4
  80. package/lib/module/api.js.map +1 -1
  81. package/lib/module/core/AudioRecorder.js +159 -13
  82. package/lib/module/core/AudioRecorder.js.map +1 -1
  83. package/lib/module/specs/NativeAudioAPIModule.js.map +1 -1
  84. package/lib/module/system/notification/PlaybackNotificationManager.js +17 -14
  85. package/lib/module/system/notification/PlaybackNotificationManager.js.map +1 -1
  86. package/lib/module/system/notification/RecordingNotificationManager.js +22 -19
  87. package/lib/module/system/notification/RecordingNotificationManager.js.map +1 -1
  88. package/lib/module/system/notification/SimpleNotificationManager.js +16 -13
  89. package/lib/module/system/notification/SimpleNotificationManager.js.map +1 -1
  90. package/lib/module/types.js +38 -1
  91. package/lib/module/types.js.map +1 -1
  92. package/lib/module/utils/filePresets.js +39 -0
  93. package/lib/module/utils/filePresets.js.map +1 -0
  94. package/lib/module/web-system/notification/PlaybackNotificationManager.js +6 -3
  95. package/lib/module/web-system/notification/PlaybackNotificationManager.js.map +1 -1
  96. package/lib/module/web-system/notification/RecordingNotificationManager.js +6 -3
  97. package/lib/module/web-system/notification/RecordingNotificationManager.js.map +1 -1
  98. package/lib/typescript/api.d.ts +5 -4
  99. package/lib/typescript/api.d.ts.map +1 -1
  100. package/lib/typescript/core/AudioRecorder.d.ts +69 -7
  101. package/lib/typescript/core/AudioRecorder.d.ts.map +1 -1
  102. package/lib/typescript/events/types.d.ts +36 -2
  103. package/lib/typescript/events/types.d.ts.map +1 -1
  104. package/lib/typescript/interfaces.d.ts +24 -4
  105. package/lib/typescript/interfaces.d.ts.map +1 -1
  106. package/lib/typescript/specs/NativeAudioAPIModule.d.ts +1 -1
  107. package/lib/typescript/specs/NativeAudioAPIModule.d.ts.map +1 -1
  108. package/lib/typescript/system/notification/PlaybackNotificationManager.d.ts +4 -3
  109. package/lib/typescript/system/notification/PlaybackNotificationManager.d.ts.map +1 -1
  110. package/lib/typescript/system/notification/RecordingNotificationManager.d.ts +4 -3
  111. package/lib/typescript/system/notification/RecordingNotificationManager.d.ts.map +1 -1
  112. package/lib/typescript/system/notification/SimpleNotificationManager.d.ts +3 -2
  113. package/lib/typescript/system/notification/SimpleNotificationManager.d.ts.map +1 -1
  114. package/lib/typescript/system/notification/types.d.ts.map +1 -1
  115. package/lib/typescript/types.d.ts +79 -3
  116. package/lib/typescript/types.d.ts.map +1 -1
  117. package/lib/typescript/utils/filePresets.d.ts +9 -0
  118. package/lib/typescript/utils/filePresets.d.ts.map +1 -0
  119. package/lib/typescript/web-system/notification/PlaybackNotificationManager.d.ts +4 -3
  120. package/lib/typescript/web-system/notification/PlaybackNotificationManager.d.ts.map +1 -1
  121. package/lib/typescript/web-system/notification/RecordingNotificationManager.d.ts +4 -3
  122. package/lib/typescript/web-system/notification/RecordingNotificationManager.d.ts.map +1 -1
  123. package/package.json +4 -4
  124. package/src/AudioAPIModule/globals.d.ts +1 -2
  125. package/src/api.ts +8 -29
  126. package/src/core/AudioRecorder.ts +195 -23
  127. package/src/events/types.ts +40 -2
  128. package/src/interfaces.ts +34 -5
  129. package/src/specs/NativeAudioAPIModule.ts +2 -2
  130. package/src/system/notification/PlaybackNotificationManager.ts +20 -16
  131. package/src/system/notification/RecordingNotificationManager.ts +26 -21
  132. package/src/system/notification/SimpleNotificationManager.ts +18 -13
  133. package/src/system/notification/types.ts +1 -0
  134. package/src/types.ts +89 -3
  135. package/src/utils/filePresets.ts +47 -0
  136. package/src/web-system/notification/PlaybackNotificationManager.ts +9 -5
  137. 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
- JSI_PROPERTY_SETTER_IMPL(AudioRecorderHostObject, onAudioReady) {
39
- audioRecorder_->setOnAudioReadyCallbackId(std::stoull(value.getString(runtime).utf8(runtime)));
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, start) {
56
- audioRecorder_->start();
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, stop) {
62
- audioRecorder_->stop();
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
- JSI_PROPERTY_SETTER_DECL(onAudioReady);
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
- JSI_HOST_FUNCTION_DECL(start);
28
- JSI_HOST_FUNCTION_DECL(stop);
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
- #include <audioapi/HostObjects/sources/AudioBufferHostObject.h>
1
+
2
2
  #include <audioapi/core/inputs/AudioRecorder.h>
3
- #include <audioapi/core/sources/AudioBuffer.h>
4
- #include <audioapi/core/sources/RecorderAdapterNode.h>
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
- AudioRecorder::AudioRecorder(
17
- float sampleRate,
18
- int bufferLength,
19
- const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry)
20
- : sampleRate_(sampleRate),
21
- bufferLength_(bufferLength),
22
- audioEventHandlerRegistry_(audioEventHandlerRegistry) {
23
- constexpr int minRingBufferSize = 8192;
24
- ringBufferSize_ = std::max(2 * bufferLength, minRingBufferSize);
25
- circularBuffer_ = std::make_shared<CircularAudioArray>(ringBufferSize_);
26
- isRunning_.store(false);
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
- void AudioRecorder::setOnAudioReadyCallbackId(uint64_t callbackId) {
30
- onAudioReadyCallbackId_ = callbackId;
22
+ errorCallbackId_.store(callbackId, std::memory_order_release);
31
23
  }
32
24
 
33
- void AudioRecorder::invokeOnAudioReadyCallback(
34
- const std::shared_ptr<AudioBus> &bus,
35
- int numFrames) {
36
- auto audioBuffer = std::make_shared<AudioBuffer>(bus);
37
- auto audioBufferHostObject = std::make_shared<AudioBufferHostObject>(audioBuffer);
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
- std::unordered_map<std::string, EventValue> body = {};
40
- body.insert({"buffer", audioBufferHostObject});
41
- body.insert({"numFrames", numFrames});
31
+ if (usesFileOutput()) {
32
+ fileWriter_->clearOnErrorCallback();
33
+ }
42
34
 
43
- if (audioEventHandlerRegistry_ != nullptr) {
44
- audioEventHandlerRegistry_->invokeHandlerWithEventBody(
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
- void AudioRecorder::sendRemainingData() {
50
- auto bus =
51
- std::make_shared<AudioBus>(circularBuffer_->getNumberOfAvailableFrames(), 1, sampleRate_);
52
- auto *outputChannel = bus->getChannel(0)->getData();
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
- circularBuffer_->pop_front(outputChannel, circularBuffer_->getNumberOfAvailableFrames());
47
+ if (usesFileOutput()) {
48
+ duration = fileWriter_->getCurrentDuration();
49
+ }
56
50
 
57
- invokeOnAudioReadyCallback(bus, availableFrames);
51
+ return duration;
58
52
  }
59
53
 
60
- void AudioRecorder::connect(const std::shared_ptr<RecorderAdapterNode> &node) {
61
- node->init(ringBufferSize_);
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
- void AudioRecorder::disconnect() {
68
- adapterNodeLock_.lock();
69
- adapterNode_ = nullptr;
70
- adapterNodeLock_.unlock();
58
+ bool AudioRecorder::usesFileOutput() const {
59
+ return fileOutputEnabled_.load(std::memory_order_acquire);
71
60
  }
72
61
 
73
- void AudioRecorder::writeToBuffers(const float *data, int numFrames) {
74
- if (adapterNodeLock_.try_lock()) {
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 CircularOverflowableAudioArray;
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
- float sampleRate,
19
- int bufferLength,
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
- void setOnAudioReadyCallbackId(uint64_t callbackId);
25
- void invokeOnAudioReadyCallback(const std::shared_ptr<AudioBus> &bus, int numFrames);
26
- void sendRemainingData();
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
- /// @brief
29
- /// # Connects the recorder to the adapter node.
30
- ///
31
- /// The adapter node will be used to read audio data from the recorder.
32
- /// @note Few frames of audio might not yet be written to the buffer when connecting.
33
- void connect(const std::shared_ptr<RecorderAdapterNode> &node);
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
- /// @brief
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
- virtual void start() = 0;
43
- virtual void stop() = 0;
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
- float sampleRate_;
47
- int bufferLength_;
48
- size_t ringBufferSize_;
62
+ std::atomic<RecorderState> state_{RecorderState::Idle};
49
63
 
50
- std::atomic<bool> isRunning_;
51
- std::shared_ptr<CircularAudioArray> circularBuffer_;
64
+ std::atomic<bool> isConnected_{false};
65
+ std::atomic<bool> fileOutputEnabled_{false};
66
+ std::atomic<bool> callbackOutputEnabled_{false};
52
67
 
53
- mutable std::mutex adapterNodeLock_;
54
- std::shared_ptr<RecorderAdapterNode> adapterNode_ = nullptr;
68
+ std::mutex callbackMutex_;
69
+ std::mutex fileWriterMutex_;
70
+ std::mutex errorCallbackMutex_;
71
+ mutable std::mutex adapterNodeMutex_;
55
72
 
56
- std::shared_ptr<AudioEventHandlerRegistry> audioEventHandlerRegistry_;
57
- uint64_t onAudioReadyCallbackId_ = 0;
73
+ std::atomic<uint64_t> errorCallbackId_{0};
58
74
 
59
- void writeToBuffers(const float *data, int numFrames);
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
- throw std::runtime_error(
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
- buff_ = std::make_shared<CircularOverflowableAudioArray>(bufferSize);
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
- float *outputChannel = processingBus->getChannel(0)->getData();
31
- readFrames(outputChannel, framesToProcess);
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(float *output, const size_t framesToRead) {
41
- size_t readFrames = buff_->read(output, framesToRead);
69
+ void RecorderAdapterNode::readFrames(const size_t framesToRead) {
70
+ adapterOutputBus_->zero();
42
71
 
43
- if (readFrames < framesToRead) {
44
- // Fill the rest with silence
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
- void init(size_t bufferSize);
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<CircularOverflowableAudioArray> buff_;
39
+ std::shared_ptr<AudioBus> adapterOutputBus_;
34
40
 
35
41
  private:
36
- /// @brief Read audio frames from the recorder's internal adapterBuffer.
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(float *output, size_t framesToRead);
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