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.
- package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +77 -29
- package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.h +17 -4
- package/android/src/main/cpp/audioapi/android/core/utils/AndroidFileWriterBackend.h +6 -2
- package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp +187 -0
- package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h +57 -0
- package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/FFmpegFileWriter.cpp +22 -24
- package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/FFmpegFileWriter.h +3 -2
- package/android/src/main/cpp/audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.cpp +40 -8
- package/android/src/main/cpp/audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.h +3 -2
- package/common/cpp/audioapi/HostObjects/inputs/AudioRecorderHostObject.cpp +24 -7
- package/common/cpp/audioapi/HostObjects/inputs/AudioRecorderHostObject.h +3 -0
- package/common/cpp/audioapi/core/inputs/AudioRecorder.cpp +16 -115
- package/common/cpp/audioapi/core/inputs/AudioRecorder.h +35 -32
- package/ios/audioapi/ios/core/IOSAudioFileOptions.h +1 -0
- package/ios/audioapi/ios/core/IOSAudioFileOptions.mm +5 -0
- package/ios/audioapi/ios/core/IOSAudioFileWriter.h +16 -3
- package/ios/audioapi/ios/core/IOSAudioFileWriter.mm +86 -50
- package/ios/audioapi/ios/core/IOSAudioRecorder.h +11 -2
- package/ios/audioapi/ios/core/IOSAudioRecorder.mm +76 -31
- package/ios/audioapi/ios/core/IOSRecorderCallback.h +57 -0
- package/ios/audioapi/ios/core/IOSRecorderCallback.mm +189 -0
- package/ios/audioapi/ios/core/NativeAudioRecorder.h +2 -3
- package/ios/audioapi/ios/core/NativeAudioRecorder.m +26 -0
- package/ios/audioapi/ios/core/utils/AudioDecoder.mm +1 -0
- package/lib/commonjs/core/AudioRecorder.js +14 -1
- package/lib/commonjs/core/AudioRecorder.js.map +1 -1
- package/lib/module/core/AudioRecorder.js +14 -1
- package/lib/module/core/AudioRecorder.js.map +1 -1
- package/lib/typescript/core/AudioRecorder.d.ts +6 -3
- package/lib/typescript/core/AudioRecorder.d.ts.map +1 -1
- package/lib/typescript/interfaces.d.ts +5 -3
- package/lib/typescript/interfaces.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +5 -0
- package/lib/typescript/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/AudioRecorder.ts +27 -10
- package/src/interfaces.ts +6 -2
- package/src/types.ts +6 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#include <android/log.h>
|
|
2
2
|
#include <audioapi/android/core/AndroidAudioRecorder.h>
|
|
3
3
|
#include <audioapi/android/core/utils/AndroidFileWriterBackend.h>
|
|
4
|
+
#include <audioapi/android/core/utils/AndroidRecorderCallback.h>
|
|
4
5
|
#include <audioapi/android/core/utils/ffmpegBackend/FFmpegFileWriter.h>
|
|
5
6
|
#include <audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.h>
|
|
6
7
|
#include <audioapi/core/sources/RecorderAdapterNode.h>
|
|
@@ -30,7 +31,6 @@ AndroidAudioRecorder::AndroidAudioRecorder(
|
|
|
30
31
|
streamSampleRate_ = mStream_->getSampleRate();
|
|
31
32
|
streamChannelCount_ = mStream_->getChannelCount();
|
|
32
33
|
streamMaxBufferSizeInFrames_ = mStream_->getBufferSizeInFrames();
|
|
33
|
-
|
|
34
34
|
nativeAudioRecorder_ = jni::make_global(NativeAudioRecorder::create());
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -44,9 +44,9 @@ AndroidAudioRecorder::~AndroidAudioRecorder() {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
std::string AndroidAudioRecorder::start() {
|
|
48
48
|
if (isRecording()) {
|
|
49
|
-
return;
|
|
49
|
+
return filePath_;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
if (!mStream_ || !nativeAudioRecorder_) {
|
|
@@ -54,16 +54,18 @@ void AndroidAudioRecorder::start() {
|
|
|
54
54
|
ANDROID_LOG_ERROR,
|
|
55
55
|
"AndroidAudioRecorder",
|
|
56
56
|
"Audio stream is not initialized.\n");
|
|
57
|
-
return;
|
|
57
|
+
return filePath_;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
if (usesFileOutput()) {
|
|
61
|
-
fileWriter_->openFile(
|
|
61
|
+
filePath_ = fileWriter_->openFile(
|
|
62
62
|
streamSampleRate_, streamChannelCount_, streamMaxBufferSizeInFrames_);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
if (usesCallback()) {
|
|
66
66
|
// TODO: create circular buffer and converter?
|
|
67
|
+
callback_->prepare(
|
|
68
|
+
streamSampleRate_, streamChannelCount_, streamMaxBufferSizeInFrames_);
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
if (isConnected()) {
|
|
@@ -72,12 +74,18 @@ void AndroidAudioRecorder::start() {
|
|
|
72
74
|
|
|
73
75
|
nativeAudioRecorder_->start();
|
|
74
76
|
mStream_->requestStart();
|
|
75
|
-
|
|
77
|
+
state_.store(RecorderState::Recording);
|
|
78
|
+
|
|
79
|
+
return filePath_;
|
|
76
80
|
}
|
|
77
81
|
|
|
78
|
-
std::string AndroidAudioRecorder::stop() {
|
|
82
|
+
std::tuple<std::string, double, double> AndroidAudioRecorder::stop() {
|
|
83
|
+
std::string filePath = filePath_;
|
|
84
|
+
double outputFileSize = 0.0;
|
|
85
|
+
double outputDuration = 0.0;
|
|
86
|
+
|
|
79
87
|
if (!isRecording()) {
|
|
80
|
-
return
|
|
88
|
+
return {filePath_, 0.0, 0.0};
|
|
81
89
|
}
|
|
82
90
|
|
|
83
91
|
if (!mStream_ || !nativeAudioRecorder_) {
|
|
@@ -85,20 +93,25 @@ std::string AndroidAudioRecorder::stop() {
|
|
|
85
93
|
ANDROID_LOG_ERROR,
|
|
86
94
|
"AndroidAudioRecorder",
|
|
87
95
|
"Audio stream is not initialized.\n");
|
|
88
|
-
return
|
|
96
|
+
return {filePath_, 0.0, 0.0};
|
|
89
97
|
}
|
|
90
98
|
|
|
91
99
|
nativeAudioRecorder_->stop();
|
|
92
100
|
mStream_->requestStop();
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// TODO: sendRemainingData() ?
|
|
101
|
+
state_.store(RecorderState::Idle);
|
|
96
102
|
|
|
97
103
|
if (usesFileOutput()) {
|
|
98
|
-
|
|
104
|
+
auto [size, duration] = fileWriter_->closeFile();
|
|
105
|
+
outputFileSize = size;
|
|
106
|
+
outputDuration = duration;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (usesCallback()) {
|
|
110
|
+
callback_->cleanup();
|
|
99
111
|
}
|
|
100
112
|
|
|
101
|
-
|
|
113
|
+
filePath_ = "";
|
|
114
|
+
return {filePath, outputFileSize, outputDuration};
|
|
102
115
|
}
|
|
103
116
|
|
|
104
117
|
void AndroidAudioRecorder::enableFileOutput(
|
|
@@ -125,33 +138,68 @@ void AndroidAudioRecorder::disableFileOutput() {
|
|
|
125
138
|
fileWriter_ = nullptr;
|
|
126
139
|
}
|
|
127
140
|
|
|
128
|
-
void AndroidAudioRecorder::pause() {
|
|
141
|
+
void AndroidAudioRecorder::pause() {
|
|
142
|
+
if (!isRecording()) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
mStream_->pause(0);
|
|
147
|
+
state_.store(RecorderState::Paused);
|
|
148
|
+
}
|
|
129
149
|
|
|
130
|
-
void AndroidAudioRecorder::resume() {
|
|
150
|
+
void AndroidAudioRecorder::resume() {
|
|
151
|
+
if (!isPaused()) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
mStream_->start(0);
|
|
156
|
+
state_.store(RecorderState::Recording);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
void AndroidAudioRecorder::setOnAudioReadyCallback(
|
|
160
|
+
float sampleRate,
|
|
161
|
+
size_t bufferLength,
|
|
162
|
+
size_t channelCount,
|
|
163
|
+
uint64_t callbackId) {
|
|
164
|
+
callback_ = std::make_shared<AndroidRecorderCallback>(
|
|
165
|
+
audioEventHandlerRegistry_,
|
|
166
|
+
sampleRate,
|
|
167
|
+
bufferLength,
|
|
168
|
+
channelCount,
|
|
169
|
+
callbackId);
|
|
170
|
+
callbackOutputEnabled_.store(true);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
void AndroidAudioRecorder::clearOnAudioReadyCallback() {
|
|
174
|
+
callbackOutputEnabled_.store(false);
|
|
175
|
+
callback_ = nullptr;
|
|
176
|
+
}
|
|
131
177
|
|
|
132
178
|
DataCallbackResult AndroidAudioRecorder::onAudioReady(
|
|
133
179
|
oboe::AudioStream *oboeStream,
|
|
134
180
|
void *audioData,
|
|
135
181
|
int32_t numFrames) {
|
|
182
|
+
if (isPaused()) {
|
|
183
|
+
return DataCallbackResult::Continue;
|
|
184
|
+
}
|
|
185
|
+
|
|
136
186
|
if (usesFileOutput()) {
|
|
137
187
|
fileWriter_->writeAudioData(audioData, numFrames);
|
|
138
188
|
}
|
|
139
189
|
|
|
190
|
+
if (usesCallback()) {
|
|
191
|
+
callback_->receiveAudioData(audioData, numFrames);
|
|
192
|
+
}
|
|
193
|
+
|
|
140
194
|
return DataCallbackResult::Continue;
|
|
141
195
|
}
|
|
142
196
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
// writeToBuffers(inputChannel, numFrames);
|
|
148
|
-
// }
|
|
149
|
-
|
|
150
|
-
// while (circularBuffer_->getNumberOfAvailableFrames() >= bufferLength_) {
|
|
151
|
-
// auto bus = std::make_shared<AudioBus>(bufferLength_, 1, sampleRate_);
|
|
152
|
-
// auto *outputChannel = bus->getChannel(0)->getData();
|
|
197
|
+
double AndroidAudioRecorder::getCurrentDuration() const {
|
|
198
|
+
if (usesFileOutput()) {
|
|
199
|
+
return fileWriter_->getCurrentDuration();
|
|
200
|
+
}
|
|
153
201
|
|
|
154
|
-
|
|
202
|
+
return 0.0;
|
|
203
|
+
}
|
|
155
204
|
|
|
156
|
-
//
|
|
157
|
-
// }
|
|
205
|
+
} // namespace audioapi
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
#include <functional>
|
|
7
7
|
#include <memory>
|
|
8
8
|
#include <string>
|
|
9
|
+
#include <tuple>
|
|
9
10
|
|
|
10
11
|
#include <audioapi/android/core/NativeAudioRecorder.hpp>
|
|
11
12
|
|
|
@@ -15,6 +16,7 @@ using namespace oboe;
|
|
|
15
16
|
|
|
16
17
|
class AudioBus;
|
|
17
18
|
class CircularAudioArray;
|
|
19
|
+
class AndroidRecorderCallback;
|
|
18
20
|
class AndroidFileWriterBackend;
|
|
19
21
|
class AudioEventHandlerRegistry;
|
|
20
22
|
|
|
@@ -23,8 +25,8 @@ class AndroidAudioRecorder : public AudioStreamDataCallback, public AudioRecorde
|
|
|
23
25
|
explicit AndroidAudioRecorder(const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry);
|
|
24
26
|
~AndroidAudioRecorder() override;
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
std::string stop() override;
|
|
28
|
+
std::string start() override;
|
|
29
|
+
std::tuple<std::string, double, double> stop() override;
|
|
28
30
|
|
|
29
31
|
void enableFileOutput(
|
|
30
32
|
float sampleRate,
|
|
@@ -37,18 +39,29 @@ class AndroidAudioRecorder : public AudioStreamDataCallback, public AudioRecorde
|
|
|
37
39
|
void pause() override;
|
|
38
40
|
void resume() override;
|
|
39
41
|
|
|
42
|
+
void setOnAudioReadyCallback(float sampleRate, size_t bufferLength, size_t channelCount, uint64_t callbackId)
|
|
43
|
+
override;
|
|
44
|
+
void clearOnAudioReadyCallback() override;
|
|
45
|
+
|
|
46
|
+
double getCurrentDuration() const override;
|
|
47
|
+
|
|
40
48
|
DataCallbackResult onAudioReady(
|
|
41
49
|
AudioStream *oboeStream,
|
|
42
50
|
void *audioData,
|
|
43
51
|
int32_t numFrames) override;
|
|
44
52
|
|
|
45
53
|
private:
|
|
46
|
-
std::
|
|
54
|
+
std::string filePath_;
|
|
47
55
|
std::shared_ptr<AudioStream> mStream_;
|
|
48
|
-
|
|
56
|
+
|
|
57
|
+
std::shared_ptr<AndroidFileWriterBackend> fileWriter_;
|
|
58
|
+
std::shared_ptr<AndroidRecorderCallback> callback_;
|
|
59
|
+
|
|
49
60
|
int32_t streamSampleRate_;
|
|
50
61
|
int32_t streamChannelCount_;
|
|
51
62
|
int32_t streamMaxBufferSizeInFrames_;
|
|
63
|
+
|
|
64
|
+
facebook::jni::global_ref<NativeAudioRecorder> nativeAudioRecorder_;
|
|
52
65
|
};
|
|
53
66
|
|
|
54
67
|
} // namespace audioapi
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
+
#include <tuple>
|
|
3
4
|
#include <string>
|
|
4
5
|
#include <memory>
|
|
5
6
|
|
|
@@ -15,15 +16,18 @@ class AndroidFileWriterBackend {
|
|
|
15
16
|
|
|
16
17
|
virtual ~AndroidFileWriterBackend() = default;
|
|
17
18
|
|
|
18
|
-
virtual
|
|
19
|
-
virtual std::
|
|
19
|
+
virtual std::string openFile(int32_t streamSampleRate, int32_t streamChannelCount, int32_t streamMaxBufferSize) = 0;
|
|
20
|
+
virtual std::tuple<double, double> closeFile() = 0;
|
|
20
21
|
|
|
21
22
|
virtual bool writeAudioData(void *data, int numFrames) = 0;
|
|
22
23
|
|
|
23
24
|
std::string getFilePath() const { return filePath_; }
|
|
24
25
|
|
|
26
|
+
double getCurrentDuration() const { return static_cast<double>(framesWritten_.load()) / streamSampleRate_; }
|
|
27
|
+
|
|
25
28
|
protected:
|
|
26
29
|
std::string filePath_{""};
|
|
30
|
+
std::atomic<size_t> framesWritten_{0};
|
|
27
31
|
|
|
28
32
|
int32_t streamSampleRate_{0};
|
|
29
33
|
int32_t streamChannelCount_{0};
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
#include <android/log.h>
|
|
2
|
+
#include <audioapi/HostObjects/sources/AudioBufferHostObject.h>
|
|
3
|
+
#include <audioapi/android/core/utils/AndroidRecorderCallback.h>
|
|
4
|
+
#include <audioapi/events/AudioEventHandlerRegistry.h>
|
|
5
|
+
#include <audioapi/libs/miniaudio/miniaudio.h>
|
|
6
|
+
#include <audioapi/utils/AudioArray.h>
|
|
7
|
+
#include <audioapi/utils/AudioBus.h>
|
|
8
|
+
#include <audioapi/utils/CircularAudioArray.h>
|
|
9
|
+
|
|
10
|
+
#include <memory>
|
|
11
|
+
|
|
12
|
+
namespace audioapi {
|
|
13
|
+
|
|
14
|
+
AndroidRecorderCallback::AndroidRecorderCallback(
|
|
15
|
+
const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry,
|
|
16
|
+
float sampleRate,
|
|
17
|
+
size_t bufferLength,
|
|
18
|
+
size_t channelCount,
|
|
19
|
+
uint64_t callbackId)
|
|
20
|
+
: sampleRate_(sampleRate),
|
|
21
|
+
bufferLength_(bufferLength),
|
|
22
|
+
channelCount_(channelCount),
|
|
23
|
+
callbackId_(callbackId),
|
|
24
|
+
audioEventHandlerRegistry_(audioEventHandlerRegistry) {
|
|
25
|
+
ringBufferSize_ = std::max((int)bufferLength * 2, 8192);
|
|
26
|
+
circularBus_.resize(channelCount_);
|
|
27
|
+
|
|
28
|
+
for (size_t i = 0; i < channelCount_; ++i) {
|
|
29
|
+
auto busArray = std::make_shared<CircularAudioArray>(ringBufferSize_);
|
|
30
|
+
circularBus_[i] = busArray;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
AndroidRecorderCallback::~AndroidRecorderCallback() {
|
|
35
|
+
for (size_t i = 0; i < circularBus_.size(); ++i) {
|
|
36
|
+
circularBus_[i].reset();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
void AndroidRecorderCallback::prepare(
|
|
41
|
+
int32_t streamSampleRate,
|
|
42
|
+
int32_t streamChannelCount,
|
|
43
|
+
size_t maxInputBufferLength) {
|
|
44
|
+
ma_result result;
|
|
45
|
+
|
|
46
|
+
streamSampleRate_ = streamSampleRate;
|
|
47
|
+
streamChannelCount_ = streamChannelCount;
|
|
48
|
+
maxInputBufferLength_ = maxInputBufferLength;
|
|
49
|
+
|
|
50
|
+
ma_data_converter_config converterConfig = ma_data_converter_config_init(
|
|
51
|
+
ma_format_f32,
|
|
52
|
+
ma_format_f32,
|
|
53
|
+
streamChannelCount_,
|
|
54
|
+
channelCount_,
|
|
55
|
+
streamSampleRate_,
|
|
56
|
+
static_cast<int32_t>(sampleRate_));
|
|
57
|
+
|
|
58
|
+
converter_ = std::make_unique<ma_data_converter>();
|
|
59
|
+
result = ma_data_converter_init(&converterConfig, NULL, converter_.get());
|
|
60
|
+
|
|
61
|
+
if (result != MA_SUCCESS) {
|
|
62
|
+
__android_log_print(
|
|
63
|
+
ANDROID_LOG_ERROR,
|
|
64
|
+
"AndroidRecorderCallback",
|
|
65
|
+
"Failed to initialize miniaudio data converter");
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
ma_data_converter_get_expected_output_frame_count(
|
|
70
|
+
converter_.get(), maxInputBufferLength_, &processingBufferLength_);
|
|
71
|
+
|
|
72
|
+
processingBufferLength_ =
|
|
73
|
+
std::max(processingBufferLength_, (ma_uint64)maxInputBufferLength_);
|
|
74
|
+
|
|
75
|
+
deinterleavingArray_ = std::make_shared<AudioArray>(processingBufferLength_);
|
|
76
|
+
processingBuffer_ = ma_malloc(
|
|
77
|
+
processingBufferLength_ * channelCount_ *
|
|
78
|
+
ma_get_bytes_per_sample(ma_format_f32),
|
|
79
|
+
NULL);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
void AndroidRecorderCallback::cleanup() {
|
|
83
|
+
if (converter_ != nullptr) {
|
|
84
|
+
ma_data_converter_uninit(converter_.get(), NULL);
|
|
85
|
+
converter_.reset();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (processingBuffer_ != nullptr) {
|
|
89
|
+
ma_free(processingBuffer_, NULL);
|
|
90
|
+
processingBuffer_ = nullptr;
|
|
91
|
+
processingBufferLength_ = 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
for (size_t i = 0; i < circularBus_.size(); ++i) {
|
|
95
|
+
circularBus_[i].reset();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
void AndroidRecorderCallback::receiveAudioData(void *data, int numFrames) {
|
|
100
|
+
ma_uint64 inputFrameCount = numFrames;
|
|
101
|
+
ma_uint64 outputFrameCount = 0;
|
|
102
|
+
|
|
103
|
+
if ((float)streamSampleRate_ == sampleRate_ ||
|
|
104
|
+
streamChannelCount_ == channelCount_) {
|
|
105
|
+
deinterleaveAndWriteAudioData(data, numFrames);
|
|
106
|
+
emitAudioData();
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
ma_data_converter_get_expected_output_frame_count(
|
|
111
|
+
converter_.get(), inputFrameCount, &outputFrameCount);
|
|
112
|
+
|
|
113
|
+
ma_data_converter_process_pcm_frames(
|
|
114
|
+
converter_.get(),
|
|
115
|
+
data,
|
|
116
|
+
&inputFrameCount,
|
|
117
|
+
processingBuffer_,
|
|
118
|
+
&outputFrameCount);
|
|
119
|
+
|
|
120
|
+
deinterleaveAndWriteAudioData(
|
|
121
|
+
processingBuffer_, static_cast<int>(outputFrameCount));
|
|
122
|
+
emitAudioData();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
void AndroidRecorderCallback::deinterleaveAndWriteAudioData(
|
|
126
|
+
void *data,
|
|
127
|
+
int numFrames) {
|
|
128
|
+
auto *inputData = static_cast<float *>(data);
|
|
129
|
+
|
|
130
|
+
for (size_t channel = 0; channel < channelCount_; ++channel) {
|
|
131
|
+
float *channelData = deinterleavingArray_->getData();
|
|
132
|
+
|
|
133
|
+
for (int frame = 0; frame < numFrames; ++frame) {
|
|
134
|
+
channelData[frame] = inputData[frame * streamChannelCount_ + channel];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
circularBus_[channel]->push_back(channelData, numFrames);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
void AndroidRecorderCallback::emitAudioData() {
|
|
142
|
+
while (circularBus_[0]->getNumberOfAvailableFrames() >= bufferLength_) {
|
|
143
|
+
auto bus =
|
|
144
|
+
std::make_shared<AudioBus>(bufferLength_, channelCount_, sampleRate_);
|
|
145
|
+
|
|
146
|
+
for (int i = 0; i < channelCount_; ++i) {
|
|
147
|
+
auto *outputChannel = bus->getChannel(i)->getData();
|
|
148
|
+
circularBus_[i]->pop_front(outputChannel, bufferLength_);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
invokeCallback(bus, (int)bufferLength_);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
void AndroidRecorderCallback::invokeCallback(
|
|
156
|
+
const std::shared_ptr<AudioBus> &bus,
|
|
157
|
+
int numFrames) {
|
|
158
|
+
auto audioBuffer = std::make_shared<AudioBuffer>(bus);
|
|
159
|
+
auto audioBufferHostObject =
|
|
160
|
+
std::make_shared<AudioBufferHostObject>(audioBuffer);
|
|
161
|
+
|
|
162
|
+
std::unordered_map<std::string, EventValue> eventPayload = {};
|
|
163
|
+
eventPayload.insert({"buffer", audioBufferHostObject});
|
|
164
|
+
eventPayload.insert({"numFrames", numFrames});
|
|
165
|
+
|
|
166
|
+
if (audioEventHandlerRegistry_) {
|
|
167
|
+
audioEventHandlerRegistry_->invokeHandlerWithEventBody(
|
|
168
|
+
"audioReady", callbackId_, eventPayload);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
void AndroidRecorderCallback::sendRemainingData() {
|
|
173
|
+
auto numberOfFrames = circularBus_[0]->getNumberOfAvailableFrames();
|
|
174
|
+
auto bus = std::make_shared<AudioBus>(
|
|
175
|
+
circularBus_[0]->getNumberOfAvailableFrames(),
|
|
176
|
+
channelCount_,
|
|
177
|
+
sampleRate_);
|
|
178
|
+
|
|
179
|
+
for (int i = 0; i < channelCount_; ++i) {
|
|
180
|
+
auto *outputChannel = bus->getChannel(i)->getData();
|
|
181
|
+
circularBus_[i]->pop_front(outputChannel, numberOfFrames);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
invokeCallback(bus, (int)numberOfFrames);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
} // namespace audioapi
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
#include <audioapi/libs/miniaudio/miniaudio.h>
|
|
5
|
+
#include <memory>
|
|
6
|
+
#include <vector>
|
|
7
|
+
|
|
8
|
+
namespace audioapi {
|
|
9
|
+
|
|
10
|
+
class AudioBus;
|
|
11
|
+
class AudioArray;
|
|
12
|
+
class CircularAudioArray;
|
|
13
|
+
class AudioEventHandlerRegistry;
|
|
14
|
+
|
|
15
|
+
class AndroidRecorderCallback {
|
|
16
|
+
public:
|
|
17
|
+
AndroidRecorderCallback(
|
|
18
|
+
const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry,
|
|
19
|
+
float sampleRate,
|
|
20
|
+
size_t bufferLength,
|
|
21
|
+
size_t channelCount,
|
|
22
|
+
uint64_t callbackId);
|
|
23
|
+
~AndroidRecorderCallback();
|
|
24
|
+
|
|
25
|
+
void prepare(int32_t streamSampleRate, int32_t streamChannelCount, size_t maxInputBufferLength);
|
|
26
|
+
void cleanup();
|
|
27
|
+
|
|
28
|
+
void receiveAudioData(void *data, int numFrames);
|
|
29
|
+
void emitAudioData();
|
|
30
|
+
|
|
31
|
+
void invokeCallback(const std::shared_ptr<AudioBus> &bus, int numFrames);
|
|
32
|
+
void sendRemainingData();
|
|
33
|
+
|
|
34
|
+
private:
|
|
35
|
+
int32_t streamSampleRate_;
|
|
36
|
+
int32_t streamChannelCount_;
|
|
37
|
+
size_t maxInputBufferLength_;
|
|
38
|
+
|
|
39
|
+
float sampleRate_;
|
|
40
|
+
size_t bufferLength_;
|
|
41
|
+
size_t channelCount_;
|
|
42
|
+
uint64_t callbackId_;
|
|
43
|
+
size_t ringBufferSize_;
|
|
44
|
+
|
|
45
|
+
ma_uint64 processingBufferLength_{0};
|
|
46
|
+
void *processingBuffer_{nullptr};
|
|
47
|
+
|
|
48
|
+
std::unique_ptr<ma_data_converter> converter_{nullptr};
|
|
49
|
+
|
|
50
|
+
std::shared_ptr<AudioEventHandlerRegistry> audioEventHandlerRegistry_;
|
|
51
|
+
std::vector<std::shared_ptr<CircularAudioArray>> circularBus_;
|
|
52
|
+
std::shared_ptr<AudioArray> deinterleavingArray_;
|
|
53
|
+
|
|
54
|
+
void deinterleaveAndWriteAudioData(void *data, int numFrames);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
} // namespace audioapi
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
#include <android/log.h>
|
|
2
2
|
#include <cmath>
|
|
3
|
+
|
|
3
4
|
extern "C" {
|
|
4
|
-
#include <
|
|
5
|
+
#include <libavformat/avio.h>
|
|
5
6
|
}
|
|
6
7
|
|
|
7
8
|
#include <audioapi/android/core/utils/AndroidFileWriterBackend.h>
|
|
8
9
|
#include <audioapi/android/core/utils/ffmpegBackend/FFmpegAudioFileOptions.h>
|
|
9
10
|
#include <audioapi/android/core/utils/ffmpegBackend/FFmpegFileWriter.h>
|
|
10
11
|
|
|
12
|
+
constexpr double BYTES_TO_MB = 1024.0 * 1024.0;
|
|
13
|
+
|
|
11
14
|
namespace audioapi {
|
|
12
15
|
|
|
13
16
|
FFmpegAudioFileWriter::FFmpegAudioFileWriter(
|
|
@@ -20,16 +23,6 @@ FFmpegAudioFileWriter::FFmpegAudioFileWriter(
|
|
|
20
23
|
channelCount,
|
|
21
24
|
bitRate,
|
|
22
25
|
androidFlags) {
|
|
23
|
-
av_log_set_level(AV_LOG_DEBUG);
|
|
24
|
-
|
|
25
|
-
av_log_set_callback([](void *, int level, const char *fmt, va_list vl) {
|
|
26
|
-
if (level > av_log_get_level())
|
|
27
|
-
return;
|
|
28
|
-
char msg[1024];
|
|
29
|
-
vsnprintf(msg, sizeof(msg), fmt, vl);
|
|
30
|
-
__android_log_print(ANDROID_LOG_INFO, "FFmpeg", "%s", msg);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
26
|
fileOptions_ = std::make_shared<FFmpegAudioFileOptions>(
|
|
34
27
|
sampleRate, channelCount, bitRate, androidFlags);
|
|
35
28
|
}
|
|
@@ -39,7 +32,7 @@ FFmpegAudioFileWriter::~FFmpegAudioFileWriter() {
|
|
|
39
32
|
fileOptions_.reset();
|
|
40
33
|
}
|
|
41
34
|
|
|
42
|
-
|
|
35
|
+
std::string FFmpegAudioFileWriter::openFile(
|
|
43
36
|
int32_t streamSampleRate,
|
|
44
37
|
int32_t streamChannelCount,
|
|
45
38
|
int32_t streamMaxBufferSize) {
|
|
@@ -48,6 +41,7 @@ void FFmpegAudioFileWriter::openFile(
|
|
|
48
41
|
streamSampleRate_ = streamSampleRate;
|
|
49
42
|
streamChannelCount_ = streamChannelCount;
|
|
50
43
|
streamMaxBufferSize_ = streamMaxBufferSize;
|
|
44
|
+
framesWritten_.store(0);
|
|
51
45
|
nextPts_ = 0;
|
|
52
46
|
|
|
53
47
|
const AVCodec *codec = fileOptions_->getCodec();
|
|
@@ -64,7 +58,7 @@ void FFmpegAudioFileWriter::openFile(
|
|
|
64
58
|
"FFmpegFileWriter",
|
|
65
59
|
"Failed to allocate FFmpeg format context for file: %s",
|
|
66
60
|
filePath_.c_str());
|
|
67
|
-
return;
|
|
61
|
+
return "";
|
|
68
62
|
}
|
|
69
63
|
|
|
70
64
|
formatCtx_ = AVFormatContextPtr(rawFormatCtx);
|
|
@@ -77,7 +71,7 @@ void FFmpegAudioFileWriter::openFile(
|
|
|
77
71
|
ANDROID_LOG_ERROR,
|
|
78
72
|
"FFmpegFileWriter",
|
|
79
73
|
"Failed to allocate FFmpeg codec context for file");
|
|
80
|
-
return;
|
|
74
|
+
return "";
|
|
81
75
|
}
|
|
82
76
|
|
|
83
77
|
AVDictionary *codecOptions = nullptr;
|
|
@@ -104,7 +98,7 @@ void FFmpegAudioFileWriter::openFile(
|
|
|
104
98
|
"FFmpegFileWriter",
|
|
105
99
|
"Failed to open FFmpeg codec for file");
|
|
106
100
|
av_dict_free(&codecOptions);
|
|
107
|
-
return;
|
|
101
|
+
return "";
|
|
108
102
|
}
|
|
109
103
|
|
|
110
104
|
av_dict_free(&codecOptions);
|
|
@@ -115,7 +109,7 @@ void FFmpegAudioFileWriter::openFile(
|
|
|
115
109
|
ANDROID_LOG_ERROR,
|
|
116
110
|
"FFmpegFileWriter",
|
|
117
111
|
"Failed to copy codec parameters to stream for file");
|
|
118
|
-
return;
|
|
112
|
+
return "";
|
|
119
113
|
}
|
|
120
114
|
|
|
121
115
|
if (!(formatCtx_->oformat->flags & AVFMT_NOFILE)) {
|
|
@@ -125,7 +119,7 @@ void FFmpegAudioFileWriter::openFile(
|
|
|
125
119
|
"FFmpegFileWriter",
|
|
126
120
|
"Failed to open output file: %s",
|
|
127
121
|
filePath_.c_str());
|
|
128
|
-
return;
|
|
122
|
+
return "";
|
|
129
123
|
}
|
|
130
124
|
}
|
|
131
125
|
|
|
@@ -138,7 +132,7 @@ void FFmpegAudioFileWriter::openFile(
|
|
|
138
132
|
"FFmpegFileWriter",
|
|
139
133
|
"Failed to write header to file: %s",
|
|
140
134
|
filePath_.c_str());
|
|
141
|
-
return;
|
|
135
|
+
return "";
|
|
142
136
|
}
|
|
143
137
|
|
|
144
138
|
frame_ = AVFramePtr(av_frame_alloc());
|
|
@@ -171,7 +165,7 @@ void FFmpegAudioFileWriter::openFile(
|
|
|
171
165
|
"FFmpegFileWriter",
|
|
172
166
|
"Failed to initialize resampler for file: %s",
|
|
173
167
|
filePath_.c_str());
|
|
174
|
-
return;
|
|
168
|
+
return "";
|
|
175
169
|
}
|
|
176
170
|
|
|
177
171
|
int contextFrameRatio = 2;
|
|
@@ -198,11 +192,13 @@ void FFmpegAudioFileWriter::openFile(
|
|
|
198
192
|
fifoSize);
|
|
199
193
|
|
|
200
194
|
isFileOpen_.store(true);
|
|
195
|
+
|
|
196
|
+
return filePath_;
|
|
201
197
|
}
|
|
202
198
|
|
|
203
|
-
std::
|
|
199
|
+
std::tuple<double, double> FFmpegAudioFileWriter::closeFile() {
|
|
204
200
|
if (!isFileOpen()) {
|
|
205
|
-
return
|
|
201
|
+
return {0.0, 0.0};
|
|
206
202
|
}
|
|
207
203
|
|
|
208
204
|
isFileOpen_.store(false);
|
|
@@ -273,6 +269,9 @@ std::string FFmpegAudioFileWriter::closeFile() {
|
|
|
273
269
|
filePath_.c_str());
|
|
274
270
|
}
|
|
275
271
|
|
|
272
|
+
double fileSizeInMB = avio_size(formatCtx_->pb) / BYTES_TO_MB;
|
|
273
|
+
double durationInSeconds = getCurrentDuration();
|
|
274
|
+
|
|
276
275
|
if (formatCtx_ && formatCtx_->pb) {
|
|
277
276
|
avio_closep(&formatCtx_->pb);
|
|
278
277
|
}
|
|
@@ -284,10 +283,8 @@ std::string FFmpegAudioFileWriter::closeFile() {
|
|
|
284
283
|
formatCtx_.reset();
|
|
285
284
|
audioFifo_.reset();
|
|
286
285
|
|
|
287
|
-
std::string closedFilePath = filePath_;
|
|
288
286
|
filePath_ = "";
|
|
289
|
-
|
|
290
|
-
return closedFilePath;
|
|
287
|
+
return {fileSizeInMB, durationInSeconds};
|
|
291
288
|
}
|
|
292
289
|
|
|
293
290
|
bool FFmpegAudioFileWriter::writeAudioData(void *data, int numFrames) {
|
|
@@ -409,6 +406,7 @@ bool FFmpegAudioFileWriter::writeAudioData(void *data, int numFrames) {
|
|
|
409
406
|
av_frame_unref(frame_.get());
|
|
410
407
|
}
|
|
411
408
|
|
|
409
|
+
framesWritten_.fetch_add(numFrames);
|
|
412
410
|
return true;
|
|
413
411
|
}
|
|
414
412
|
|
|
@@ -13,6 +13,7 @@ extern "C" {
|
|
|
13
13
|
|
|
14
14
|
#include <string>
|
|
15
15
|
#include <memory>
|
|
16
|
+
#include <tuple>
|
|
16
17
|
|
|
17
18
|
namespace audioapi {
|
|
18
19
|
|
|
@@ -82,8 +83,8 @@ class FFmpegAudioFileWriter : public AndroidFileWriterBackend {
|
|
|
82
83
|
size_t androidFlags);
|
|
83
84
|
~FFmpegAudioFileWriter() override;
|
|
84
85
|
|
|
85
|
-
|
|
86
|
-
std::
|
|
86
|
+
std::string openFile(int32_t streamSampleRate, int32_t streamChannelCount, int32_t streamMaxBufferSize) override;
|
|
87
|
+
std::tuple<double, double> closeFile() override;
|
|
87
88
|
|
|
88
89
|
bool writeAudioData(void *data, int numFrames) override;
|
|
89
90
|
|