react-native-audio-api 0.11.0-nightly-95f9c99-20251215 → 0.11.0-nightly-52d0b79-20251216
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +382 -39
- package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.h +45 -18
- package/android/src/main/cpp/audioapi/android/core/NativeAudioRecorder.hpp +9 -9
- package/android/src/main/cpp/audioapi/android/core/utils/AndroidFileWriterBackend.h +33 -0
- package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp +170 -0
- package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h +46 -0
- package/android/src/main/cpp/audioapi/android/core/utils/AudioDecoder.cpp +0 -1
- package/android/src/main/cpp/audioapi/android/core/utils/FileOptions.cpp +83 -0
- package/android/src/main/cpp/audioapi/android/core/utils/FileOptions.h +22 -0
- package/android/src/main/cpp/audioapi/android/core/utils/MiniaudioImplementation.cpp +8 -0
- package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/FFmpegFileWriter.cpp +493 -0
- package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/FFmpegFileWriter.h +70 -0
- package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/ptrs.hpp +56 -0
- package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/utils.cpp +114 -0
- package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/utils.h +34 -0
- package/android/src/main/cpp/audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.cpp +296 -0
- package/android/src/main/cpp/audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.h +40 -0
- package/android/src/main/cpp/audioapi/android/system/NativeFileInfo.hpp +32 -0
- package/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt +2 -0
- package/android/src/main/java/com/swmansion/audioapi/system/AudioFocusListener.kt +7 -3
- package/android/src/main/java/com/swmansion/audioapi/system/CentralizedForegroundService.kt +1 -0
- package/android/src/main/java/com/swmansion/audioapi/system/NativeFileInfo.kt +18 -0
- package/android/src/main/java/com/swmansion/audioapi/system/notification/PlaybackNotification.kt +1 -0
- package/android/src/main/java/com/swmansion/audioapi/system/notification/RecordingNotificationReceiver.kt +2 -0
- package/common/cpp/audioapi/AudioAPIModuleInstaller.h +3 -11
- package/common/cpp/audioapi/HostObjects/inputs/AudioRecorderHostObject.cpp +145 -16
- package/common/cpp/audioapi/HostObjects/inputs/AudioRecorderHostObject.h +21 -6
- package/common/cpp/audioapi/core/inputs/AudioRecorder.cpp +43 -60
- package/common/cpp/audioapi/core/inputs/AudioRecorder.h +53 -33
- package/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp +42 -14
- package/common/cpp/audioapi/core/sources/RecorderAdapterNode.h +12 -9
- package/common/cpp/audioapi/core/utils/AudioFileWriter.cpp +41 -0
- package/common/cpp/audioapi/core/utils/AudioFileWriter.h +44 -0
- package/common/cpp/audioapi/core/utils/AudioRecorderCallback.cpp +101 -0
- package/common/cpp/audioapi/core/utils/AudioRecorderCallback.h +52 -0
- package/common/cpp/audioapi/utils/AudioFileProperties.cpp +92 -0
- package/common/cpp/audioapi/utils/AudioFileProperties.h +76 -0
- package/common/cpp/audioapi/utils/Result.hpp +323 -0
- package/common/cpp/audioapi/utils/UnitConversion.h +9 -0
- package/ios/audioapi/ios/AudioAPIModule.mm +9 -14
- package/ios/audioapi/ios/core/IOSAudioPlayer.h +1 -1
- package/ios/audioapi/ios/core/IOSAudioPlayer.mm +7 -6
- package/ios/audioapi/ios/core/IOSAudioRecorder.h +39 -13
- package/ios/audioapi/ios/core/IOSAudioRecorder.mm +302 -26
- package/ios/audioapi/ios/core/NativeAudioPlayer.m +7 -11
- package/ios/audioapi/ios/core/NativeAudioRecorder.h +8 -9
- package/ios/audioapi/ios/core/NativeAudioRecorder.m +70 -76
- package/ios/audioapi/ios/core/utils/AudioDecoder.mm +1 -0
- package/ios/audioapi/ios/core/utils/FileOptions.h +31 -0
- package/ios/audioapi/ios/core/utils/FileOptions.mm +195 -0
- package/ios/audioapi/ios/core/utils/IOSFileWriter.h +53 -0
- package/ios/audioapi/ios/core/utils/IOSFileWriter.mm +239 -0
- package/ios/audioapi/ios/core/utils/IOSRecorderCallback.h +47 -0
- package/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm +185 -0
- package/ios/audioapi/ios/system/AudioEngine.h +21 -16
- package/ios/audioapi/ios/system/AudioEngine.mm +138 -130
- package/ios/audioapi/ios/system/AudioSessionManager.h +19 -9
- package/ios/audioapi/ios/system/AudioSessionManager.mm +250 -215
- package/ios/audioapi/ios/system/NotificationManager.mm +24 -42
- package/lib/commonjs/api.js +82 -109
- package/lib/commonjs/api.js.map +1 -1
- package/lib/commonjs/core/AudioRecorder.js +159 -13
- package/lib/commonjs/core/AudioRecorder.js.map +1 -1
- package/lib/commonjs/specs/NativeAudioAPIModule.js.map +1 -1
- package/lib/commonjs/system/notification/PlaybackNotificationManager.js +17 -14
- package/lib/commonjs/system/notification/PlaybackNotificationManager.js.map +1 -1
- package/lib/commonjs/system/notification/RecordingNotificationManager.js +22 -19
- package/lib/commonjs/system/notification/RecordingNotificationManager.js.map +1 -1
- package/lib/commonjs/system/notification/SimpleNotificationManager.js +16 -13
- package/lib/commonjs/system/notification/SimpleNotificationManager.js.map +1 -1
- package/lib/commonjs/types.js +39 -0
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/utils/filePresets.js +43 -0
- package/lib/commonjs/utils/filePresets.js.map +1 -0
- package/lib/commonjs/web-system/notification/PlaybackNotificationManager.js +6 -3
- package/lib/commonjs/web-system/notification/PlaybackNotificationManager.js.map +1 -1
- package/lib/commonjs/web-system/notification/RecordingNotificationManager.js +6 -3
- package/lib/commonjs/web-system/notification/RecordingNotificationManager.js.map +1 -1
- package/lib/module/api.js +5 -4
- package/lib/module/api.js.map +1 -1
- package/lib/module/core/AudioRecorder.js +159 -13
- package/lib/module/core/AudioRecorder.js.map +1 -1
- package/lib/module/specs/NativeAudioAPIModule.js.map +1 -1
- package/lib/module/system/notification/PlaybackNotificationManager.js +17 -14
- package/lib/module/system/notification/PlaybackNotificationManager.js.map +1 -1
- package/lib/module/system/notification/RecordingNotificationManager.js +22 -19
- package/lib/module/system/notification/RecordingNotificationManager.js.map +1 -1
- package/lib/module/system/notification/SimpleNotificationManager.js +16 -13
- package/lib/module/system/notification/SimpleNotificationManager.js.map +1 -1
- package/lib/module/types.js +38 -1
- package/lib/module/types.js.map +1 -1
- package/lib/module/utils/filePresets.js +39 -0
- package/lib/module/utils/filePresets.js.map +1 -0
- package/lib/module/web-system/notification/PlaybackNotificationManager.js +6 -3
- package/lib/module/web-system/notification/PlaybackNotificationManager.js.map +1 -1
- package/lib/module/web-system/notification/RecordingNotificationManager.js +6 -3
- package/lib/module/web-system/notification/RecordingNotificationManager.js.map +1 -1
- package/lib/typescript/api.d.ts +5 -4
- package/lib/typescript/api.d.ts.map +1 -1
- package/lib/typescript/core/AudioRecorder.d.ts +69 -7
- package/lib/typescript/core/AudioRecorder.d.ts.map +1 -1
- package/lib/typescript/events/types.d.ts +36 -2
- package/lib/typescript/events/types.d.ts.map +1 -1
- package/lib/typescript/interfaces.d.ts +24 -4
- package/lib/typescript/interfaces.d.ts.map +1 -1
- package/lib/typescript/specs/NativeAudioAPIModule.d.ts +1 -1
- package/lib/typescript/specs/NativeAudioAPIModule.d.ts.map +1 -1
- package/lib/typescript/system/notification/PlaybackNotificationManager.d.ts +4 -3
- package/lib/typescript/system/notification/PlaybackNotificationManager.d.ts.map +1 -1
- package/lib/typescript/system/notification/RecordingNotificationManager.d.ts +4 -3
- package/lib/typescript/system/notification/RecordingNotificationManager.d.ts.map +1 -1
- package/lib/typescript/system/notification/SimpleNotificationManager.d.ts +3 -2
- package/lib/typescript/system/notification/SimpleNotificationManager.d.ts.map +1 -1
- package/lib/typescript/system/notification/types.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +79 -3
- package/lib/typescript/types.d.ts.map +1 -1
- package/lib/typescript/utils/filePresets.d.ts +9 -0
- package/lib/typescript/utils/filePresets.d.ts.map +1 -0
- package/lib/typescript/web-system/notification/PlaybackNotificationManager.d.ts +4 -3
- package/lib/typescript/web-system/notification/PlaybackNotificationManager.d.ts.map +1 -1
- package/lib/typescript/web-system/notification/RecordingNotificationManager.d.ts +4 -3
- package/lib/typescript/web-system/notification/RecordingNotificationManager.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/AudioAPIModule/globals.d.ts +1 -2
- package/src/api.ts +8 -29
- package/src/core/AudioRecorder.ts +195 -23
- package/src/events/types.ts +40 -2
- package/src/interfaces.ts +34 -5
- package/src/specs/NativeAudioAPIModule.ts +2 -2
- package/src/system/notification/PlaybackNotificationManager.ts +20 -16
- package/src/system/notification/RecordingNotificationManager.ts +26 -21
- package/src/system/notification/SimpleNotificationManager.ts +18 -13
- package/src/system/notification/types.ts +1 -0
- package/src/types.ts +89 -3
- package/src/utils/filePresets.ts +47 -0
- package/src/web-system/notification/PlaybackNotificationManager.ts +9 -5
- package/src/web-system/notification/RecordingNotificationManager.ts +9 -5
|
@@ -4,42 +4,36 @@
|
|
|
4
4
|
|
|
5
5
|
@implementation NativeAudioRecorder
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
bufferLength:(int)bufferLength
|
|
9
|
-
sampleRate:(float)sampleRate
|
|
7
|
+
static inline uint32_t nextPowerOfTwo(uint32_t x)
|
|
10
8
|
{
|
|
11
|
-
if (
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
self.receiverBlock = [receiverBlock copy];
|
|
16
|
-
|
|
17
|
-
float devicePrefferedSampleRate = [[AVAudioSession sharedInstance] sampleRate];
|
|
9
|
+
if (x == 0) {
|
|
10
|
+
return 1;
|
|
11
|
+
}
|
|
18
12
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
24
|
-
|
|
21
|
+
return x;
|
|
22
|
+
}
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
atTimestamp:(const AudioTimeStamp *)timestamp
|
|
45
|
+
// Note: this method should be called only after the session is activated
|
|
46
|
+
- (AVAudioFormat *)getInputFormat
|
|
54
47
|
{
|
|
55
|
-
|
|
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
|
-
|
|
82
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
53
|
+
format = [[AVAudioFormat alloc]
|
|
54
|
+
initStandardFormatWithSampleRate:[[sessionManager getDevicePreferredSampleRate] doubleValue]
|
|
55
|
+
channels:[[sessionManager getDevicePreferredInputChannelCount]
|
|
56
|
+
intValue]];
|
|
57
|
+
}
|
|
94
58
|
|
|
95
|
-
|
|
59
|
+
return format;
|
|
60
|
+
}
|
|
96
61
|
|
|
97
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
118
|
-
|
|
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
|
-
|
|
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
|
|
@@ -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
|