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
@@ -4,42 +4,36 @@
4
4
 
5
5
  @implementation NativeAudioRecorder
6
6
 
7
- - (instancetype)initWithReceiverBlock:(AudioReceiverBlock)receiverBlock
8
- bufferLength:(int)bufferLength
9
- sampleRate:(float)sampleRate
7
+ static inline uint32_t nextPowerOfTwo(uint32_t x)
10
8
  {
11
- if (self = [super init]) {
12
- self.bufferLength = bufferLength;
13
- self.sampleRate = sampleRate;
14
-
15
- self.receiverBlock = [receiverBlock copy];
16
-
17
- float devicePrefferedSampleRate = [[AVAudioSession sharedInstance] sampleRate];
9
+ if (x == 0) {
10
+ return 1;
11
+ }
18
12
 
19
- if (!devicePrefferedSampleRate) {
20
- NSError *error;
21
- devicePrefferedSampleRate = sampleRate;
13
+ x--;
14
+ x |= x >> 1;
15
+ x |= x >> 2;
16
+ x |= x >> 4;
17
+ x |= x >> 8;
18
+ x |= x >> 16;
19
+ x++;
22
20
 
23
- [[AVAudioSession sharedInstance] setPreferredSampleRate:sampleRate error:&error];
24
- }
21
+ return x;
22
+ }
25
23
 
26
- self.inputFormat = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32
27
- sampleRate:devicePrefferedSampleRate
28
- channels:1
29
- interleaved:NO];
30
- self.outputFormat = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32
31
- sampleRate:sampleRate
32
- channels:1
33
- interleaved:NO];
34
- self.audioConverter = [[AVAudioConverter alloc] initFromFormat:self.inputFormat
35
- toFormat:self.outputFormat];
24
+ - (instancetype)initWithReceiverBlock:(AudioReceiverBlock)receiverBlock
25
+ {
26
+ if (self = [super init]) {
27
+ self.receiverBlock = [receiverBlock copy];
36
28
 
37
29
  __weak typeof(self) weakSelf = self;
38
30
  self.receiverSinkBlock = ^OSStatus(
39
31
  const AudioTimeStamp *_Nonnull timestamp,
40
32
  AVAudioFrameCount frameCount,
41
33
  const AudioBufferList *_Nonnull inputData) {
42
- return [weakSelf processAudioInput:inputData withFrameCount:frameCount atTimestamp:timestamp];
34
+ weakSelf.receiverBlock(inputData, frameCount);
35
+
36
+ return kAudioServicesNoError;
43
37
  };
44
38
 
45
39
  self.sinkNode = [[AVAudioSinkNode alloc] initWithReceiverBlock:self.receiverSinkBlock];
@@ -48,58 +42,36 @@
48
42
  return self;
49
43
  }
50
44
 
51
- - (OSStatus)processAudioInput:(const AudioBufferList *)inputData
52
- withFrameCount:(AVAudioFrameCount)frameCount
53
- atTimestamp:(const AudioTimeStamp *)timestamp
45
+ // Note: this method should be called only after the session is activated
46
+ - (AVAudioFormat *)getInputFormat
54
47
  {
55
- float inputSampleRate = self.inputFormat.sampleRate;
56
- float outputSampleRate = self.outputFormat.sampleRate;
57
-
58
- if (inputSampleRate != outputSampleRate) {
59
- AVAudioPCMBuffer *inputBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:self.inputFormat
60
- frameCapacity:frameCount];
61
- memcpy(
62
- inputBuffer.mutableAudioBufferList->mBuffers[0].mData,
63
- inputData->mBuffers[0].mData,
64
- inputData->mBuffers[0].mDataByteSize);
65
- inputBuffer.frameLength = frameCount;
66
-
67
- int outputFrameCount = frameCount * outputSampleRate / inputSampleRate;
68
-
69
- AVAudioPCMBuffer *outputBuffer =
70
- [[AVAudioPCMBuffer alloc] initWithPCMFormat:self.audioConverter.outputFormat
71
- frameCapacity:outputFrameCount];
72
-
73
- NSError *error = nil;
74
- AVAudioConverterInputBlock inputBlock = ^AVAudioBuffer *_Nullable(
75
- AVAudioPacketCount inNumberOfPackets, AVAudioConverterInputStatus *outStatus)
76
- {
77
- *outStatus = AVAudioConverterInputStatus_HaveData;
78
- return inputBuffer;
79
- };
48
+ AVAudioFormat *format = [AudioEngine.sharedInstance.audioEngine.inputNode inputFormatForBus:0];
80
49
 
81
- /// IMPORTANT: AVAudioConverter leaks memory without autorelease pool
82
- /// more details here:
83
- /// https://github.com/poneciak57/AVAudioConverter-memory-leak-repro-electric-boogaloo
84
- /// we can try to remove it in the future or refactor to reuse buffers to
85
- /// minimize allocations
86
- @autoreleasepool {
87
- [self.audioConverter convertToBuffer:outputBuffer error:&error withInputFromBlock:inputBlock];
88
- }
50
+ if (format.sampleRate == 0 || format.channelCount == 0) {
51
+ AudioSessionManager *sessionManager = [AudioSessionManager sharedInstance];
89
52
 
90
- if (error) {
91
- NSLog(@"Error during audio conversion: %@", error.localizedDescription);
92
- return kAudioServicesBadSpecifierSizeError;
93
- }
53
+ format = [[AVAudioFormat alloc]
54
+ initStandardFormatWithSampleRate:[[sessionManager getDevicePreferredSampleRate] doubleValue]
55
+ channels:[[sessionManager getDevicePreferredInputChannelCount]
56
+ intValue]];
57
+ }
94
58
 
95
- self.receiverBlock(outputBuffer.audioBufferList, outputBuffer.frameLength);
59
+ return format;
60
+ }
96
61
 
97
- return kAudioServicesNoError;
98
- }
62
+ - (int)getBufferSize
63
+ {
64
+ // NOTE: this method should be called only after the session is activated
65
+ AVAudioSession *audioSession = [AVAudioSession sharedInstance];
99
66
 
100
- self.receiverBlock(inputData, frameCount);
67
+ // TMPfix: it seems that buffer duration in some cases (background/device change) can switch
68
+ // to longer values, exceeding buffer size predicted after session start
69
+ // since it is just a couple of buffers we can set min value of 200ms
70
+ // to enforce we always have enough frames allocated to pass further down the pipeline
71
+ float bufferDuration = MAX(audioSession.IOBufferDuration, 0.2);
101
72
 
102
- return kAudioServicesNoError;
73
+ // IOS returns buffer duration rounded, but expects the buffer size to be power of two in runtime
74
+ return nextPowerOfTwo(ceil(bufferDuration * audioSession.sampleRate));
103
75
  }
104
76
 
105
77
  - (void)start
@@ -113,10 +85,9 @@
113
85
  // we haven't break rules of at runtime modifications from docs
114
86
  // https://developer.apple.com/documentation/avfaudio/avaudioengine?language=objc
115
87
  //
116
- // Currently we are restarting because we do not see any significant
117
- // performance issue and case when you will need to start and stop recorder
118
- // very frequently
119
- [audioEngine stopEngine];
88
+ // Currently we are restarting because we do not see any significant performance issue and case when
89
+ // you will need to start and stop recorder very frequently
90
+ [audioEngine stopIfNecessary];
120
91
  [audioEngine attachInputNode:self.sinkNode];
121
92
  [audioEngine startIfNecessary];
122
93
  }
@@ -125,8 +96,31 @@
125
96
  {
126
97
  AudioEngine *audioEngine = [AudioEngine sharedInstance];
127
98
  assert(audioEngine != nil);
99
+ [audioEngine stopIfPossible];
128
100
  [audioEngine detachInputNode];
129
- [audioEngine stopIfNecessary];
101
+
102
+ // This makes sure that the engine releases the input properly when we no longer need it
103
+ // (i.e. no more misleading dot)
104
+ // Restart only if is not running to avoid interruptions of playback
105
+ if ([audioEngine getState] != AudioEngineStateRunning) {
106
+ [audioEngine restartAudioEngine];
107
+ }
108
+ }
109
+
110
+ - (void)pause
111
+ {
112
+ AudioEngine *audioEngine = [AudioEngine sharedInstance];
113
+ assert(audioEngine != nil);
114
+
115
+ [audioEngine pauseIfNecessary];
116
+ }
117
+
118
+ - (void)resume
119
+ {
120
+ AudioEngine *audioEngine = [AudioEngine sharedInstance];
121
+ assert(audioEngine != nil);
122
+
123
+ [audioEngine startIfNecessary];
130
124
  }
131
125
 
132
126
  - (void)cleanup
@@ -61,6 +61,7 @@ std::shared_ptr<AudioBuffer> AudioDecoder::makeAudioBufferFromFloatBuffer(
61
61
  channelData[i] = buffer[i * outputChannels + ch];
62
62
  }
63
63
  }
64
+
64
65
  return std::make_shared<AudioBuffer>(audioBus);
65
66
  }
66
67
 
@@ -0,0 +1,31 @@
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
+ namespace audioapi {
10
+
11
+ class AudioFileProperties;
12
+
13
+ namespace ios::fileoptions {
14
+
15
+ AudioFormatID getFormat(const std::shared_ptr<AudioFileProperties> &properties);
16
+ NSInteger getQuality(const std::shared_ptr<AudioFileProperties> &properties);
17
+ NSInteger getFlacCompressionLevel(const std::shared_ptr<AudioFileProperties> &properties);
18
+ NSString *getFileExtension(const std::shared_ptr<AudioFileProperties> &properties);
19
+ NSInteger getBitDepth(const std::shared_ptr<AudioFileProperties> &properties);
20
+ float getSampleRate(const std::shared_ptr<AudioFileProperties> &properties);
21
+
22
+ NSDictionary *getFileSettings(const std::shared_ptr<AudioFileProperties> &properties);
23
+ NSURL *getFileURL(const std::shared_ptr<AudioFileProperties> &properties);
24
+ NSSearchPathDirectory getDirectory(const std::shared_ptr<AudioFileProperties> &properties);
25
+
26
+ NSString *getDateString();
27
+ NSString *getTimestampString();
28
+
29
+ } // namespace ios::fileoptions
30
+
31
+ } // 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