react-native-audio-api 0.11.0-alpha.0 → 0.11.0-alpha.1

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 (38) hide show
  1. package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +77 -29
  2. package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.h +17 -4
  3. package/android/src/main/cpp/audioapi/android/core/utils/AndroidFileWriterBackend.h +6 -2
  4. package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp +187 -0
  5. package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h +57 -0
  6. package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/FFmpegFileWriter.cpp +22 -24
  7. package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/FFmpegFileWriter.h +3 -2
  8. package/android/src/main/cpp/audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.cpp +40 -8
  9. package/android/src/main/cpp/audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.h +3 -2
  10. package/common/cpp/audioapi/HostObjects/inputs/AudioRecorderHostObject.cpp +24 -7
  11. package/common/cpp/audioapi/HostObjects/inputs/AudioRecorderHostObject.h +3 -0
  12. package/common/cpp/audioapi/core/inputs/AudioRecorder.cpp +16 -115
  13. package/common/cpp/audioapi/core/inputs/AudioRecorder.h +35 -32
  14. package/ios/audioapi/ios/core/IOSAudioFileOptions.h +1 -0
  15. package/ios/audioapi/ios/core/IOSAudioFileOptions.mm +5 -0
  16. package/ios/audioapi/ios/core/IOSAudioFileWriter.h +16 -3
  17. package/ios/audioapi/ios/core/IOSAudioFileWriter.mm +86 -50
  18. package/ios/audioapi/ios/core/IOSAudioRecorder.h +11 -2
  19. package/ios/audioapi/ios/core/IOSAudioRecorder.mm +76 -31
  20. package/ios/audioapi/ios/core/IOSRecorderCallback.h +57 -0
  21. package/ios/audioapi/ios/core/IOSRecorderCallback.mm +189 -0
  22. package/ios/audioapi/ios/core/NativeAudioRecorder.h +2 -3
  23. package/ios/audioapi/ios/core/NativeAudioRecorder.m +26 -0
  24. package/ios/audioapi/ios/core/utils/AudioDecoder.mm +1 -0
  25. package/lib/commonjs/core/AudioRecorder.js +14 -1
  26. package/lib/commonjs/core/AudioRecorder.js.map +1 -1
  27. package/lib/module/core/AudioRecorder.js +14 -1
  28. package/lib/module/core/AudioRecorder.js.map +1 -1
  29. package/lib/typescript/core/AudioRecorder.d.ts +6 -3
  30. package/lib/typescript/core/AudioRecorder.d.ts.map +1 -1
  31. package/lib/typescript/interfaces.d.ts +5 -3
  32. package/lib/typescript/interfaces.d.ts.map +1 -1
  33. package/lib/typescript/types.d.ts +5 -0
  34. package/lib/typescript/types.d.ts.map +1 -1
  35. package/package.json +1 -1
  36. package/src/core/AudioRecorder.ts +27 -10
  37. package/src/interfaces.ts +6 -2
  38. package/src/types.ts +6 -0
@@ -4,6 +4,8 @@
4
4
  #include <audioapi/ios/core/IOSAudioFileOptions.h>
5
5
  #include <audioapi/ios/core/IOSAudioFileWriter.h>
6
6
 
7
+ constexpr double BYTES_TO_MB = 1024.0 * 1024.0;
8
+
7
9
  namespace audioapi {
8
10
  IOSAudioFileWriter::IOSAudioFileWriter(float sampleRate, size_t channelCount, size_t bitRate, size_t iosFlags)
9
11
  {
@@ -18,13 +20,14 @@ IOSAudioFileWriter::~IOSAudioFileWriter()
18
20
  bufferFormat_ = nil;
19
21
  }
20
22
 
21
- void IOSAudioFileWriter::openFile(AVAudioFormat *bufferFormat)
23
+ std::string IOSAudioFileWriter::openFile(AVAudioFormat *bufferFormat, size_t maxInputBufferLength)
22
24
  {
23
25
  @autoreleasepool {
24
26
  if (audioFile_ != nil) {
25
27
  NSLog(@"⚠️ createFileForWriting: currentAudioFile_ already exists");
26
- return;
28
+ return "";
27
29
  }
30
+ framesWritten_.store(0);
28
31
 
29
32
  bufferFormat_ = bufferFormat;
30
33
 
@@ -44,28 +47,59 @@ void IOSAudioFileWriter::openFile(AVAudioFormat *bufferFormat)
44
47
  converter_.sampleRateConverterQuality = AVAudioQualityMax;
45
48
  converter_.primeMethod = AVAudioConverterPrimeMethod_None;
46
49
 
47
- NSLog(@"buferFormat: %@ fileFormat: %@", bufferFormat, [audioFile_ processingFormat]);
50
+ converterInputBufferSize_ = maxInputBufferLength;
51
+ converterOutputBufferSize_ = std::max(
52
+ (double)maxInputBufferLength, fileOptions_->getSampleRate() / bufferFormat.sampleRate * maxInputBufferLength);
53
+
54
+ converterInputBuffer_ = [[AVAudioPCMBuffer alloc] initWithPCMFormat:bufferFormat
55
+ frameCapacity:(AVAudioFrameCount)maxInputBufferLength];
56
+ converterOutputBuffer_ = [[AVAudioPCMBuffer alloc] initWithPCMFormat:[audioFile_ processingFormat]
57
+ frameCapacity:(AVAudioFrameCount)maxInputBufferLength];
48
58
 
49
59
  if (error != nil || audioFile_ == nil) {
50
60
  NSLog(@"Error creating audio file for writing: %@", [error debugDescription]);
51
61
  audioFile_ = nil;
62
+
63
+ return "";
52
64
  }
65
+
66
+ return [[fileURL_ path] UTF8String];
53
67
  }
54
68
  }
55
69
 
56
- std::string IOSAudioFileWriter::closeFile()
70
+ std::tuple<double, double> IOSAudioFileWriter::closeFile()
57
71
  {
58
72
  @autoreleasepool {
73
+ NSError *error;
59
74
  std::string filePath = [[fileURL_ path] UTF8String];
60
75
 
61
76
  if (audioFile_ == nil) {
62
- return "";
77
+ return {0, 0};
63
78
  }
64
79
 
65
80
  // AVAudioFile automatically finalizes the file when deallocated
66
81
  audioFile_ = nil;
82
+
83
+ double fileDuration = CMTimeGetSeconds([[AVURLAsset URLAssetWithURL:fileURL_ options:nil] duration]);
84
+ double fileSizeBytesMb =
85
+ static_cast<double>([[[NSFileManager defaultManager] attributesOfItemAtPath:fileURL_.path
86
+ error:&error] fileSize]) /
87
+ BYTES_TO_MB;
88
+
89
+ NSLog(
90
+ @"ℹ️ Closed audio file at path: %s, duration: %.2f sec, size: %.2f MB",
91
+ filePath.c_str(),
92
+ fileDuration,
93
+ fileSizeBytesMb);
94
+
95
+ if (error != nil) {
96
+ NSLog(@"⚠️ closeFile: error while retrieving file size");
97
+ fileSizeBytesMb = 0;
98
+ }
99
+
67
100
  fileURL_ = nil;
68
- return filePath;
101
+
102
+ return {fileSizeBytesMb, fileDuration};
69
103
  }
70
104
  }
71
105
 
@@ -76,66 +110,63 @@ bool IOSAudioFileWriter::writeAudioData(const AudioBufferList *audioBufferList,
76
110
  return false;
77
111
  }
78
112
 
79
- // TODO: not sure if this is necessary
80
113
  @autoreleasepool {
81
114
  NSError *error = nil;
82
- AVAudioFormat *filePCMFormat = [audioFile_ processingFormat];
115
+ AVAudioFormat *fileFormat = [audioFile_ processingFormat];
83
116
 
84
- if (audioBufferList->mNumberBuffers != filePCMFormat.channelCount ||
85
- bufferFormat_.sampleRate != filePCMFormat.sampleRate ||
86
- bufferFormat_.interleaved != filePCMFormat.interleaved) {
87
- int outputFrameCount = ceil(numFrames * filePCMFormat.sampleRate / bufferFormat_.sampleRate);
117
+ if (bufferFormat_.sampleRate == fileFormat.sampleRate && bufferFormat_.channelCount == fileFormat.channelCount &&
118
+ bufferFormat_.isInterleaved == fileFormat.isInterleaved) {
119
+ AVAudioPCMBuffer *processingBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:fileFormat
120
+ bufferListNoCopy:audioBufferList
121
+ deallocator:NULL];
122
+ processingBuffer.frameLength = (AVAudioFrameCount)numFrames;
88
123
 
89
- AVAudioPCMBuffer *inputBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:bufferFormat_
90
- frameCapacity:(AVAudioFrameCount)numFrames];
124
+ [audioFile_ writeFromBuffer:processingBuffer error:&error];
91
125
 
92
- for (int i = 0; i < bufferFormat_.channelCount; i++) {
93
- memcpy(
94
- inputBuffer.mutableAudioBufferList->mBuffers[i].mData,
95
- audioBufferList->mBuffers[i].mData,
96
- audioBufferList->mBuffers[i].mDataByteSize);
126
+ if (error != nil) {
127
+ NSLog(@"Error writing audio data to file: %@", [error debugDescription]);
128
+ return false;
97
129
  }
98
130
 
99
- inputBuffer.frameLength = numFrames;
100
- AVAudioPCMBuffer *outputBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:filePCMFormat
101
- frameCapacity:outputFrameCount];
102
- outputBuffer.frameLength = outputFrameCount;
131
+ framesWritten_.fetch_add(numFrames);
132
+ return true;
133
+ }
134
+
135
+ size_t outputFrameCount = ceil(numFrames * fileFormat.sampleRate / bufferFormat_.sampleRate);
103
136
 
104
- AVAudioConverterInputBlock inputBlock =
105
- ^AVAudioBuffer *_Nullable(AVAudioPacketCount inNumberOfPackets, AVAudioConverterInputStatus *outStatus)
106
- {
107
- // this line is probably an delusion, but for my sanity lets keep it
108
- inNumberOfPackets = numFrames;
109
- *outStatus = AVAudioConverterInputStatus_HaveData;
110
- return inputBuffer;
111
- };
137
+ for (size_t i = 0; i < bufferFormat_.channelCount; ++i) {
138
+ memcpy(
139
+ converterInputBuffer_.mutableAudioBufferList->mBuffers[i].mData,
140
+ audioBufferList->mBuffers[i].mData,
141
+ audioBufferList->mBuffers[i].mDataByteSize);
142
+ }
112
143
 
113
- [converter_ convertToBuffer:outputBuffer error:&error withInputFromBlock:inputBlock];
144
+ converterInputBuffer_.frameLength = numFrames;
114
145
 
115
- if (error != nil) {
116
- NSLog(@"Error during audio conversion: %@", [error debugDescription]);
117
- return false;
118
- }
146
+ AVAudioConverterInputBlock inputBlock =
147
+ ^AVAudioBuffer *_Nullable(AVAudioPacketCount inNumberOfPackets, AVAudioConverterInputStatus *outStatus)
148
+ {
149
+ // this line is probably an delusion, but for my sanity lets keep it
150
+ inNumberOfPackets = numFrames;
151
+ *outStatus = AVAudioConverterInputStatus_HaveData;
152
+ return converterInputBuffer_;
153
+ };
119
154
 
120
- [audioFile_ writeFromBuffer:outputBuffer error:&error];
155
+ [converter_ convertToBuffer:converterOutputBuffer_ error:&error withInputFromBlock:inputBlock];
121
156
 
122
- if (error != nil) {
123
- NSLog(@"Error writing audio data to file: %@", [error debugDescription]);
124
- return false;
125
- }
126
- } else {
127
- AVAudioPCMBuffer *processingBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:filePCMFormat
128
- bufferListNoCopy:audioBufferList
129
- deallocator:NULL];
157
+ if (error != nil) {
158
+ NSLog(@"Error during audio conversion: %@", [error debugDescription]);
159
+ return false;
160
+ }
130
161
 
131
- [audioFile_ writeFromBuffer:processingBuffer error:&error];
162
+ [audioFile_ writeFromBuffer:converterOutputBuffer_ error:&error];
132
163
 
133
- if (error != nil) {
134
- NSLog(@"Error writing audio data to file: %@", [error debugDescription]);
135
- return false;
136
- }
164
+ if (error != nil) {
165
+ NSLog(@"Error writing audio data to file: %@", [error debugDescription]);
166
+ return false;
137
167
  }
138
168
 
169
+ framesWritten_.fetch_add(numFrames);
139
170
  return true;
140
171
  }
141
172
  }
@@ -184,4 +215,9 @@ NSURL *IOSAudioFileWriter::getFileURL()
184
215
  return [dirURL URLByAppendingPathComponent:fileName];
185
216
  }
186
217
 
218
+ double IOSAudioFileWriter::getCurrentDuration() const
219
+ {
220
+ return static_cast<double>(framesWritten_.load()) / bufferFormat_.sampleRate;
221
+ }
222
+
187
223
  } // namespace audioapi
@@ -16,6 +16,7 @@ namespace audioapi {
16
16
  class AudioBus;
17
17
  class CircularAudioArray;
18
18
  class IOSAudioFileWriter;
19
+ class IOSRecorderCallback;
19
20
  class AudioEventHandlerRegistry;
20
21
 
21
22
  class IOSAudioRecorder : public AudioRecorder {
@@ -23,8 +24,8 @@ class IOSAudioRecorder : public AudioRecorder {
23
24
  IOSAudioRecorder(const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry);
24
25
  ~IOSAudioRecorder() override;
25
26
 
26
- void start() override;
27
- std::string stop() override;
27
+ std::string start() override;
28
+ std::tuple<std::string, double, double> stop() override;
28
29
 
29
30
  void enableFileOutput(float sampleRate, size_t channelCount, size_t bitRate, size_t iosFlags, size_t androidFlags)
30
31
  override;
@@ -33,8 +34,16 @@ class IOSAudioRecorder : public AudioRecorder {
33
34
  void pause() override;
34
35
  void resume() override;
35
36
 
37
+ void setOnAudioReadyCallback(float sampleRate, size_t bufferLength, size_t channelCount, uint64_t callbackId)
38
+ override;
39
+ void clearOnAudioReadyCallback() override;
40
+
41
+ double getCurrentDuration() const override;
42
+
36
43
  private:
37
44
  std::shared_ptr<IOSAudioFileWriter> fileWriter_;
45
+ std::shared_ptr<IOSRecorderCallback> callback_;
46
+ std::string filePath_{""};
38
47
  NativeAudioRecorder *nativeRecorder_;
39
48
  };
40
49
 
@@ -7,6 +7,7 @@
7
7
  #include <audioapi/events/AudioEventHandlerRegistry.h>
8
8
  #include <audioapi/ios/core/IOSAudioFileWriter.h>
9
9
  #include <audioapi/ios/core/IOSAudioRecorder.h>
10
+ #include <audioapi/ios/core/IOSRecorderCallback.h>
10
11
  #include <audioapi/utils/AudioArray.h>
11
12
  #include <audioapi/utils/AudioBus.h>
12
13
  #include <audioapi/utils/CircularAudioArray.h>
@@ -14,22 +15,6 @@
14
15
 
15
16
  namespace audioapi {
16
17
 
17
- // AudioReceiverBlock audioReceiverBlock = ^(const AudioBufferList *inputBuffer, int numFrames) {
18
- // if (isRunning_.load()) {
19
- // auto *inputChannel = static_cast<float *>(inputBuffer->mBuffers[0].mData);
20
- // writeToBuffers(inputChannel, numFrames);
21
- // }
22
-
23
- // while (circularBuffer_->getNumberOfAvailableFrames() >= bufferLength_) {
24
- // auto bus = std::make_shared<AudioBus>(bufferLength_, 1, sampleRate_);
25
- // auto *outputChannel = bus->getChannel(0)->getData();
26
-
27
- // circularBuffer_->pop_front(outputChannel, bufferLength_);
28
-
29
- // invokeOnAudioReadyCallback(bus, bufferLength_);
30
- // }
31
- // };
32
-
33
18
  IOSAudioRecorder::IOSAudioRecorder(const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry)
34
19
  : AudioRecorder(audioEventHandlerRegistry), fileWriter_(nullptr)
35
20
  {
@@ -37,6 +22,10 @@ IOSAudioRecorder::IOSAudioRecorder(const std::shared_ptr<AudioEventHandlerRegist
37
22
  if (usesFileOutput()) {
38
23
  fileWriter_->writeAudioData(inputBuffer, numFrames);
39
24
  }
25
+
26
+ if (usesCallback()) {
27
+ callback_->receiveAudioData(inputBuffer, numFrames);
28
+ }
40
29
  };
41
30
 
42
31
  nativeRecorder_ = [[NativeAudioRecorder alloc] initWithReceiverBlock:receiverBlock];
@@ -47,19 +36,20 @@ IOSAudioRecorder::~IOSAudioRecorder()
47
36
  stop();
48
37
  [nativeRecorder_ cleanup];
49
38
  }
50
-
51
- void IOSAudioRecorder::start()
39
+ std::string IOSAudioRecorder::start()
52
40
  {
41
+ size_t maxInputBufferLength = [nativeRecorder_ getBufferSize];
42
+
53
43
  if (isRecording()) {
54
- return;
44
+ return filePath_;
55
45
  }
56
46
 
57
47
  if (usesFileOutput()) {
58
- fileWriter_->openFile([nativeRecorder_ getInputFormat]);
48
+ filePath_ = fileWriter_->openFile([nativeRecorder_ getInputFormat], maxInputBufferLength);
59
49
  }
60
50
 
61
51
  if (usesCallback()) {
62
- // TODO: create circular buffer and converter?
52
+ callback_->prepare([nativeRecorder_ getInputFormat], maxInputBufferLength);
63
53
  }
64
54
 
65
55
  if (isConnected()) {
@@ -67,25 +57,36 @@ void IOSAudioRecorder::start()
67
57
  }
68
58
 
69
59
  [nativeRecorder_ start];
70
- isRunning_.store(true);
60
+ state_.store(RecorderState::Recording);
61
+
62
+ return filePath_;
71
63
  }
72
64
 
73
- std::string IOSAudioRecorder::stop()
65
+ std::tuple<std::string, double, double> IOSAudioRecorder::stop()
74
66
  {
67
+ std::string filePath = filePath_;
68
+ double outputFileSize = 0;
69
+ double outputDuration = 0;
70
+
75
71
  if (!isRecording()) {
76
- return std::string("");
72
+ return {filePath, 0, 0};
77
73
  }
78
74
 
79
75
  [nativeRecorder_ stop];
80
- isRunning_.store(false);
81
-
82
- // TODO: send remaining data?
76
+ state_.store(RecorderState::Idle);
83
77
 
84
78
  if (usesFileOutput()) {
85
- return fileWriter_->closeFile();
79
+ auto [size, duration] = fileWriter_->closeFile();
80
+ outputFileSize = size;
81
+ outputDuration = duration;
86
82
  }
87
83
 
88
- return std::string("");
84
+ if (usesCallback()) {
85
+ callback_->cleanup();
86
+ }
87
+
88
+ filePath_ = "";
89
+ return {filePath, outputFileSize, outputDuration};
89
90
  }
90
91
 
91
92
  void IOSAudioRecorder::enableFileOutput(
@@ -105,8 +106,52 @@ void IOSAudioRecorder::disableFileOutput()
105
106
  fileWriter_ = nullptr;
106
107
  }
107
108
 
108
- void IOSAudioRecorder::pause() {}
109
+ void IOSAudioRecorder::pause()
110
+ {
111
+ if (!isRecording()) {
112
+ return;
113
+ }
114
+
115
+ [nativeRecorder_ stop];
116
+ state_.store(RecorderState::Paused);
117
+ }
118
+
119
+ void IOSAudioRecorder::resume()
120
+ {
121
+ if (!isPaused()) {
122
+ return;
123
+ }
109
124
 
110
- void IOSAudioRecorder::resume() {}
125
+ [nativeRecorder_ start];
126
+ state_.store(RecorderState::Recording);
127
+ }
128
+
129
+ void IOSAudioRecorder::setOnAudioReadyCallback(
130
+ float sampleRate,
131
+ size_t bufferLength,
132
+ size_t channelCount,
133
+ uint64_t callbackId)
134
+ {
135
+ callback_ = std::make_shared<IOSRecorderCallback>(
136
+ audioEventHandlerRegistry_, sampleRate, bufferLength, channelCount, callbackId);
137
+ callbackOutputEnabled_.store(true);
138
+ }
139
+
140
+ void IOSAudioRecorder::clearOnAudioReadyCallback()
141
+ {
142
+ callbackOutputEnabled_.store(false);
143
+ callback_ = nullptr;
144
+ }
145
+
146
+ double IOSAudioRecorder::getCurrentDuration() const
147
+ {
148
+ double duration = 0.0;
149
+
150
+ if (usesFileOutput() && fileWriter_) {
151
+ duration = fileWriter_->getCurrentDuration();
152
+ }
153
+
154
+ return duration;
155
+ }
111
156
 
112
157
  } // namespace audioapi
@@ -0,0 +1,57 @@
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 <memory>
10
+ #include <vector>
11
+
12
+ namespace audioapi {
13
+
14
+ class AudioBus;
15
+ class CircularAudioArray;
16
+ class AudioEventHandlerRegistry;
17
+
18
+ class IOSRecorderCallback {
19
+ public:
20
+ IOSRecorderCallback(
21
+ const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry,
22
+ float sampleRate,
23
+ size_t bufferLength,
24
+ size_t channelCount,
25
+ uint64_t callbackId);
26
+ ~IOSRecorderCallback();
27
+
28
+ void prepare(AVAudioFormat *bufferFormat, size_t maxInputBufferLength);
29
+ void cleanup();
30
+
31
+ void receiveAudioData(const AudioBufferList *audioBufferList, int numFrames);
32
+ void emitAudioData();
33
+
34
+ void invokeCallback(const std::shared_ptr<AudioBus> &bus, int numFrames);
35
+ void sendRemainingData();
36
+
37
+ private:
38
+ float sampleRate_;
39
+ size_t bufferLength_;
40
+ size_t channelCount_;
41
+ uint64_t callbackId_;
42
+ size_t ringBufferSize_;
43
+ size_t converterInputBufferSize_;
44
+ size_t converterOutputBufferSize_;
45
+
46
+ std::shared_ptr<AudioEventHandlerRegistry> audioEventHandlerRegistry_;
47
+ std::vector<std::shared_ptr<CircularAudioArray>> circularBus_;
48
+
49
+ AVAudioFormat *bufferFormat_;
50
+ AVAudioFormat *callbackFormat_;
51
+ AVAudioConverter *converter_;
52
+
53
+ AVAudioPCMBuffer *converterInputBuffer_;
54
+ AVAudioPCMBuffer *converterOutputBuffer_;
55
+ };
56
+
57
+ } // namespace audioapi
@@ -0,0 +1,189 @@
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/IOSRecorderCallback.h>
9
+ #include <audioapi/utils/AudioArray.h>
10
+ #include <audioapi/utils/AudioBus.h>
11
+ #include <audioapi/utils/CircularAudioArray.h>
12
+ #include <algorithm>
13
+
14
+ namespace audioapi {
15
+
16
+ IOSRecorderCallback::IOSRecorderCallback(
17
+ const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry,
18
+ float sampleRate,
19
+ size_t bufferLength,
20
+ size_t channelCount,
21
+ uint64_t callbackId)
22
+ : audioEventHandlerRegistry_(audioEventHandlerRegistry),
23
+ sampleRate_(sampleRate),
24
+ bufferLength_(bufferLength),
25
+ channelCount_(channelCount),
26
+ callbackId_(callbackId)
27
+ {
28
+ ringBufferSize_ = std::max((int)bufferLength_ * 2, 8192);
29
+ circularBus_.resize(channelCount_);
30
+
31
+ for (size_t i = 0; i < channelCount_; ++i) {
32
+ auto busAudioArray = std::make_shared<CircularAudioArray>(ringBufferSize_);
33
+ circularBus_[i] = busAudioArray;
34
+ }
35
+ }
36
+
37
+ IOSRecorderCallback::~IOSRecorderCallback()
38
+ {
39
+ @autoreleasepool {
40
+ converter_ = nil;
41
+ bufferFormat_ = nil;
42
+ callbackFormat_ = nil;
43
+ converterInputBuffer_ = nil;
44
+ converterOutputBuffer_ = nil;
45
+
46
+ for (size_t i = 0; i < channelCount_; ++i) {
47
+ circularBus_[i].reset();
48
+ }
49
+ }
50
+ }
51
+
52
+ void IOSRecorderCallback::prepare(AVAudioFormat *bufferFormat, size_t maxInputBufferLength)
53
+ {
54
+ @autoreleasepool {
55
+ bufferFormat_ = bufferFormat;
56
+ converterInputBufferSize_ = maxInputBufferLength;
57
+
58
+ converterOutputBufferSize_ =
59
+ std::max((double)maxInputBufferLength, sampleRate_ / bufferFormat.sampleRate * maxInputBufferLength);
60
+
61
+ callbackFormat_ = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32
62
+ sampleRate:sampleRate_
63
+ channels:channelCount_
64
+ interleaved:NO];
65
+
66
+ converter_ = [[AVAudioConverter alloc] initFromFormat:bufferFormat toFormat:callbackFormat_];
67
+ converter_.sampleRateConverterAlgorithm = AVSampleRateConverterAlgorithm_Normal;
68
+ converter_.sampleRateConverterQuality = AVAudioQualityMax;
69
+ converter_.primeMethod = AVAudioConverterPrimeMethod_None;
70
+
71
+ converterInputBuffer_ = [[AVAudioPCMBuffer alloc] initWithPCMFormat:bufferFormat_
72
+ frameCapacity:(AVAudioFrameCount)converterInputBufferSize_];
73
+ converterOutputBuffer_ = [[AVAudioPCMBuffer alloc] initWithPCMFormat:callbackFormat_
74
+ frameCapacity:(AVAudioFrameCount)converterOutputBufferSize_];
75
+ }
76
+ }
77
+
78
+ void IOSRecorderCallback::cleanup()
79
+ {
80
+ @autoreleasepool {
81
+ sendRemainingData();
82
+
83
+ converter_ = nil;
84
+ bufferFormat_ = nil;
85
+ callbackFormat_ = nil;
86
+ converterInputBuffer_ = nil;
87
+ converterOutputBuffer_ = nil;
88
+
89
+ for (size_t i = 0; i < channelCount_; ++i) {
90
+ circularBus_[i].reset();
91
+ }
92
+ }
93
+ }
94
+
95
+ void IOSRecorderCallback::receiveAudioData(const AudioBufferList *inputBuffer, int numFrames)
96
+ {
97
+ @autoreleasepool {
98
+ NSError *error = nil;
99
+
100
+ if (bufferFormat_.sampleRate == sampleRate_ && bufferFormat_.channelCount == channelCount_ &&
101
+ !bufferFormat_.isInterleaved) {
102
+ // Directly write to circular buffer
103
+ for (size_t i = 0; i < channelCount_; ++i) {
104
+ auto *inputChannel = static_cast<float *>(inputBuffer->mBuffers[i].mData);
105
+ circularBus_[i]->push_back(inputChannel, numFrames);
106
+ }
107
+
108
+ emitAudioData();
109
+ return;
110
+ }
111
+
112
+ size_t outputFrameCount = ceil(numFrames * (sampleRate_ / bufferFormat_.sampleRate));
113
+
114
+ for (size_t i = 0; i < bufferFormat_.channelCount; ++i) {
115
+ memcpy(
116
+ converterInputBuffer_.mutableAudioBufferList->mBuffers[i].mData,
117
+ inputBuffer->mBuffers[i].mData,
118
+ inputBuffer->mBuffers[i].mDataByteSize);
119
+ }
120
+
121
+ converterInputBuffer_.frameLength = numFrames;
122
+
123
+ AVAudioConverterInputBlock inputBlock =
124
+ ^AVAudioBuffer *_Nullable(AVAudioPacketCount inNumberOfPackets, AVAudioConverterInputStatus *outStatus)
125
+ {
126
+ // this line is probably an delusion, but for my sanity lets keep it
127
+ inNumberOfPackets = numFrames;
128
+ *outStatus = AVAudioConverterInputStatus_HaveData;
129
+ return converterInputBuffer_;
130
+ };
131
+
132
+ [converter_ convertToBuffer:converterOutputBuffer_ error:&error withInputFromBlock:inputBlock];
133
+
134
+ if (error != nil) {
135
+ NSLog(@"Error during audio conversion: %@", [error debugDescription]);
136
+ return;
137
+ }
138
+
139
+ for (size_t i = 0; i < channelCount_; ++i) {
140
+ auto *inputChannel = static_cast<float *>(converterOutputBuffer_.audioBufferList->mBuffers[i].mData);
141
+ circularBus_[i]->push_back(inputChannel, outputFrameCount);
142
+ }
143
+
144
+ emitAudioData();
145
+ }
146
+ }
147
+
148
+ void IOSRecorderCallback::emitAudioData()
149
+ {
150
+ while (circularBus_[0]->getNumberOfAvailableFrames() >= bufferLength_) {
151
+ auto bus = std::make_shared<AudioBus>(bufferLength_, channelCount_, sampleRate_);
152
+
153
+ for (size_t i = 0; i < channelCount_; ++i) {
154
+ auto *outputChannel = bus->getChannel(i)->getData();
155
+ circularBus_[i]->pop_front(outputChannel, bufferLength_);
156
+ }
157
+
158
+ invokeCallback(bus, bufferLength_);
159
+ }
160
+ }
161
+
162
+ void IOSRecorderCallback::invokeCallback(const std::shared_ptr<AudioBus> &bus, int numFrames)
163
+ {
164
+ auto audioBuffer = std::make_shared<AudioBuffer>(bus);
165
+ auto audioBufferHostObject = std::make_shared<AudioBufferHostObject>(audioBuffer);
166
+
167
+ std::unordered_map<std::string, EventValue> eventPayload = {};
168
+ eventPayload.insert({"buffer", audioBufferHostObject});
169
+ eventPayload.insert({"numFrames", numFrames});
170
+
171
+ if (audioEventHandlerRegistry_) {
172
+ audioEventHandlerRegistry_->invokeHandlerWithEventBody("audioReady", callbackId_, eventPayload);
173
+ }
174
+ }
175
+
176
+ void IOSRecorderCallback::sendRemainingData()
177
+ {
178
+ auto numberOfFrames = circularBus_[0]->getNumberOfAvailableFrames();
179
+ auto bus = std::make_shared<AudioBus>(circularBus_[0]->getNumberOfAvailableFrames(), channelCount_, sampleRate_);
180
+
181
+ for (size_t i = 0; i < channelCount_; ++i) {
182
+ auto *outputChannel = bus->getChannel(i)->getData();
183
+ circularBus_[i]->pop_front(outputChannel, numberOfFrames);
184
+ }
185
+
186
+ invokeCallback(bus, numberOfFrames);
187
+ }
188
+
189
+ } // namespace audioapi
@@ -7,9 +7,6 @@ typedef void (^AudioReceiverBlock)(const AudioBufferList *inputBuffer, int numFr
7
7
 
8
8
  @interface NativeAudioRecorder : NSObject
9
9
 
10
- @property (nonatomic, assign) int bufferLength;
11
- @property (nonatomic, assign) float sampleRate;
12
-
13
10
  @property (nonatomic, strong) AVAudioSinkNode *sinkNode;
14
11
  @property (nonatomic, copy) AVAudioSinkNodeReceiverBlock receiverSinkBlock;
15
12
  @property (nonatomic, copy) AudioReceiverBlock receiverBlock;
@@ -18,6 +15,8 @@ typedef void (^AudioReceiverBlock)(const AudioBufferList *inputBuffer, int numFr
18
15
 
19
16
  - (AVAudioFormat *)getInputFormat;
20
17
 
18
+ - (int)getBufferSize;
19
+
21
20
  - (void)start;
22
21
 
23
22
  - (void)stop;