react-native-audio-api 0.11.0-nightly-95f9c99-20251215 → 0.11.0-nightly-dd83923-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 (138) 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/android/src/oldarch/NativeAudioAPIModuleSpec.java +100 -80
  26. package/common/cpp/audioapi/AudioAPIModuleInstaller.h +3 -11
  27. package/common/cpp/audioapi/HostObjects/inputs/AudioRecorderHostObject.cpp +145 -16
  28. package/common/cpp/audioapi/HostObjects/inputs/AudioRecorderHostObject.h +21 -6
  29. package/common/cpp/audioapi/core/inputs/AudioRecorder.cpp +43 -60
  30. package/common/cpp/audioapi/core/inputs/AudioRecorder.h +53 -33
  31. package/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp +42 -14
  32. package/common/cpp/audioapi/core/sources/RecorderAdapterNode.h +12 -9
  33. package/common/cpp/audioapi/core/utils/AudioFileWriter.cpp +41 -0
  34. package/common/cpp/audioapi/core/utils/AudioFileWriter.h +44 -0
  35. package/common/cpp/audioapi/core/utils/AudioRecorderCallback.cpp +101 -0
  36. package/common/cpp/audioapi/core/utils/AudioRecorderCallback.h +52 -0
  37. package/common/cpp/audioapi/utils/AudioFileProperties.cpp +92 -0
  38. package/common/cpp/audioapi/utils/AudioFileProperties.h +76 -0
  39. package/common/cpp/audioapi/utils/Result.hpp +323 -0
  40. package/common/cpp/audioapi/utils/UnitConversion.h +9 -0
  41. package/ios/audioapi/ios/AudioAPIModule.mm +9 -14
  42. package/ios/audioapi/ios/core/IOSAudioPlayer.h +1 -1
  43. package/ios/audioapi/ios/core/IOSAudioPlayer.mm +7 -6
  44. package/ios/audioapi/ios/core/IOSAudioRecorder.h +39 -13
  45. package/ios/audioapi/ios/core/IOSAudioRecorder.mm +302 -26
  46. package/ios/audioapi/ios/core/NativeAudioPlayer.m +7 -11
  47. package/ios/audioapi/ios/core/NativeAudioRecorder.h +8 -9
  48. package/ios/audioapi/ios/core/NativeAudioRecorder.m +70 -76
  49. package/ios/audioapi/ios/core/utils/AudioDecoder.mm +1 -0
  50. package/ios/audioapi/ios/core/utils/FileOptions.h +33 -0
  51. package/ios/audioapi/ios/core/utils/FileOptions.mm +195 -0
  52. package/ios/audioapi/ios/core/utils/IOSFileWriter.h +53 -0
  53. package/ios/audioapi/ios/core/utils/IOSFileWriter.mm +239 -0
  54. package/ios/audioapi/ios/core/utils/IOSRecorderCallback.h +47 -0
  55. package/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm +185 -0
  56. package/ios/audioapi/ios/system/AudioEngine.h +21 -16
  57. package/ios/audioapi/ios/system/AudioEngine.mm +138 -130
  58. package/ios/audioapi/ios/system/AudioSessionManager.h +19 -9
  59. package/ios/audioapi/ios/system/AudioSessionManager.mm +250 -215
  60. package/ios/audioapi/ios/system/NotificationManager.mm +24 -42
  61. package/lib/commonjs/api.js +82 -109
  62. package/lib/commonjs/api.js.map +1 -1
  63. package/lib/commonjs/core/AudioRecorder.js +159 -13
  64. package/lib/commonjs/core/AudioRecorder.js.map +1 -1
  65. package/lib/commonjs/specs/NativeAudioAPIModule.js.map +1 -1
  66. package/lib/commonjs/system/notification/PlaybackNotificationManager.js +17 -14
  67. package/lib/commonjs/system/notification/PlaybackNotificationManager.js.map +1 -1
  68. package/lib/commonjs/system/notification/RecordingNotificationManager.js +22 -19
  69. package/lib/commonjs/system/notification/RecordingNotificationManager.js.map +1 -1
  70. package/lib/commonjs/system/notification/SimpleNotificationManager.js +16 -13
  71. package/lib/commonjs/system/notification/SimpleNotificationManager.js.map +1 -1
  72. package/lib/commonjs/types.js +39 -0
  73. package/lib/commonjs/types.js.map +1 -1
  74. package/lib/commonjs/utils/filePresets.js +43 -0
  75. package/lib/commonjs/utils/filePresets.js.map +1 -0
  76. package/lib/commonjs/web-system/notification/PlaybackNotificationManager.js +6 -3
  77. package/lib/commonjs/web-system/notification/PlaybackNotificationManager.js.map +1 -1
  78. package/lib/commonjs/web-system/notification/RecordingNotificationManager.js +6 -3
  79. package/lib/commonjs/web-system/notification/RecordingNotificationManager.js.map +1 -1
  80. package/lib/module/api.js +5 -4
  81. package/lib/module/api.js.map +1 -1
  82. package/lib/module/core/AudioRecorder.js +159 -13
  83. package/lib/module/core/AudioRecorder.js.map +1 -1
  84. package/lib/module/specs/NativeAudioAPIModule.js.map +1 -1
  85. package/lib/module/system/notification/PlaybackNotificationManager.js +17 -14
  86. package/lib/module/system/notification/PlaybackNotificationManager.js.map +1 -1
  87. package/lib/module/system/notification/RecordingNotificationManager.js +22 -19
  88. package/lib/module/system/notification/RecordingNotificationManager.js.map +1 -1
  89. package/lib/module/system/notification/SimpleNotificationManager.js +16 -13
  90. package/lib/module/system/notification/SimpleNotificationManager.js.map +1 -1
  91. package/lib/module/types.js +38 -1
  92. package/lib/module/types.js.map +1 -1
  93. package/lib/module/utils/filePresets.js +39 -0
  94. package/lib/module/utils/filePresets.js.map +1 -0
  95. package/lib/module/web-system/notification/PlaybackNotificationManager.js +6 -3
  96. package/lib/module/web-system/notification/PlaybackNotificationManager.js.map +1 -1
  97. package/lib/module/web-system/notification/RecordingNotificationManager.js +6 -3
  98. package/lib/module/web-system/notification/RecordingNotificationManager.js.map +1 -1
  99. package/lib/typescript/api.d.ts +5 -4
  100. package/lib/typescript/api.d.ts.map +1 -1
  101. package/lib/typescript/core/AudioRecorder.d.ts +69 -7
  102. package/lib/typescript/core/AudioRecorder.d.ts.map +1 -1
  103. package/lib/typescript/events/types.d.ts +36 -2
  104. package/lib/typescript/events/types.d.ts.map +1 -1
  105. package/lib/typescript/interfaces.d.ts +24 -4
  106. package/lib/typescript/interfaces.d.ts.map +1 -1
  107. package/lib/typescript/specs/NativeAudioAPIModule.d.ts +1 -1
  108. package/lib/typescript/specs/NativeAudioAPIModule.d.ts.map +1 -1
  109. package/lib/typescript/system/notification/PlaybackNotificationManager.d.ts +4 -3
  110. package/lib/typescript/system/notification/PlaybackNotificationManager.d.ts.map +1 -1
  111. package/lib/typescript/system/notification/RecordingNotificationManager.d.ts +4 -3
  112. package/lib/typescript/system/notification/RecordingNotificationManager.d.ts.map +1 -1
  113. package/lib/typescript/system/notification/SimpleNotificationManager.d.ts +3 -2
  114. package/lib/typescript/system/notification/SimpleNotificationManager.d.ts.map +1 -1
  115. package/lib/typescript/system/notification/types.d.ts.map +1 -1
  116. package/lib/typescript/types.d.ts +79 -3
  117. package/lib/typescript/types.d.ts.map +1 -1
  118. package/lib/typescript/utils/filePresets.d.ts +9 -0
  119. package/lib/typescript/utils/filePresets.d.ts.map +1 -0
  120. package/lib/typescript/web-system/notification/PlaybackNotificationManager.d.ts +4 -3
  121. package/lib/typescript/web-system/notification/PlaybackNotificationManager.d.ts.map +1 -1
  122. package/lib/typescript/web-system/notification/RecordingNotificationManager.d.ts +4 -3
  123. package/lib/typescript/web-system/notification/RecordingNotificationManager.d.ts.map +1 -1
  124. package/package.json +4 -4
  125. package/src/AudioAPIModule/globals.d.ts +1 -2
  126. package/src/api.ts +8 -29
  127. package/src/core/AudioRecorder.ts +195 -23
  128. package/src/events/types.ts +40 -2
  129. package/src/interfaces.ts +34 -5
  130. package/src/specs/NativeAudioAPIModule.ts +2 -2
  131. package/src/system/notification/PlaybackNotificationManager.ts +20 -16
  132. package/src/system/notification/RecordingNotificationManager.ts +26 -21
  133. package/src/system/notification/SimpleNotificationManager.ts +18 -13
  134. package/src/system/notification/types.ts +1 -0
  135. package/src/types.ts +89 -3
  136. package/src/utils/filePresets.ts +47 -0
  137. package/src/web-system/notification/PlaybackNotificationManager.ts +9 -5
  138. package/src/web-system/notification/RecordingNotificationManager.ts +9 -5
@@ -0,0 +1,33 @@
1
+ #pragma once
2
+
3
+ #ifndef __OBJC__ // when compiled as C++
4
+ typedef struct objc_object AVAudioFile;
5
+ typedef struct objc_object NSURL;
6
+ typedef struct objc_object AudioBufferList;
7
+ #endif // __OBJC__
8
+
9
+ #include <memory>
10
+
11
+ namespace audioapi {
12
+
13
+ class AudioFileProperties;
14
+
15
+ namespace ios::fileoptions {
16
+
17
+ AudioFormatID getFormat(const std::shared_ptr<AudioFileProperties> &properties);
18
+ NSInteger getQuality(const std::shared_ptr<AudioFileProperties> &properties);
19
+ NSInteger getFlacCompressionLevel(const std::shared_ptr<AudioFileProperties> &properties);
20
+ NSString *getFileExtension(const std::shared_ptr<AudioFileProperties> &properties);
21
+ NSInteger getBitDepth(const std::shared_ptr<AudioFileProperties> &properties);
22
+ float getSampleRate(const std::shared_ptr<AudioFileProperties> &properties);
23
+
24
+ NSDictionary *getFileSettings(const std::shared_ptr<AudioFileProperties> &properties);
25
+ NSURL *getFileURL(const std::shared_ptr<AudioFileProperties> &properties);
26
+ NSSearchPathDirectory getDirectory(const std::shared_ptr<AudioFileProperties> &properties);
27
+
28
+ NSString *getDateString();
29
+ NSString *getTimestampString();
30
+
31
+ } // namespace ios::fileoptions
32
+
33
+ } // namespace audioapi
@@ -0,0 +1,195 @@
1
+ #import <AVFoundation/AVFoundation.h>
2
+ #import <Foundation/Foundation.h>
3
+
4
+ #include <audioapi/ios/core/utils/FileOptions.h>
5
+ #include <audioapi/utils/AudioFileProperties.h>
6
+
7
+ namespace audioapi::ios::fileoptions {
8
+
9
+ /// @brief Maps AudioFileProperties to iOS AVFoundation AudioFormatID.
10
+ /// @param properties Shared pointer to AudioFileProperties.
11
+ /// @returns Corresponding AudioFormatID for AVFoundation.
12
+ AudioFormatID getFormat(const std::shared_ptr<AudioFileProperties> &properties)
13
+ {
14
+ switch (properties->format) {
15
+ case AudioFileProperties::Format::WAV:
16
+ return kAudioFormatLinearPCM;
17
+
18
+ case AudioFileProperties::Format::CAF:
19
+ return kAudioFormatLinearPCM;
20
+
21
+ case AudioFileProperties::Format::M4A:
22
+ return kAudioFormatMPEG4AAC;
23
+
24
+ case AudioFileProperties::Format::FLAC:
25
+ return kAudioFormatFLAC;
26
+
27
+ default:
28
+ return kAudioFormatLinearPCM;
29
+ }
30
+ }
31
+
32
+ /// @brief Maps AudioFileProperties to iOS AVFoundation audio quality settings.
33
+ /// @param properties Shared pointer to AudioFileProperties.
34
+ /// @returns Corresponding NSInteger value for AVAudioQuality.
35
+ NSInteger getQuality(const std::shared_ptr<AudioFileProperties> &properties)
36
+ {
37
+ switch (properties->iosAudioQuality) {
38
+ case AudioFileProperties::IOSAudioQuality::Min:
39
+ return AVAudioQualityMin;
40
+
41
+ case AudioFileProperties::IOSAudioQuality::Low:
42
+ return AVAudioQualityLow;
43
+
44
+ case AudioFileProperties::IOSAudioQuality::Medium:
45
+ return AVAudioQualityMedium;
46
+
47
+ case AudioFileProperties::IOSAudioQuality::High:
48
+ return AVAudioQualityHigh;
49
+
50
+ case AudioFileProperties::IOSAudioQuality::Max:
51
+ return AVAudioQualityMax;
52
+
53
+ default:
54
+ return AVAudioQualityMedium;
55
+ }
56
+ }
57
+
58
+ /// @brief Retrieves the FLAC compression level from AudioFileProperties.
59
+ /// @param properties Shared pointer to AudioFileProperties.
60
+ /// @returns NSInteger representing the FLAC compression level.
61
+ NSInteger getFlacCompressionLevel(const std::shared_ptr<AudioFileProperties> &properties)
62
+ {
63
+ return properties->flacCompressionLevel;
64
+ }
65
+
66
+ /// @brief Retrieves the file extension based on AudioFileProperties format.
67
+ /// @param properties Shared pointer to AudioFileProperties.
68
+ /// @returns NSString representing the file extension.
69
+ NSString *getFileExtension(const std::shared_ptr<AudioFileProperties> &properties)
70
+ {
71
+ switch (properties->format) {
72
+ case AudioFileProperties::Format::WAV:
73
+ return @"wav";
74
+
75
+ case AudioFileProperties::Format::CAF:
76
+ return @"caf";
77
+
78
+ case AudioFileProperties::Format::M4A:
79
+ return @"m4a";
80
+
81
+ case AudioFileProperties::Format::FLAC:
82
+ return @"flac";
83
+
84
+ default:
85
+ return @"wav";
86
+ }
87
+ }
88
+
89
+ /// @brief Retrieves the bit depth from AudioFileProperties.
90
+ /// @param properties Shared pointer to AudioFileProperties.
91
+ /// @returns NSInteger representing the bit depth.
92
+ NSInteger getBitDepth(const std::shared_ptr<AudioFileProperties> &properties)
93
+ {
94
+ switch (properties->bitDepth) {
95
+ case AudioFileProperties::BitDepth::Bit16:
96
+ return 16;
97
+
98
+ case AudioFileProperties::BitDepth::Bit24:
99
+ return 24;
100
+
101
+ case AudioFileProperties::BitDepth::Bit32:
102
+ return 32;
103
+
104
+ default:
105
+ return 32;
106
+ }
107
+ }
108
+
109
+ /// @brief Constructs AVFoundation file settings dictionary from AudioFileProperties.
110
+ /// @param properties Shared pointer to AudioFileProperties.
111
+ /// @returns NSDictionary containing AVFoundation audio file settings.
112
+ NSDictionary *getFileSettings(const std::shared_ptr<AudioFileProperties> &properties)
113
+ {
114
+ AudioFormatID format = getFormat(properties);
115
+ NSMutableDictionary *settings = [NSMutableDictionary dictionary];
116
+
117
+ settings[AVFormatIDKey] = @(format);
118
+ settings[AVSampleRateKey] = @(properties->sampleRate);
119
+ settings[AVNumberOfChannelsKey] = @(properties->channelCount);
120
+ settings[AVEncoderAudioQualityKey] = @(getQuality(properties));
121
+
122
+ if (format == kAudioFormatMPEG4AAC) {
123
+ settings[AVEncoderBitRateKey] = @(properties->bitRate);
124
+ }
125
+
126
+ if (format == kAudioFormatLinearPCM) {
127
+ NSInteger bitDepth = getBitDepth(properties);
128
+
129
+ settings[AVLinearPCMBitDepthKey] = @(bitDepth);
130
+ settings[AVLinearPCMIsFloatKey] = @(bitDepth == 32);
131
+ settings[AVLinearPCMIsBigEndianKey] = @(NO);
132
+ settings[AVLinearPCMIsNonInterleaved] = @(NO);
133
+ }
134
+
135
+ if (format == kAudioFormatFLAC) {
136
+ settings[@"FLACCompressionLevel"] = @(getFlacCompressionLevel(properties));
137
+ }
138
+
139
+ return settings;
140
+ }
141
+
142
+ NSURL *getFileURL(const std::shared_ptr<AudioFileProperties> &properties)
143
+ {
144
+ NSError *error = nil;
145
+
146
+ NSSearchPathDirectory directory = getDirectory(properties);
147
+ NSString *subDirectory = [NSString stringWithUTF8String:properties->subDirectory.c_str()];
148
+
149
+ NSURL *baseURL = [[[NSFileManager defaultManager] URLsForDirectory:directory
150
+ inDomains:NSUserDomainMask] firstObject];
151
+ NSURL *directoryURL = [baseURL URLByAppendingPathComponent:subDirectory isDirectory:YES];
152
+
153
+ [[NSFileManager defaultManager] createDirectoryAtURL:directoryURL
154
+ withIntermediateDirectories:YES
155
+ attributes:nil
156
+ error:&error];
157
+
158
+ if (error != nil) {
159
+ NSLog(@"Error creating directory for audio recordings: %@", [error debugDescription]);
160
+ directoryURL = baseURL;
161
+ }
162
+
163
+ NSString *fileNamePrefix = [NSString stringWithUTF8String:properties->fileNamePrefix.c_str()];
164
+ NSString *timestamp = getTimestampString();
165
+ NSString *fileExtension = getFileExtension(properties);
166
+
167
+ NSString *fileName =
168
+ [NSString stringWithFormat:@"%@_%@.%@", fileNamePrefix, timestamp, fileExtension];
169
+ return [directoryURL URLByAppendingPathComponent:fileName];
170
+ }
171
+
172
+ NSSearchPathDirectory getDirectory(const std::shared_ptr<AudioFileProperties> &properties)
173
+ {
174
+ switch (properties->directory) {
175
+ case AudioFileProperties::FileDirectory::Document:
176
+ return NSDocumentDirectory;
177
+
178
+ case AudioFileProperties::FileDirectory::Cache:
179
+ return NSCachesDirectory;
180
+
181
+ default:
182
+ return NSCachesDirectory;
183
+ }
184
+ }
185
+
186
+ NSString *getTimestampString()
187
+ {
188
+ NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
189
+ fmt.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
190
+ fmt.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"]; // or local if you prefer
191
+ fmt.dateFormat = @"yyyyMMdd_HHmmss_SSS";
192
+ return [fmt stringFromDate:[NSDate date]];
193
+ }
194
+
195
+ } // namespace audioapi::ios::fileoptions
@@ -0,0 +1,53 @@
1
+ #pragma once
2
+
3
+ #include <audioapi/core/utils/AudioFileWriter.h>
4
+ #include <audioapi/utils/Result.hpp>
5
+ #include <memory>
6
+ #include <string>
7
+ #include <tuple>
8
+
9
+ #ifndef __OBJC__ // when compiled as C++
10
+ typedef struct objc_object NSURL;
11
+ typedef struct objc_object NSString;
12
+ typedef struct objc_object AVAudioFile;
13
+ typedef struct objc_object AVAudioFormat;
14
+ typedef struct objc_object AudioBufferList;
15
+ typedef struct objc_object AVAudioConverter;
16
+ #endif // __OBJC__
17
+
18
+ namespace audioapi {
19
+
20
+ class AudioFileProperties;
21
+ class AudioEventHandlerRegistry;
22
+
23
+ class IOSFileWriter : public AudioFileWriter {
24
+ public:
25
+ IOSFileWriter(
26
+ const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry,
27
+ const std::shared_ptr<AudioFileProperties> &fileProperties);
28
+ ~IOSFileWriter();
29
+
30
+ Result<std::string, std::string> openFile(
31
+ AVAudioFormat *bufferFormat,
32
+ size_t maxInputBufferLength);
33
+ Result<std::tuple<double, double>, std::string> closeFile() override;
34
+
35
+ bool writeAudioData(const AudioBufferList *audioBufferList, int numFrames);
36
+ double getCurrentDuration() const override;
37
+
38
+ std::string getFilePath() const override;
39
+
40
+ protected:
41
+ size_t converterInputBufferSize_;
42
+ size_t converterOutputBufferSize_;
43
+
44
+ AVAudioFile *audioFile_;
45
+ AVAudioFormat *bufferFormat_;
46
+ AVAudioConverter *converter_;
47
+ NSURL *fileURL_;
48
+
49
+ AVAudioPCMBuffer *converterInputBuffer_;
50
+ AVAudioPCMBuffer *converterOutputBuffer_;
51
+ };
52
+
53
+ } // namespace audioapi
@@ -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