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.
- 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/android/src/oldarch/NativeAudioAPIModuleSpec.java +100 -80
- 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 +33 -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
|
@@ -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
|
|
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
|
-
|
|
19
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
void
|
|
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
|
-
|
|
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
|
-
|
|
43
|
-
|
|
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
|
-
|
|
47
|
-
int bufferLength_;
|
|
48
|
-
size_t ringBufferSize_;
|
|
62
|
+
std::atomic<RecorderState> state_{RecorderState::Idle};
|
|
49
63
|
|
|
50
|
-
std::atomic<bool>
|
|
51
|
-
std::
|
|
64
|
+
std::atomic<bool> isConnected_{false};
|
|
65
|
+
std::atomic<bool> fileOutputEnabled_{false};
|
|
66
|
+
std::atomic<bool> callbackOutputEnabled_{false};
|
|
52
67
|
|
|
53
|
-
|
|
54
|
-
std::
|
|
68
|
+
std::mutex callbackMutex_;
|
|
69
|
+
std::mutex fileWriterMutex_;
|
|
70
|
+
std::mutex errorCallbackMutex_;
|
|
71
|
+
mutable std::mutex adapterNodeMutex_;
|
|
55
72
|
|
|
56
|
-
std::
|
|
57
|
-
uint64_t onAudioReadyCallbackId_ = 0;
|
|
73
|
+
std::atomic<uint64_t> errorCallbackId_{0};
|
|
58
74
|
|
|
59
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
31
|
-
|
|
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(
|
|
41
|
-
|
|
69
|
+
void RecorderAdapterNode::readFrames(const size_t framesToRead) {
|
|
70
|
+
adapterOutputBus_->zero();
|
|
42
71
|
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
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<
|
|
39
|
+
std::shared_ptr<AudioBus> adapterOutputBus_;
|
|
34
40
|
|
|
35
41
|
private:
|
|
36
|
-
/// @brief Read audio frames from the recorder's internal
|
|
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(
|
|
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
|