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
@@ -1,62 +1,82 @@
1
1
  #pragma once
2
2
 
3
+ #include <audioapi/utils/Result.hpp>
3
4
  #include <atomic>
4
5
  #include <memory>
5
6
  #include <mutex>
7
+ #include <string>
8
+ #include <tuple>
6
9
 
7
10
  namespace audioapi {
8
11
 
9
- class RecorderAdapterNode;
10
12
  class AudioBus;
13
+ class AudioFileWriter;
11
14
  class CircularAudioArray;
12
- class CircularOverflowableAudioArray;
15
+ class RecorderAdapterNode;
16
+ class AudioFileProperties;
17
+ class AudioRecorderCallback;
13
18
  class AudioEventHandlerRegistry;
14
19
 
15
20
  class AudioRecorder {
16
21
  public:
22
+ enum class RecorderState { Idle = 0, Recording, Paused };
17
23
  explicit AudioRecorder(
18
- float sampleRate,
19
- int bufferLength,
20
- const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry);
21
-
24
+ const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry)
25
+ : audioEventHandlerRegistry_(audioEventHandlerRegistry) {}
22
26
  virtual ~AudioRecorder() = default;
23
27
 
24
- void setOnAudioReadyCallbackId(uint64_t callbackId);
25
- void invokeOnAudioReadyCallback(const std::shared_ptr<AudioBus> &bus, int numFrames);
26
- void sendRemainingData();
28
+ virtual Result<std::string, std::string> start() = 0;
29
+ virtual Result<std::tuple<std::string, double, double>, std::string> stop() = 0;
30
+
31
+ virtual Result<std::string, std::string> enableFileOutput(
32
+ std::shared_ptr<AudioFileProperties> properties) = 0;
33
+ virtual void disableFileOutput() = 0;
34
+
35
+ virtual void pause() = 0;
36
+ virtual void resume() = 0;
37
+
38
+ virtual void connect(const std::shared_ptr<RecorderAdapterNode> &node) = 0;
39
+ virtual void disconnect() = 0;
27
40
 
28
- /// @brief
29
- /// # Connects the recorder to the adapter node.
30
- ///
31
- /// The adapter node will be used to read audio data from the recorder.
32
- /// @note Few frames of audio might not yet be written to the buffer when connecting.
33
- void connect(const std::shared_ptr<RecorderAdapterNode> &node);
41
+ virtual Result<NoneType, std::string> setOnAudioReadyCallback(
42
+ float sampleRate,
43
+ size_t bufferLength,
44
+ int channelCount,
45
+ uint64_t callbackId) = 0;
46
+ virtual void clearOnAudioReadyCallback() = 0;
47
+
48
+ void setOnErrorCallback(uint64_t callbackId);
49
+ void clearOnErrorCallback();
34
50
 
35
- /// @brief
36
- /// # Disconnects the recorder from the adapter node.
37
- ///
38
- /// The adapter node will no longer be used to read audio data from the recorder.
39
- /// @note Last few frames of audio might be written to the buffer after disconnecting.
40
- void disconnect();
51
+ virtual double getCurrentDuration() const;
41
52
 
42
- virtual void start() = 0;
43
- virtual void stop() = 0;
53
+ bool usesCallback() const;
54
+ bool usesFileOutput() const;
55
+ bool isConnected() const;
56
+
57
+ virtual bool isRecording() const = 0;
58
+ virtual bool isPaused() const = 0;
59
+ virtual bool isIdle() const = 0;
44
60
 
45
61
  protected:
46
- float sampleRate_;
47
- int bufferLength_;
48
- size_t ringBufferSize_;
62
+ std::atomic<RecorderState> state_{RecorderState::Idle};
49
63
 
50
- std::atomic<bool> isRunning_;
51
- std::shared_ptr<CircularAudioArray> circularBuffer_;
64
+ std::atomic<bool> isConnected_{false};
65
+ std::atomic<bool> fileOutputEnabled_{false};
66
+ std::atomic<bool> callbackOutputEnabled_{false};
52
67
 
53
- mutable std::mutex adapterNodeLock_;
54
- std::shared_ptr<RecorderAdapterNode> adapterNode_ = nullptr;
68
+ std::mutex callbackMutex_;
69
+ std::mutex fileWriterMutex_;
70
+ std::mutex errorCallbackMutex_;
71
+ mutable std::mutex adapterNodeMutex_;
55
72
 
56
- std::shared_ptr<AudioEventHandlerRegistry> audioEventHandlerRegistry_;
57
- uint64_t onAudioReadyCallbackId_ = 0;
73
+ std::atomic<uint64_t> errorCallbackId_{0};
58
74
 
59
- void writeToBuffers(const float *data, int numFrames);
75
+ std::string filePath_{""};
76
+ std::shared_ptr<AudioFileWriter> fileWriter_ = nullptr;
77
+ std::shared_ptr<RecorderAdapterNode> adapterNode_ = nullptr;
78
+ std::shared_ptr<AudioRecorderCallback> dataCallback_ = nullptr;
79
+ std::shared_ptr<AudioEventHandlerRegistry> audioEventHandlerRegistry_;
60
80
  };
61
81
 
62
82
  } // namespace audioapi
@@ -1,5 +1,7 @@
1
1
 
2
2
  #include <audioapi/core/sources/RecorderAdapterNode.h>
3
+ #include <audioapi/core/types/ChannelInterpretation.h>
4
+ #include <audioapi/core/utils/Constants.h>
3
5
  #include <audioapi/utils/AudioArray.h>
4
6
  #include <audioapi/utils/AudioBus.h>
5
7
  #include <memory>
@@ -15,34 +17,60 @@ RecorderAdapterNode::RecorderAdapterNode(BaseAudioContext *context) noexcept(
15
17
  isInitialized_ = false;
16
18
  }
17
19
 
18
- void RecorderAdapterNode::init(size_t bufferSize) {
20
+ void RecorderAdapterNode::init(size_t bufferSize, int channelCount) {
19
21
  if (isInitialized_) {
20
- throw std::runtime_error(
21
- "RecorderAdapterNode should not be initialized more than once. Just create a new instance.");
22
+ return;
22
23
  }
24
+
25
+ channelCount_ = channelCount;
26
+
27
+ buff_.resize(channelCount_);
28
+
29
+ for (size_t i = 0; i < channelCount_; ++i) {
30
+ buff_[i] = std::make_shared<CircularOverflowableAudioArray>(bufferSize);
31
+ }
32
+
33
+ // This assumes that the sample rate is the same in audio context and recorder.
34
+ // (recorder is not enforcing any sample rate on the system*). This means that only
35
+ // channel mixing might be required. To do so, we create an output bus with
36
+ // the desired channel count and will take advantage of the AudioBus sum method.
37
+ //
38
+ // * any allocations required by the recorder (including this method) are during recording start
39
+ // or after, which means that audio context has already setup the system in 99% of sane cases.
40
+ // But if we would like to support cases when context is created on the fly during recording,
41
+ // we would need to add sample rate conversion as well or other weird bullshit like resampling
42
+ // context output and not enforcing anything on the system output/input configuration.
43
+ // A lot of words for a couple of lines of implementation :shrug:
44
+ adapterOutputBus_ =
45
+ std::make_shared<AudioBus>(RENDER_QUANTUM_SIZE, channelCount_, context_->getSampleRate());
23
46
  isInitialized_ = true;
24
- buff_ = std::make_shared<CircularOverflowableAudioArray>(bufferSize);
47
+ }
48
+
49
+ void RecorderAdapterNode::cleanup() {
50
+ isInitialized_ = false;
51
+ buff_.clear();
52
+ adapterOutputBus_.reset();
25
53
  }
26
54
 
27
55
  std::shared_ptr<AudioBus> RecorderAdapterNode::processNode(
28
56
  const std::shared_ptr<AudioBus> &processingBus,
29
57
  int framesToProcess) {
30
- float *outputChannel = processingBus->getChannel(0)->getData();
31
- readFrames(outputChannel, framesToProcess);
32
-
33
- for (int i = 1; i < processingBus->getNumberOfChannels(); i++) {
34
- processingBus->getChannel(i)->copy(processingBus->getChannel(0), 0, framesToProcess);
58
+ if (!isInitialized_) {
59
+ processingBus->zero();
60
+ return processingBus;
35
61
  }
36
62
 
63
+ readFrames(framesToProcess);
64
+
65
+ processingBus->sum(adapterOutputBus_.get(), ChannelInterpretation::SPEAKERS);
37
66
  return processingBus;
38
67
  }
39
68
 
40
- void RecorderAdapterNode::readFrames(float *output, const size_t framesToRead) {
41
- size_t readFrames = buff_->read(output, framesToRead);
69
+ void RecorderAdapterNode::readFrames(const size_t framesToRead) {
70
+ adapterOutputBus_->zero();
42
71
 
43
- if (readFrames < framesToRead) {
44
- // Fill the rest with silence
45
- std::memset(output + readFrames, 0, (framesToRead - readFrames) * sizeof(float));
72
+ for (size_t channel = 0; channel < channelCount_; ++channel) {
73
+ buff_[channel]->read(adapterOutputBus_->getChannel(channel)->getData(), framesToRead);
46
74
  }
47
75
  }
48
76
 
@@ -6,6 +6,7 @@
6
6
  #include <audioapi/core/inputs/AudioRecorder.h>
7
7
  #include <audioapi/utils/CircularOverflowableAudioArray.h>
8
8
  #include <memory>
9
+ #include <vector>
9
10
 
10
11
  namespace audioapi {
11
12
 
@@ -20,26 +21,28 @@ class RecorderAdapterNode : public AudioNode {
20
21
  public:
21
22
  explicit RecorderAdapterNode(BaseAudioContext *context);
22
23
 
23
- /// @brief Initialize the RecorderAdapterNode with a buffer size.
24
+ /// @brief Initialize the RecorderAdapterNode with a buffer size and channel count.
24
25
  /// @note This method should be called ONLY ONCE when the buffer size is known.
25
- /// @throws std::runtime_error if the node is already initialized.
26
26
  /// @param bufferSize The size of the buffer to be used.
27
- void init(size_t bufferSize);
27
+ /// @param channelCount The number of channels.
28
+ void init(size_t bufferSize, int channelCount);
29
+ void cleanup();
30
+
31
+ int channelCount_;
32
+ // TODO: CircularOverflowableAudioBus
33
+ std::vector<std::shared_ptr<CircularOverflowableAudioArray>> buff_;
28
34
 
29
35
  protected:
30
36
  std::shared_ptr<AudioBus> processNode(
31
37
  const std::shared_ptr<AudioBus> &processingBus,
32
38
  int framesToProcess) override;
33
- std::shared_ptr<CircularOverflowableAudioArray> buff_;
39
+ std::shared_ptr<AudioBus> adapterOutputBus_;
34
40
 
35
41
  private:
36
- /// @brief Read audio frames from the recorder's internal adapterBuffer.
42
+ /// @brief Read audio frames from the recorder's internal circular buffer into output buss.
37
43
  /// @note If `framesToRead` is greater than the number of available frames, it will fill empty space with silence.
38
- /// @param output Pointer to the output buffer.
39
44
  /// @param framesToRead Number of frames to read.
40
- void readFrames(float *output, size_t framesToRead);
41
-
42
- friend class AudioRecorder;
45
+ void readFrames(size_t framesToRead);
43
46
  };
44
47
 
45
48
  } // namespace audioapi
@@ -0,0 +1,41 @@
1
+ #include <audioapi/core/utils/AudioFileWriter.h>
2
+ #include <audioapi/events/AudioEventHandlerRegistry.h>
3
+ #include <memory>
4
+ #include <string>
5
+ #include <unordered_map>
6
+
7
+ namespace audioapi {
8
+
9
+ AudioFileWriter::AudioFileWriter(
10
+ const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry,
11
+ const std::shared_ptr<AudioFileProperties> &fileProperties)
12
+ : audioEventHandlerRegistry_(audioEventHandlerRegistry), fileProperties_(fileProperties) {}
13
+
14
+ void AudioFileWriter::setOnErrorCallback(uint64_t callbackId) {
15
+ errorCallbackId_.store(callbackId, std::memory_order_release);
16
+ }
17
+
18
+ void AudioFileWriter::clearOnErrorCallback() {
19
+ errorCallbackId_.store(0, std::memory_order_release);
20
+ }
21
+
22
+ void AudioFileWriter::invokeOnErrorCallback(const std::string &message) {
23
+ uint64_t callbackId = errorCallbackId_.load(std::memory_order_acquire);
24
+
25
+ // TODO: only the line above is atomic, which means that between reading the callbackId and invoking the callback,
26
+ // the callback could be cleared. We need to ensure that the callback is still valid when invoking it.
27
+ // TL;DR: atomic szpont
28
+ if (audioEventHandlerRegistry_ == nullptr || callbackId == 0) {
29
+ return;
30
+ }
31
+
32
+ std::unordered_map<std::string, EventValue> eventPayload = {};
33
+ eventPayload.insert({"message", message});
34
+ audioEventHandlerRegistry_->invokeHandlerWithEventBody("error", callbackId, eventPayload);
35
+ }
36
+
37
+ bool AudioFileWriter::isFileOpen() {
38
+ return isFileOpen_.load(std::memory_order_acquire);
39
+ }
40
+
41
+ } // namespace audioapi
@@ -0,0 +1,44 @@
1
+ #pragma once
2
+
3
+ #include <audioapi/utils/Result.hpp>
4
+ #include <atomic>
5
+ #include <memory>
6
+ #include <string>
7
+ #include <tuple>
8
+
9
+ namespace audioapi {
10
+
11
+ class AudioFileProperties;
12
+ class AudioEventHandlerRegistry;
13
+
14
+ typedef Result<std::string, std::string> OpenFileResult;
15
+ typedef Result<std::tuple<double, double>, std::string> CloseFileResult;
16
+
17
+ class AudioFileWriter {
18
+ public:
19
+ AudioFileWriter(
20
+ const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry,
21
+ const std::shared_ptr<AudioFileProperties> &fileProperties);
22
+ virtual ~AudioFileWriter() = default;
23
+
24
+ virtual CloseFileResult closeFile() = 0;
25
+
26
+ virtual std::string getFilePath() const = 0;
27
+ virtual double getCurrentDuration() const = 0;
28
+
29
+ void setOnErrorCallback(uint64_t callbackId);
30
+ void clearOnErrorCallback();
31
+ void invokeOnErrorCallback(const std::string &message);
32
+
33
+ protected:
34
+ bool isFileOpen();
35
+
36
+ std::atomic<bool> isFileOpen_{false};
37
+ std::atomic<size_t> framesWritten_{0};
38
+ std::atomic<uint64_t> errorCallbackId_{0};
39
+
40
+ std::shared_ptr<AudioFileProperties> fileProperties_;
41
+ std::shared_ptr<AudioEventHandlerRegistry> audioEventHandlerRegistry_;
42
+ };
43
+
44
+ } // namespace audioapi
@@ -0,0 +1,101 @@
1
+
2
+ #include <audioapi/core/utils/AudioRecorderCallback.h>
3
+
4
+ #include <audioapi/HostObjects/sources/AudioBufferHostObject.h>
5
+ #include <audioapi/events/AudioEventHandlerRegistry.h>
6
+ #include <audioapi/utils/AudioArray.h>
7
+ #include <audioapi/utils/AudioBus.h>
8
+ #include <audioapi/utils/CircularAudioArray.h>
9
+
10
+ #include <algorithm>
11
+ #include <memory>
12
+ #include <string>
13
+ #include <unordered_map>
14
+
15
+ namespace audioapi {
16
+
17
+ /// @brief Constructor
18
+ /// Allocates circular buffer (as every property to do so is already known at this point).
19
+ /// @param audioEventHandlerRegistry The audio event handler registry
20
+ /// @param sampleRate The user desired sample rate
21
+ /// @param bufferLength The user desired buffer length
22
+ /// @param channelCount The user desired channel count
23
+ /// @param callbackId The callback identifier
24
+ AudioRecorderCallback::AudioRecorderCallback(
25
+ const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry,
26
+ float sampleRate,
27
+ size_t bufferLength,
28
+ int channelCount,
29
+ uint64_t callbackId)
30
+ : sampleRate_(sampleRate),
31
+ bufferLength_(bufferLength),
32
+ channelCount_(channelCount),
33
+ callbackId_(callbackId),
34
+ audioEventHandlerRegistry_(audioEventHandlerRegistry) {
35
+ ringBufferSize_ = std::max(bufferLength * 2, static_cast<size_t>(8192));
36
+ circularBus_.resize(channelCount_);
37
+
38
+ for (size_t i = 0; i < channelCount_; ++i) {
39
+ circularBus_[i] = std::make_shared<CircularAudioArray>(ringBufferSize_);
40
+ }
41
+
42
+ isInitialized_.store(true, std::memory_order_release);
43
+ }
44
+
45
+ AudioRecorderCallback::~AudioRecorderCallback() {
46
+ isInitialized_.store(false, std::memory_order_release);
47
+ }
48
+
49
+ /// @brief Emits audio data from the circular buffer when enough frames are available.
50
+ /// @param flush If true, emits all available data regardless of buffer length.
51
+ void AudioRecorderCallback::emitAudioData(bool flush) {
52
+ size_t sizeLimit = flush ? circularBus_[0]->getNumberOfAvailableFrames() : bufferLength_;
53
+
54
+ while (circularBus_[0]->getNumberOfAvailableFrames() >= sizeLimit) {
55
+ auto bus = std::make_shared<AudioBus>(sizeLimit, channelCount_, sampleRate_);
56
+
57
+ for (int i = 0; i < channelCount_; ++i) {
58
+ auto *outputChannel = bus->getChannel(i)->getData();
59
+ circularBus_[i]->pop_front(outputChannel, sizeLimit);
60
+ }
61
+
62
+ invokeCallback(bus, static_cast<int>(sizeLimit));
63
+ }
64
+ }
65
+
66
+ void AudioRecorderCallback::invokeCallback(const std::shared_ptr<AudioBus> &bus, int numFrames) {
67
+ auto audioBuffer = std::make_shared<AudioBuffer>(bus);
68
+ auto audioBufferHostObject = std::make_shared<AudioBufferHostObject>(audioBuffer);
69
+
70
+ std::unordered_map<std::string, EventValue> eventPayload = {};
71
+ eventPayload.insert({"buffer", audioBufferHostObject});
72
+ eventPayload.insert({"numFrames", numFrames});
73
+
74
+ if (audioEventHandlerRegistry_) {
75
+ audioEventHandlerRegistry_->invokeHandlerWithEventBody("audioReady", callbackId_, eventPayload);
76
+ }
77
+ }
78
+
79
+ void AudioRecorderCallback::setOnErrorCallback(uint64_t callbackId) {
80
+ errorCallbackId_.store(callbackId, std::memory_order_release);
81
+ }
82
+
83
+ void AudioRecorderCallback::clearOnErrorCallback() {
84
+ errorCallbackId_.store(0, std::memory_order_release);
85
+ }
86
+
87
+ /// @brief Invokes the error callback with the provided message.
88
+ /// @param message The error message to be sent to the callback.
89
+ void AudioRecorderCallback::invokeOnErrorCallback(const std::string &message) {
90
+ uint64_t callbackId = errorCallbackId_.load(std::memory_order_acquire);
91
+
92
+ if (audioEventHandlerRegistry_ == nullptr || callbackId == 0) {
93
+ return;
94
+ }
95
+
96
+ std::unordered_map<std::string, EventValue> eventPayload = {};
97
+ eventPayload.insert({"message", message});
98
+ audioEventHandlerRegistry_->invokeHandlerWithEventBody("error", callbackId, eventPayload);
99
+ }
100
+
101
+ } // namespace audioapi
@@ -0,0 +1,52 @@
1
+ #pragma once
2
+
3
+ #include <audioapi/utils/Result.hpp>
4
+ #include <atomic>
5
+ #include <memory>
6
+ #include <string>
7
+ #include <vector>
8
+
9
+ namespace audioapi {
10
+
11
+ class AudioBus;
12
+ class AudioArray;
13
+ class CircularAudioArray;
14
+ class AudioEventHandlerRegistry;
15
+
16
+ class AudioRecorderCallback {
17
+ public:
18
+ AudioRecorderCallback(
19
+ const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry,
20
+ float sampleRate,
21
+ size_t bufferLength,
22
+ int channelCount,
23
+ uint64_t callbackId);
24
+ virtual ~AudioRecorderCallback();
25
+
26
+ virtual void cleanup() = 0;
27
+
28
+ void emitAudioData(bool flush = false);
29
+ void invokeCallback(const std::shared_ptr<AudioBus> &bus, int numFrames);
30
+
31
+ void setOnErrorCallback(uint64_t callbackId);
32
+ void clearOnErrorCallback();
33
+ void invokeOnErrorCallback(const std::string &message);
34
+
35
+ protected:
36
+ std::atomic<bool> isInitialized_{false};
37
+
38
+ float sampleRate_;
39
+ size_t bufferLength_;
40
+ int channelCount_;
41
+ uint64_t callbackId_;
42
+ size_t ringBufferSize_;
43
+
44
+ std::atomic<uint64_t> errorCallbackId_{0};
45
+
46
+ std::shared_ptr<AudioEventHandlerRegistry> audioEventHandlerRegistry_;
47
+
48
+ // TODO: CircularAudioBus
49
+ std::vector<std::shared_ptr<CircularAudioArray>> circularBus_;
50
+ };
51
+
52
+ } // namespace audioapi
@@ -0,0 +1,92 @@
1
+ #include <audioapi/utils/AudioFileProperties.h>
2
+
3
+ #include <audioapi/jsi/JsiHostObject.h>
4
+ #include <jsi/jsi.h>
5
+ #include <algorithm>
6
+ #include <memory>
7
+ #include <string>
8
+
9
+ namespace audioapi {
10
+
11
+ AudioFileProperties::AudioFileProperties(
12
+ FileDirectory directory,
13
+ const std::string &subDirectory,
14
+ const std::string &fileNamePrefix,
15
+ int channelCount,
16
+ size_t batchDurationSeconds,
17
+ Format format,
18
+ float sampleRate,
19
+ size_t bitRate,
20
+ BitDepth bitDepth,
21
+ int flacCompressionLevel,
22
+ int androidFlushIntervalMs,
23
+ IOSAudioQuality iosAudioQuality)
24
+ : directory(directory),
25
+ subDirectory(subDirectory),
26
+ fileNamePrefix(fileNamePrefix),
27
+ channelCount(channelCount),
28
+ batchDurationSeconds(batchDurationSeconds),
29
+ format(format),
30
+ sampleRate(sampleRate),
31
+ bitRate(bitRate),
32
+ bitDepth(bitDepth),
33
+ flacCompressionLevel(flacCompressionLevel),
34
+ androidFlushIntervalMs(androidFlushIntervalMs),
35
+ iosAudioQuality(iosAudioQuality) {}
36
+
37
+ std::shared_ptr<AudioFileProperties> AudioFileProperties::CreateFromJSIValue(
38
+ facebook::jsi::Runtime &runtime,
39
+ const facebook::jsi::Value &value) {
40
+ auto options = value.getObject(runtime);
41
+
42
+ FileDirectory directory =
43
+ static_cast<FileDirectory>(options.getProperty(runtime, "directory").getNumber());
44
+
45
+ std::string subDirectory =
46
+ options.getProperty(runtime, "subDirectory").asString(runtime).utf8(runtime);
47
+
48
+ std::string fileNamePrefix =
49
+ options.getProperty(runtime, "fileNamePrefix").asString(runtime).utf8(runtime);
50
+
51
+ int channelCount = static_cast<int>(options.getProperty(runtime, "channelCount").getNumber());
52
+
53
+ size_t batchDurationSeconds =
54
+ static_cast<size_t>(options.getProperty(runtime, "batchDurationSeconds").getNumber());
55
+
56
+ Format format = static_cast<Format>(options.getProperty(runtime, "format").getNumber());
57
+
58
+ int androidFlushIntervalMs =
59
+ static_cast<int>(options.getProperty(runtime, "androidFlushIntervalMs").getNumber());
60
+
61
+ auto presetOptions = options.getProperty(runtime, "preset").getObject(runtime);
62
+
63
+ float sampleRate =
64
+ static_cast<float>(presetOptions.getProperty(runtime, "sampleRate").getNumber());
65
+
66
+ size_t bitRate = static_cast<size_t>(presetOptions.getProperty(runtime, "bitRate").getNumber());
67
+
68
+ BitDepth bitDepth =
69
+ static_cast<BitDepth>(presetOptions.getProperty(runtime, "bitDepth").getNumber());
70
+
71
+ int flacCompressionLevel =
72
+ static_cast<int>(presetOptions.getProperty(runtime, "flacCompressionLevel").getNumber());
73
+
74
+ IOSAudioQuality iosAudioQuality =
75
+ static_cast<IOSAudioQuality>(presetOptions.getProperty(runtime, "iosQuality").getNumber());
76
+
77
+ return std::make_shared<AudioFileProperties>(
78
+ directory,
79
+ subDirectory,
80
+ fileNamePrefix,
81
+ channelCount,
82
+ batchDurationSeconds,
83
+ format,
84
+ sampleRate,
85
+ bitRate,
86
+ bitDepth,
87
+ flacCompressionLevel,
88
+ androidFlushIntervalMs,
89
+ iosAudioQuality);
90
+ }
91
+
92
+ } // namespace audioapi
@@ -0,0 +1,76 @@
1
+ #pragma once
2
+
3
+ #include <cstddef>
4
+ #include <memory>
5
+ #include <string>
6
+
7
+ namespace facebook {
8
+ namespace jsi {
9
+ class Runtime;
10
+ class Value;
11
+ } // namespace jsi
12
+ } // namespace facebook
13
+
14
+ namespace audioapi {
15
+
16
+ class AudioFileProperties {
17
+ public:
18
+ enum class FileDirectory {
19
+ Document = 0,
20
+ Cache = 1,
21
+ };
22
+
23
+ enum class Format {
24
+ WAV = 0,
25
+ CAF = 1,
26
+ M4A = 2,
27
+ FLAC = 3,
28
+ };
29
+
30
+ enum class IOSAudioQuality {
31
+ Min = 0,
32
+ Low = 1,
33
+ Medium = 2,
34
+ High = 3,
35
+ Max = 4,
36
+ };
37
+
38
+ enum class BitDepth {
39
+ Bit16 = 0,
40
+ Bit24 = 1,
41
+ Bit32 = 2,
42
+ };
43
+
44
+ AudioFileProperties(
45
+ FileDirectory directory,
46
+ const std::string &subDirectory,
47
+ const std::string &fileNamePrefix,
48
+ int channelCount,
49
+ size_t batchDurationSeconds,
50
+ Format format,
51
+ float sampleRate,
52
+ size_t bitRate,
53
+ BitDepth bitDepth,
54
+ int flacCompressionLevel,
55
+ int androidFlushIntervalMs,
56
+ IOSAudioQuality iosAudioQuality);
57
+
58
+ static std::shared_ptr<AudioFileProperties> CreateFromJSIValue(
59
+ facebook::jsi::Runtime &runtime,
60
+ const facebook::jsi::Value &value);
61
+
62
+ FileDirectory directory;
63
+ std::string subDirectory;
64
+ std::string fileNamePrefix;
65
+ int channelCount;
66
+ size_t batchDurationSeconds;
67
+ Format format;
68
+ float sampleRate;
69
+ size_t bitRate;
70
+ BitDepth bitDepth;
71
+ int flacCompressionLevel;
72
+ int androidFlushIntervalMs;
73
+ IOSAudioQuality iosAudioQuality;
74
+ };
75
+
76
+ } // namespace audioapi