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
@@ -0,0 +1,239 @@
1
+ #import <AVFoundation/AVFoundation.h>
2
+ #import <Foundation/Foundation.h>
3
+
4
+ #include <audioapi/events/AudioEventHandlerRegistry.h>
5
+ #include <audioapi/ios/core/utils/FileOptions.h>
6
+ #include <audioapi/ios/core/utils/IOSFileWriter.h>
7
+ #include <audioapi/utils/AudioFileProperties.h>
8
+ #include <audioapi/utils/Result.hpp>
9
+ #include <audioapi/utils/UnitConversion.h>
10
+
11
+ namespace audioapi {
12
+ IOSFileWriter::IOSFileWriter(
13
+ const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry,
14
+ const std::shared_ptr<AudioFileProperties> &fileProperties)
15
+ : AudioFileWriter(audioEventHandlerRegistry, fileProperties)
16
+ {
17
+ }
18
+
19
+ IOSFileWriter::~IOSFileWriter()
20
+ {
21
+ @autoreleasepool {
22
+ fileURL_ = nil;
23
+ audioFile_ = nil;
24
+ converter_ = nil;
25
+ bufferFormat_ = nil;
26
+ }
27
+ }
28
+
29
+ /// @brief Opens an audio file for writing with the specified buffer format and maximum input buffer length.
30
+ /// This method initializes the AVAudioFile and AVAudioConverter for audio data writing and conversion.
31
+ /// This method should be called from the JS thread only.
32
+ /// @param bufferFormat The audio format of the input buffer.
33
+ /// @param maxInputBufferLength The maximum length of the input buffer in frames.
34
+ /// @returns An OpenFileResult indicating success with the file path or an error message.
35
+ OpenFileResult IOSFileWriter::openFile(AVAudioFormat *bufferFormat, size_t maxInputBufferLength)
36
+ {
37
+ @autoreleasepool {
38
+ if (audioFile_ != nil) {
39
+ return OpenFileResult::Err("file already open");
40
+ }
41
+
42
+ framesWritten_.store(0, std::memory_order_release);
43
+ bufferFormat_ = bufferFormat;
44
+
45
+ NSError *error = nil;
46
+ NSDictionary *settings = ios::fileoptions::getFileSettings(fileProperties_);
47
+ fileURL_ = ios::fileoptions::getFileURL(fileProperties_);
48
+
49
+ if (fileProperties_->sampleRate == 0 || fileProperties_->channelCount == 0) {
50
+ return OpenFileResult::Err(
51
+ "Invalid file properties: sampleRate and channelCount must be greater than 0");
52
+ }
53
+
54
+ if (bufferFormat.sampleRate == 0 || bufferFormat.channelCount == 0) {
55
+ return OpenFileResult::Err(
56
+ "Invalid input format: sampleRate and channelCount must be greater than 0");
57
+ }
58
+
59
+ audioFile_ = [[AVAudioFile alloc] initForWriting:fileURL_
60
+ settings:settings
61
+ commonFormat:AVAudioPCMFormatFloat32
62
+ interleaved:bufferFormat.interleaved
63
+ error:&error];
64
+
65
+ if (error != nil) {
66
+ return OpenFileResult::Err(
67
+ std::string("Error creating audio file for writing: ") +
68
+ [[error debugDescription] UTF8String]);
69
+ }
70
+
71
+ converter_ = [[AVAudioConverter alloc] initFromFormat:bufferFormat
72
+ toFormat:[audioFile_ processingFormat]];
73
+ converter_.sampleRateConverterAlgorithm = AVSampleRateConverterAlgorithm_Normal;
74
+ converter_.sampleRateConverterQuality = AVAudioQualityMax;
75
+ converter_.primeMethod = AVAudioConverterPrimeMethod_None;
76
+
77
+ converterInputBufferSize_ = maxInputBufferLength;
78
+ converterOutputBufferSize_ = std::max(
79
+ (double)maxInputBufferLength,
80
+ fileProperties_->sampleRate / bufferFormat.sampleRate * maxInputBufferLength);
81
+
82
+ converterInputBuffer_ =
83
+ [[AVAudioPCMBuffer alloc] initWithPCMFormat:bufferFormat
84
+ frameCapacity:(AVAudioFrameCount)maxInputBufferLength];
85
+ converterOutputBuffer_ =
86
+ [[AVAudioPCMBuffer alloc] initWithPCMFormat:[audioFile_ processingFormat]
87
+ frameCapacity:(AVAudioFrameCount)converterOutputBufferSize_];
88
+
89
+ if (converterInputBuffer_ == nil || converterOutputBuffer_ == nil || audioFile_ == nil ||
90
+ converter_ == nil) {
91
+ audioFile_ = nil;
92
+ converter_ = nil;
93
+ converterInputBuffer_ = nil;
94
+ converterOutputBuffer_ = nil;
95
+
96
+ return OpenFileResult::Err("Error creating converter buffers");
97
+ }
98
+
99
+ return OpenFileResult::Ok([[fileURL_ path] UTF8String]);
100
+ }
101
+ }
102
+
103
+ /// @brief Closes the currently open audio file and finalizes writing.
104
+ /// This method retrieves the final file duration and size before closing.
105
+ /// This method should be called from the JS thread only.
106
+ /// @returns A CloseFileResult indicating success with file duration and size or an error message.
107
+ CloseFileResult IOSFileWriter::closeFile()
108
+ {
109
+ @autoreleasepool {
110
+ NSError *error;
111
+ std::string filePath = [[fileURL_ path] UTF8String];
112
+
113
+ if (audioFile_ == nil) {
114
+ return CloseFileResult::Err("file is not open: " + filePath);
115
+ }
116
+
117
+ // AVAudioFile automatically finalizes the file when deallocated
118
+ audioFile_ = nil;
119
+
120
+ double fileDuration = CMTimeGetSeconds([[AVURLAsset URLAssetWithURL:fileURL_
121
+ options:nil] duration]);
122
+ double fileSizeBytesMb = static_cast<double>([[[NSFileManager defaultManager]
123
+ attributesOfItemAtPath:fileURL_.path
124
+ error:&error] fileSize]) /
125
+ MB_IN_BYTES;
126
+
127
+ if (error != nil) {
128
+ NSLog(@"⚠️ closeFile: error while retrieving file size");
129
+ fileSizeBytesMb = 0;
130
+ }
131
+
132
+ fileURL_ = nil;
133
+ framesWritten_.store(0, std::memory_order_release);
134
+
135
+ return CloseFileResult::Ok(std::make_tuple(fileDuration, fileSizeBytesMb));
136
+ }
137
+ }
138
+
139
+ /// @brief Writes audio data to the open audio file, performing format conversion if necessary.
140
+ /// This method should be called from the audio thread.
141
+ /// @param audioBufferList Pointer to the AudioBufferList containing the audio data to write.
142
+ /// @param numFrames Number of audio frames in the audioBufferList.
143
+ /// @returns True if the write operation was successful, false otherwise.
144
+ bool IOSFileWriter::writeAudioData(const AudioBufferList *audioBufferList, int numFrames)
145
+ {
146
+ if (audioFile_ == nil) {
147
+ invokeOnErrorCallback("Attempted to write audio data when file is not open");
148
+ return false;
149
+ }
150
+
151
+ @autoreleasepool {
152
+ NSError *error = nil;
153
+ AVAudioFormat *fileFormat = [audioFile_ processingFormat];
154
+
155
+ if (bufferFormat_.sampleRate == fileFormat.sampleRate &&
156
+ bufferFormat_.channelCount == fileFormat.channelCount &&
157
+ bufferFormat_.isInterleaved == fileFormat.isInterleaved) {
158
+ // We can use the converter input buffer as a "transport" layer to the file
159
+ for (size_t i = 0; i < bufferFormat_.channelCount; ++i) {
160
+ memcpy(
161
+ converterInputBuffer_.mutableAudioBufferList->mBuffers[i].mData,
162
+ audioBufferList->mBuffers[i].mData,
163
+ audioBufferList->mBuffers[i].mDataByteSize);
164
+ }
165
+ converterInputBuffer_.frameLength = numFrames;
166
+
167
+ [audioFile_ writeFromBuffer:converterInputBuffer_ error:&error];
168
+
169
+ if (error != nil) {
170
+ invokeOnErrorCallback(
171
+ std::string("Error writing audio data to file, native error: ") +
172
+ [[error debugDescription] UTF8String]);
173
+ return false;
174
+ }
175
+
176
+ framesWritten_.fetch_add(numFrames, std::memory_order_acq_rel);
177
+ return true;
178
+ }
179
+
180
+ for (size_t i = 0; i < bufferFormat_.channelCount; ++i) {
181
+ memcpy(
182
+ converterInputBuffer_.mutableAudioBufferList->mBuffers[i].mData,
183
+ audioBufferList->mBuffers[i].mData,
184
+ audioBufferList->mBuffers[i].mDataByteSize);
185
+ }
186
+
187
+ converterInputBuffer_.frameLength = numFrames;
188
+
189
+ __block BOOL handedOff = false;
190
+ AVAudioConverterInputBlock inputBlock = ^AVAudioBuffer *_Nullable(
191
+ AVAudioPacketCount inNumberOfPackets, AVAudioConverterInputStatus *outStatus)
192
+ {
193
+ if (handedOff) {
194
+ *outStatus = AVAudioConverterInputStatus_NoDataNow;
195
+ return nil;
196
+ }
197
+
198
+ handedOff = true;
199
+ *outStatus = AVAudioConverterInputStatus_HaveData;
200
+ return converterInputBuffer_;
201
+ };
202
+
203
+ [converter_ convertToBuffer:converterOutputBuffer_ error:&error withInputFromBlock:inputBlock];
204
+ converterOutputBuffer_.frameLength =
205
+ fileProperties_->sampleRate / bufferFormat_.sampleRate * numFrames;
206
+
207
+ if (error != nil) {
208
+ invokeOnErrorCallback(
209
+ std::string("Error during audio conversion, native error: ") +
210
+ [[error debugDescription] UTF8String]);
211
+ return false;
212
+ }
213
+
214
+ [audioFile_ writeFromBuffer:converterOutputBuffer_ error:&error];
215
+
216
+ if (error != nil) {
217
+ invokeOnErrorCallback(
218
+ std::string("Error writing audio data to file, native error: ") +
219
+ [[error debugDescription] UTF8String]);
220
+ return false;
221
+ }
222
+
223
+ framesWritten_.fetch_add(numFrames, std::memory_order_acq_rel);
224
+ return true;
225
+ }
226
+ }
227
+
228
+ double IOSFileWriter::getCurrentDuration() const
229
+ {
230
+ return static_cast<double>(framesWritten_.load(std::memory_order_acquire)) /
231
+ bufferFormat_.sampleRate;
232
+ }
233
+
234
+ std::string IOSFileWriter::getFilePath() const
235
+ {
236
+ return [[fileURL_ path] UTF8String];
237
+ }
238
+
239
+ } // namespace audioapi
@@ -0,0 +1,47 @@
1
+ #pragma once
2
+
3
+ #ifndef __OBJC__ // when compiled as C++
4
+ typedef struct objc_object AVAudioFormat;
5
+ typedef struct objc_object AudioBufferList;
6
+ typedef struct objc_object AVAudioConverter;
7
+ #endif
8
+
9
+ #include <audioapi/core/utils/AudioRecorderCallback.h>
10
+ #include <audioapi/utils/Result.hpp>
11
+ #include <memory>
12
+ #include <vector>
13
+
14
+ namespace audioapi {
15
+
16
+ class AudioBus;
17
+ class CircularAudioArray;
18
+ class AudioEventHandlerRegistry;
19
+
20
+ class IOSRecorderCallback : public AudioRecorderCallback {
21
+ public:
22
+ IOSRecorderCallback(
23
+ const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry,
24
+ float sampleRate,
25
+ size_t bufferLength,
26
+ int channelCount,
27
+ uint64_t callbackId);
28
+ ~IOSRecorderCallback();
29
+
30
+ Result<NoneType, std::string> prepare(AVAudioFormat *bufferFormat, size_t maxInputBufferLength);
31
+ void cleanup() override;
32
+
33
+ void receiveAudioData(const AudioBufferList *audioBufferList, int numFrames);
34
+
35
+ protected:
36
+ size_t converterInputBufferSize_;
37
+ size_t converterOutputBufferSize_;
38
+
39
+ AVAudioFormat *bufferFormat_;
40
+ AVAudioFormat *callbackFormat_;
41
+ AVAudioConverter *converter_;
42
+
43
+ AVAudioPCMBuffer *converterInputBuffer_;
44
+ AVAudioPCMBuffer *converterOutputBuffer_;
45
+ };
46
+
47
+ } // namespace audioapi
@@ -0,0 +1,185 @@
1
+ #import <AVFoundation/AVFoundation.h>
2
+ #import <Foundation/Foundation.h>
3
+
4
+ #include <audioapi/HostObjects/sources/AudioBufferHostObject.h>
5
+ #include <audioapi/core/utils/Constants.h>
6
+ #include <audioapi/dsp/VectorMath.h>
7
+ #include <audioapi/events/AudioEventHandlerRegistry.h>
8
+ #include <audioapi/ios/core/utils/IOSRecorderCallback.h>
9
+ #include <audioapi/utils/AudioArray.h>
10
+ #include <audioapi/utils/AudioBus.h>
11
+ #include <audioapi/utils/CircularAudioArray.h>
12
+ #include <audioapi/utils/Result.hpp>
13
+ #include <algorithm>
14
+
15
+ namespace audioapi {
16
+
17
+ IOSRecorderCallback::IOSRecorderCallback(
18
+ const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry,
19
+ float sampleRate,
20
+ size_t bufferLength,
21
+ int channelCount,
22
+ uint64_t callbackId)
23
+ : AudioRecorderCallback(
24
+ audioEventHandlerRegistry,
25
+ sampleRate,
26
+ bufferLength,
27
+ channelCount,
28
+ callbackId)
29
+ {
30
+ }
31
+
32
+ IOSRecorderCallback::~IOSRecorderCallback()
33
+ {
34
+ @autoreleasepool {
35
+ converter_ = nil;
36
+ bufferFormat_ = nil;
37
+ callbackFormat_ = nil;
38
+ converterInputBuffer_ = nil;
39
+ converterOutputBuffer_ = nil;
40
+
41
+ for (int i = 0; i < channelCount_; ++i) {
42
+ circularBus_[i]->zero();
43
+ }
44
+ }
45
+ }
46
+
47
+ /// @brief Prepares the IOSRecorderCallback for receiving audio data.
48
+ /// This involves setting up the audio converter and buffers based on the provided input format.
49
+ /// This method should be called from the JS thread only.
50
+ /// @param bufferFormat The format of the incoming audio data.
51
+ /// @param maxInputBufferLength The maximum length of the input buffer in frames.
52
+ /// @returns Result indicating success or error with message.
53
+ Result<NoneType, std::string> IOSRecorderCallback::prepare(
54
+ AVAudioFormat *bufferFormat,
55
+ size_t maxInputBufferLength)
56
+ {
57
+ @autoreleasepool {
58
+ bufferFormat_ = bufferFormat;
59
+ converterInputBufferSize_ = maxInputBufferLength;
60
+
61
+ if (bufferFormat.sampleRate <= 0 || bufferFormat.channelCount == 0) {
62
+ return Result<NoneType, std::string>::Err(
63
+ "Invalid input format: sampleRate and channelCount must be greater than 0");
64
+ }
65
+
66
+ if (sampleRate_ <= 0 || channelCount_ == 0) {
67
+ return Result<NoneType, std::string>::Err(
68
+ "Invalid callback format: sampleRate and channelCount must be greater than 0");
69
+ }
70
+
71
+ converterOutputBufferSize_ = std::max(
72
+ (double)maxInputBufferLength, sampleRate_ / bufferFormat.sampleRate * maxInputBufferLength);
73
+
74
+ callbackFormat_ = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32
75
+ sampleRate:sampleRate_
76
+ channels:channelCount_
77
+ interleaved:NO];
78
+
79
+ converter_ = [[AVAudioConverter alloc] initFromFormat:bufferFormat toFormat:callbackFormat_];
80
+ converter_.sampleRateConverterAlgorithm = AVSampleRateConverterAlgorithm_Normal;
81
+ converter_.sampleRateConverterQuality = AVAudioQualityMax;
82
+ converter_.primeMethod = AVAudioConverterPrimeMethod_None;
83
+
84
+ converterInputBuffer_ =
85
+ [[AVAudioPCMBuffer alloc] initWithPCMFormat:bufferFormat_
86
+ frameCapacity:(AVAudioFrameCount)converterInputBufferSize_];
87
+ converterOutputBuffer_ =
88
+ [[AVAudioPCMBuffer alloc] initWithPCMFormat:callbackFormat_
89
+ frameCapacity:(AVAudioFrameCount)converterOutputBufferSize_];
90
+ }
91
+
92
+ return Result<NoneType, std::string>::Ok(None);
93
+ }
94
+
95
+ /// @brief Cleans up resources used by the IOSRecorderCallback.
96
+ /// This method should be called from the JS thread only.
97
+ void IOSRecorderCallback::cleanup()
98
+ {
99
+ @autoreleasepool {
100
+ emitAudioData(true);
101
+
102
+ converter_ = nil;
103
+ bufferFormat_ = nil;
104
+ callbackFormat_ = nil;
105
+ converterInputBuffer_ = nil;
106
+ converterOutputBuffer_ = nil;
107
+
108
+ for (int i = 0; i < channelCount_; ++i) {
109
+ circularBus_[i]->zero();
110
+ }
111
+ }
112
+ }
113
+
114
+ /// @brief Receives audio data from the recorder, processes it, and stores it in the circular buffer.
115
+ /// The data is converted using AVAudioConverter if the input format differs from the user desired callback format.
116
+ /// This method runs on the audio thread.
117
+ /// @param inputBuffer Pointer to the AudioBufferList containing the incoming audio data.
118
+ /// @param numFrames Number of frames in the input buffer.
119
+ void IOSRecorderCallback::receiveAudioData(const AudioBufferList *inputBuffer, int numFrames)
120
+ {
121
+ if (!isInitialized_.load(std::memory_order_acquire)) {
122
+ return;
123
+ }
124
+
125
+ @autoreleasepool {
126
+ NSError *error = nil;
127
+
128
+ if (bufferFormat_.sampleRate == sampleRate_ && bufferFormat_.channelCount == channelCount_ &&
129
+ !bufferFormat_.isInterleaved) {
130
+ // Directly write to circular buffer
131
+ for (int i = 0; i < channelCount_; ++i) {
132
+ auto *inputChannel = static_cast<float *>(inputBuffer->mBuffers[i].mData);
133
+ circularBus_[i]->push_back(inputChannel, numFrames);
134
+ }
135
+
136
+ emitAudioData();
137
+ return;
138
+ }
139
+
140
+ size_t outputFrameCount = ceil(numFrames * (sampleRate_ / bufferFormat_.sampleRate));
141
+
142
+ for (size_t i = 0; i < bufferFormat_.channelCount; ++i) {
143
+ memcpy(
144
+ converterInputBuffer_.mutableAudioBufferList->mBuffers[i].mData,
145
+ inputBuffer->mBuffers[i].mData,
146
+ inputBuffer->mBuffers[i].mDataByteSize);
147
+ }
148
+
149
+ converterInputBuffer_.frameLength = numFrames;
150
+
151
+ __block BOOL handedOff = false;
152
+ AVAudioConverterInputBlock inputBlock = ^AVAudioBuffer *_Nullable(
153
+ AVAudioPacketCount inNumberOfPackets, AVAudioConverterInputStatus *outStatus)
154
+ {
155
+ if (handedOff) {
156
+ *outStatus = AVAudioConverterInputStatus_NoDataNow;
157
+ return nil;
158
+ }
159
+
160
+ handedOff = true;
161
+ *outStatus = AVAudioConverterInputStatus_HaveData;
162
+ return converterInputBuffer_;
163
+ };
164
+
165
+ [converter_ convertToBuffer:converterOutputBuffer_ error:&error withInputFromBlock:inputBlock];
166
+ converterOutputBuffer_.frameLength = sampleRate_ / bufferFormat_.sampleRate * numFrames;
167
+
168
+ if (error != nil) {
169
+ invokeOnErrorCallback(
170
+ std::string("Error during audio conversion, native error: ") +
171
+ [[error debugDescription] UTF8String]);
172
+ return;
173
+ }
174
+
175
+ for (int i = 0; i < channelCount_; ++i) {
176
+ auto *inputChannel =
177
+ static_cast<float *>(converterOutputBuffer_.audioBufferList->mBuffers[i].mData);
178
+ circularBus_[i]->push_back(inputChannel, outputFrameCount);
179
+ }
180
+
181
+ emitAudioData();
182
+ }
183
+ }
184
+
185
+ } // namespace audioapi
@@ -5,30 +5,26 @@
5
5
 
6
6
  @class AudioSessionManager;
7
7
 
8
+ typedef NS_ENUM(NSInteger, AudioEngineState) {
9
+ AudioEngineStateIdle = 0,
10
+ AudioEngineStateRunning,
11
+ AudioEngineStatePaused,
12
+ AudioEngineStateInterrupted
13
+ };
14
+
8
15
  @interface AudioEngine : NSObject
9
16
 
10
- @property (nonatomic, assign) bool isInterrupted;
11
- @property (nonatomic, assign) bool isSupposedToBeRunning;
17
+ @property (nonatomic, assign) AudioEngineState state;
12
18
  @property (nonatomic, strong) AVAudioEngine *audioEngine;
13
19
  @property (nonatomic, strong) NSMutableDictionary *sourceNodes;
14
20
  @property (nonatomic, strong) NSMutableDictionary *sourceFormats;
15
- @property (nonatomic, strong) NSMutableDictionary *sourceStates;
16
21
  @property (nonatomic, strong) AVAudioSinkNode *inputNode;
17
22
  @property (nonatomic, weak) AudioSessionManager *sessionManager;
18
23
 
19
- - (instancetype)initWithAudioSessionManager:(AudioSessionManager *)sessionManager;
20
-
24
+ - (instancetype)init;
21
25
  + (instancetype)sharedInstance;
26
+
22
27
  - (void)cleanup;
23
- - (bool)rebuildAudioEngineAndStartIfNecessary;
24
- - (bool)restartAudioEngine;
25
- - (bool)startEngine;
26
- - (void)stopEngine;
27
- - (void)pauseEngine:(NSString *)sourceNodeId;
28
- - (bool)isRunning;
29
- - (void)markAsInterrupted;
30
- - (void)unmarkAsInterrupted;
31
- - (bool)isSupposedToRun;
32
28
 
33
29
  - (NSString *)attachSourceNode:(AVAudioSourceNode *)sourceNode format:(AVAudioFormat *)format;
34
30
  - (void)detachSourceNodeWithId:(NSString *)sourceNodeId;
@@ -36,10 +32,19 @@
36
32
  - (void)attachInputNode:(AVAudioSinkNode *)inputNode;
37
33
  - (void)detachInputNode;
38
34
 
39
- - (void)logAudioEngineState;
35
+ - (void)onInterruptionBegin;
36
+ - (void)onInterruptionEnd:(bool)shouldResume;
37
+
38
+ - (AudioEngineState)getState;
40
39
 
41
40
  - (bool)startIfNecessary;
42
- - (void)stopIfNecessary;
43
41
  - (void)pauseIfNecessary;
42
+ - (void)stopIfNecessary;
43
+
44
+ - (void)stopIfPossible;
45
+
46
+ - (void)restartAudioEngine;
47
+
48
+ - (void)logAudioEngineState;
44
49
 
45
50
  @end