react-native-audio-api 0.11.0 โ 0.11.2
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/README.md +14 -0
- package/RNAudioAPI.podspec +65 -40
- package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +9 -6
- package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.h +1 -1
- package/android/src/main/cpp/audioapi/android/core/utils/AndroidFileWriterBackend.h +1 -1
- package/android/src/main/cpp/audioapi/android/core/utils/FileOptions.cpp +7 -3
- package/android/src/main/cpp/audioapi/android/core/utils/FileOptions.h +1 -1
- package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/FFmpegFileWriter.cpp +4 -2
- package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/FFmpegFileWriter.h +1 -1
- package/android/src/main/cpp/audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.cpp +5 -4
- package/android/src/main/cpp/audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.h +2 -2
- package/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt +2 -0
- package/common/cpp/audioapi/HostObjects/AudioNodeHostObject.cpp +6 -0
- package/common/cpp/audioapi/HostObjects/AudioNodeHostObject.h +1 -0
- package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp +6 -0
- package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.h +2 -0
- package/common/cpp/audioapi/HostObjects/inputs/AudioRecorderHostObject.cpp +2 -1
- package/common/cpp/audioapi/core/inputs/AudioRecorder.h +1 -1
- package/ios/audioapi/ios/AudioAPIModule.mm +22 -1
- package/ios/audioapi/ios/core/IOSAudioRecorder.h +1 -1
- package/ios/audioapi/ios/core/IOSAudioRecorder.mm +5 -4
- package/ios/audioapi/ios/core/utils/FileOptions.h +3 -1
- package/ios/audioapi/ios/core/utils/FileOptions.mm +9 -3
- package/ios/audioapi/ios/core/utils/IOSFileWriter.h +2 -1
- package/ios/audioapi/ios/core/utils/IOSFileWriter.mm +5 -2
- package/ios/audioapi/ios/system/AudioEngine.mm +4 -2
- package/ios/audioapi/ios/system/AudioSessionManager.h +1 -1
- package/ios/audioapi/ios/system/AudioSessionManager.mm +4 -11
- package/lib/commonjs/core/AudioRecorder.js +3 -3
- package/lib/commonjs/core/AudioRecorder.js.map +1 -1
- package/lib/commonjs/plugin/withAudioAPI.js +6 -1
- package/lib/commonjs/plugin/withAudioAPI.js.map +1 -1
- package/lib/commonjs/system/AudioManager.js +8 -2
- package/lib/commonjs/system/AudioManager.js.map +1 -1
- package/lib/commonjs/system/errors.js +79 -0
- package/lib/commonjs/system/errors.js.map +1 -0
- package/lib/module/core/AudioRecorder.js +3 -4
- package/lib/module/core/AudioRecorder.js.map +1 -1
- package/lib/module/plugin/withAudioAPI.js +7 -2
- package/lib/module/plugin/withAudioAPI.js.map +1 -1
- package/lib/module/system/AudioManager.js +8 -2
- package/lib/module/system/AudioManager.js.map +1 -1
- package/lib/module/system/errors.js +73 -0
- package/lib/module/system/errors.js.map +1 -0
- package/lib/typescript/core/AudioRecorder.d.ts +2 -2
- package/lib/typescript/core/AudioRecorder.d.ts.map +1 -1
- package/lib/typescript/interfaces.d.ts +1 -1
- package/lib/typescript/interfaces.d.ts.map +1 -1
- package/lib/typescript/plugin/withAudioAPI.d.ts.map +1 -1
- package/lib/typescript/system/AudioManager.d.ts +2 -2
- package/lib/typescript/system/AudioManager.d.ts.map +1 -1
- package/lib/typescript/system/errors.d.ts +12 -0
- package/lib/typescript/system/errors.d.ts.map +1 -0
- package/lib/typescript/system/types.d.ts +1 -1
- package/lib/typescript/system/types.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +3 -0
- package/lib/typescript/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/scripts/rnaa_utils.rb +50 -0
- package/src/core/AudioRecorder.ts +4 -3
- package/src/interfaces.ts +1 -1
- package/src/plugin/withAudioAPI.ts +12 -4
- package/src/system/AudioManager.ts +15 -7
- package/src/system/errors.ts +111 -0
- package/src/system/types.ts +1 -1
- package/src/types.ts +4 -0
package/README.md
CHANGED
|
@@ -36,6 +36,20 @@ check out the [Getting Started](https://docs.swmansion.com/react-native-audio-ap
|
|
|
36
36
|
- **Noise Cancellation ๐ฆ**<br />
|
|
37
37
|
System-based active noise and echo cancellation support
|
|
38
38
|
|
|
39
|
+
### <a href="https://github.com/software-mansion/react-native-audio-api/releases/tag/0.11.0"><img src="https://img.shields.io/badge/Released_in-0.11.0-green" /></a>
|
|
40
|
+
|
|
41
|
+
- **Recording to file ๐ผ**<br />
|
|
42
|
+
Fully customizable recording to a file. Choose a file format, quality and other parameters. Fully integrated with the audio graph.
|
|
43
|
+
|
|
44
|
+
- **Playback and recording notification system ๐**<br />
|
|
45
|
+
Control playback and recording from the notification center / lock screen and create custom notification layouts.
|
|
46
|
+
|
|
47
|
+
- **Wave Shaper Node ๐ธ**<br />
|
|
48
|
+
Create custom nonlinear distortion effects. No need to buy guitar pedals anymore!
|
|
49
|
+
|
|
50
|
+
- **IIR Filter Node ๐งช**<br />
|
|
51
|
+
Implement custom digital filters using feedforward and feedback coefficients.
|
|
52
|
+
|
|
39
53
|
### <a href="https://github.com/software-mansion/react-native-audio-api/releases/tag/0.10.0"><img src="https://img.shields.io/badge/Released_in-0.10.0-green" /></a>
|
|
40
54
|
|
|
41
55
|
- **Decoding and utility modules ๐ง**<br />
|
package/RNAudioAPI.podspec
CHANGED
|
@@ -2,15 +2,18 @@ require "json"
|
|
|
2
2
|
require_relative './scripts/rnaa_utils'
|
|
3
3
|
|
|
4
4
|
package_json = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
5
|
+
$audio_api_config = find_audio_api_config()
|
|
5
6
|
|
|
6
7
|
$new_arch_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == '1'
|
|
7
8
|
$RN_AUDIO_API_FFMPEG_DISABLED = ENV['DISABLE_AUDIOAPI_FFMPEG'].nil? ? false : ENV['DISABLE_AUDIOAPI_FFMPEG'] == '1' # false by default
|
|
8
9
|
|
|
9
|
-
folly_flags = "-DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32"
|
|
10
10
|
fabric_flags = $new_arch_enabled ? '-DRCT_NEW_ARCH_ENABLED' : ''
|
|
11
11
|
version_flag = "-DAUDIOAPI_VERSION=#{package_json['version']}"
|
|
12
|
+
ios_min_version = '14.0'
|
|
13
|
+
|
|
14
|
+
worklets_enabled = $audio_api_config[:worklets_enabled]
|
|
15
|
+
worklets_preprocessor_flag = worklets_enabled ? '-DRN_AUDIO_API_ENABLE_WORKLETS=1' : ''
|
|
12
16
|
|
|
13
|
-
worklets_preprocessor_flag = check_if_worklets_enabled() ? '-DRN_AUDIO_API_ENABLE_WORKLETS=1' : ''
|
|
14
17
|
ffmpeg_flag = $RN_AUDIO_API_FFMPEG_DISABLED ? '-DRN_AUDIO_API_FFMPEG_DISABLED=1' : ''
|
|
15
18
|
skip_ffmpeg_argument = $RN_AUDIO_API_FFMPEG_DISABLED ? 'skipffmpeg' : ''
|
|
16
19
|
|
|
@@ -22,7 +25,7 @@ Pod::Spec.new do |s|
|
|
|
22
25
|
s.license = package_json["license"]
|
|
23
26
|
s.authors = package_json["author"]
|
|
24
27
|
|
|
25
|
-
s.platforms = { :ios =>
|
|
28
|
+
s.platforms = { :ios => ios_min_version }
|
|
26
29
|
s.source = { :git => "https://github.com/software-mansion/react-native-audio-api.git", :tag => "#{s.version}" }
|
|
27
30
|
|
|
28
31
|
s.subspec "audioapi" do |ss|
|
|
@@ -45,9 +48,11 @@ Pod::Spec.new do |s|
|
|
|
45
48
|
end
|
|
46
49
|
end
|
|
47
50
|
|
|
48
|
-
|
|
51
|
+
if worklets_enabled
|
|
52
|
+
s.dependency 'RNWorklets'
|
|
53
|
+
end
|
|
49
54
|
|
|
50
|
-
s.
|
|
55
|
+
s.ios.frameworks = 'Accelerate', 'AVFoundation', 'MediaPlayer'
|
|
51
56
|
|
|
52
57
|
s.prepare_command = <<-CMD
|
|
53
58
|
chmod +x scripts/download-prebuilt-binaries.sh
|
|
@@ -67,41 +72,61 @@ Pod::Spec.new do |s|
|
|
|
67
72
|
'common/cpp/audioapi/external/ffmpeg_ios/libavutil.xcframework',
|
|
68
73
|
'common/cpp/audioapi/external/ffmpeg_ios/libswresample.xcframework'
|
|
69
74
|
]
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
75
|
+
|
|
76
|
+
s.pod_target_xcconfig = {
|
|
77
|
+
"USE_HEADERMAP" => "YES",
|
|
78
|
+
"DEFINES_MODULE" => "YES",
|
|
79
|
+
"HEADER_SEARCH_PATHS" => [
|
|
80
|
+
'"$(PODS_TARGET_SRCROOT)/ReactCommon"',
|
|
81
|
+
'"$(PODS_TARGET_SRCROOT)"',
|
|
82
|
+
'"$(PODS_ROOT)/RCT-Folly"',
|
|
83
|
+
'"$(PODS_ROOT)/boost"',
|
|
84
|
+
'"$(PODS_ROOT)/boost-for-react-native"',
|
|
85
|
+
'"$(PODS_ROOT)/DoubleConversion"',
|
|
86
|
+
'"$(PODS_ROOT)/Headers/Private/React-Core"',
|
|
87
|
+
'"$(PODS_ROOT)/Headers/Private/Yoga"',
|
|
88
|
+
"\"$(PODS_TARGET_SRCROOT)/#{external_dir_relative}/include\"",
|
|
89
|
+
"\"$(PODS_TARGET_SRCROOT)/#{external_dir_relative}/include/opus\"",
|
|
90
|
+
"\"$(PODS_TARGET_SRCROOT)/#{external_dir_relative}/include/vorbis\"",
|
|
91
|
+
]
|
|
92
|
+
.concat($RN_AUDIO_API_FFMPEG_DISABLED ? [] : ["\"$(PODS_TARGET_SRCROOT)/#{external_dir_relative}/ffmpeg_include\""])
|
|
93
|
+
.concat(worklets_enabled ? ['"$(PODS_ROOT)/Headers/Public/RNWorklets"'] : [])
|
|
94
|
+
.join(' '),
|
|
95
|
+
"CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
|
|
96
|
+
"GCC_PREPROCESSOR_DEFINITIONS" => '$(inherited) HAVE_ACCELERATE=1',
|
|
97
|
+
'OTHER_CFLAGS' => "$(inherited) #{fabric_flags} #{version_flag} #{worklets_preprocessor_flag} #{ffmpeg_flag}",
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
s.xcconfig = {
|
|
101
|
+
"HEADER_SEARCH_PATHS" => [
|
|
102
|
+
'"$(PODS_ROOT)/boost"',
|
|
103
|
+
'"$(PODS_ROOT)/boost-for-react-native"',
|
|
104
|
+
'"$(PODS_ROOT)/glog"',
|
|
105
|
+
'"$(PODS_ROOT)/RCT-Folly"',
|
|
106
|
+
'"$(PODS_ROOT)/Headers/Public/React-hermes"',
|
|
107
|
+
'"$(PODS_ROOT)/Headers/Public/hermes-engine"',
|
|
108
|
+
"\"$(PODS_ROOT)/#{$audio_api_config[:react_native_common_dir]}\"",
|
|
109
|
+
"\"$(PODS_ROOT)/#{$audio_api_config[:dynamic_frameworks_audio_api_dir]}/ios\"",
|
|
110
|
+
"\"$(PODS_ROOT)/#{$audio_api_config[:dynamic_frameworks_audio_api_dir]}/common/cpp\"",
|
|
111
|
+
]
|
|
112
|
+
.concat(worklets_enabled ? [
|
|
113
|
+
'"$(PODS_ROOT)/Headers/Public/RNWorklets"',
|
|
114
|
+
"\"$(PODS_ROOT)/#{$audio_api_config[:dynamic_frameworks_worklets_dir]}/apple\"",
|
|
115
|
+
"\"$(PODS_ROOT)/#{$audio_api_config[:dynamic_frameworks_worklets_dir]}/Common/cpp\""
|
|
116
|
+
] : [])
|
|
117
|
+
.join(' '),
|
|
118
|
+
'OTHER_LDFLAGS' => %W[
|
|
119
|
+
$(inherited)
|
|
120
|
+
-force_load #{lib_dir}/libopusfile.a
|
|
121
|
+
-force_load #{lib_dir}/libopus.a
|
|
122
|
+
-force_load #{lib_dir}/libogg.a
|
|
123
|
+
-force_load #{lib_dir}/libvorbis.a
|
|
124
|
+
-force_load #{lib_dir}/libvorbisenc.a
|
|
125
|
+
-force_load #{lib_dir}/libvorbisfile.a
|
|
126
|
+
-force_load #{lib_dir}/libssl.a
|
|
127
|
+
-force_load #{lib_dir}/libcrypto.a
|
|
128
|
+
].join(" "),
|
|
129
|
+
}
|
|
105
130
|
# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
|
|
106
131
|
# See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
|
|
107
132
|
install_modules_dependencies(s)
|
|
@@ -102,7 +102,7 @@ Result<NoneType, std::string> AndroidAudioRecorder::openAudioStream() {
|
|
|
102
102
|
/// RN side requires their "file://" prefix, but sometimes it returned raw path.
|
|
103
103
|
/// Most likely this was due to alpha version mistakes, but in case of problems leaving this here. (ใ _ ใ)
|
|
104
104
|
/// @returns On success, returns the file URI where the recording is being saved (if file output is enabled).
|
|
105
|
-
Result<std::string, std::string> AndroidAudioRecorder::start() {
|
|
105
|
+
Result<std::string, std::string> AndroidAudioRecorder::start(const std::string &fileNameOverride) {
|
|
106
106
|
std::scoped_lock startLock(callbackMutex_, fileWriterMutex_, adapterNodeMutex_);
|
|
107
107
|
|
|
108
108
|
if (!isIdle()) {
|
|
@@ -120,9 +120,12 @@ Result<std::string, std::string> AndroidAudioRecorder::start() {
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
if (usesFileOutput()) {
|
|
123
|
-
auto fileResult =
|
|
124
|
-
|
|
125
|
-
|
|
123
|
+
auto fileResult = std::static_pointer_cast<AndroidFileWriterBackend>(fileWriter_)
|
|
124
|
+
->openFile(
|
|
125
|
+
streamSampleRate_,
|
|
126
|
+
streamChannelCount_,
|
|
127
|
+
streamMaxBufferSizeInFrames_,
|
|
128
|
+
fileNameOverride);
|
|
126
129
|
|
|
127
130
|
if (!fileResult.is_ok()) {
|
|
128
131
|
return Result<std::string, std::string>::Err(
|
|
@@ -164,7 +167,7 @@ Result<std::tuple<std::string, double, double>, std::string> AndroidAudioRecorde
|
|
|
164
167
|
double outputFileSize = 0.0;
|
|
165
168
|
double outputDuration = 0.0;
|
|
166
169
|
|
|
167
|
-
if (
|
|
170
|
+
if (isIdle()) {
|
|
168
171
|
return Result<std::tuple<std::string, double, double>, std::string>::Err(
|
|
169
172
|
"Recorder is not in recording state.");
|
|
170
173
|
}
|
|
@@ -228,7 +231,7 @@ Result<std::string, std::string> AndroidAudioRecorder::enableFileOutput(
|
|
|
228
231
|
if (!isIdle()) {
|
|
229
232
|
auto fileResult =
|
|
230
233
|
std::static_pointer_cast<AndroidFileWriterBackend>(fileWriter_)
|
|
231
|
-
->openFile(streamSampleRate_, streamChannelCount_, streamMaxBufferSizeInFrames_);
|
|
234
|
+
->openFile(streamSampleRate_, streamChannelCount_, streamMaxBufferSizeInFrames_, "");
|
|
232
235
|
|
|
233
236
|
if (!fileResult.is_ok()) {
|
|
234
237
|
return Result<std::string, std::string>::Err(
|
|
@@ -26,7 +26,7 @@ class AndroidAudioRecorder : public oboe::AudioStreamCallback, public AudioRecor
|
|
|
26
26
|
~AndroidAudioRecorder() override;
|
|
27
27
|
void cleanup();
|
|
28
28
|
|
|
29
|
-
Result<std::string, std::string> start() override;
|
|
29
|
+
Result<std::string, std::string> start(const std::string &fileNameOverride) override;
|
|
30
30
|
Result<std::tuple<std::string, double, double>, std::string> stop() override;
|
|
31
31
|
|
|
32
32
|
Result<std::string, std::string> enableFileOutput(std::shared_ptr<AudioFileProperties> properties) override;
|
|
@@ -17,7 +17,7 @@ class AndroidFileWriterBackend : public AudioFileWriter {
|
|
|
17
17
|
const std::shared_ptr<AudioFileProperties> &fileProperties)
|
|
18
18
|
: AudioFileWriter(audioEventHandlerRegistry, fileProperties) {}
|
|
19
19
|
|
|
20
|
-
virtual OpenFileResult openFile(float streamSampleRate, int32_t streamChannelCount, int32_t streamMaxBufferSize) = 0;
|
|
20
|
+
virtual OpenFileResult openFile(float streamSampleRate, int32_t streamChannelCount, int32_t streamMaxBufferSize, const std::string &fileNameOverride) = 0;
|
|
21
21
|
virtual bool writeAudioData(void *data, int numFrames) = 0;
|
|
22
22
|
|
|
23
23
|
std::string getFilePath() const override { return filePath_; }
|
|
@@ -63,7 +63,8 @@ std::string getFileExtension(const std::shared_ptr<AudioFileProperties> &propert
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
Result<std::string, std::string> getFilePath(
|
|
66
|
-
const std::shared_ptr<AudioFileProperties> &properties
|
|
66
|
+
const std::shared_ptr<AudioFileProperties> &properties,
|
|
67
|
+
const std::string &fileNameOverride) {
|
|
67
68
|
std::string directory = getDirectory(properties);
|
|
68
69
|
std::string subDirectory = std::format("{}/{}", directory, properties->subDirectory);
|
|
69
70
|
std::string fileTimestamp = getTimestampString();
|
|
@@ -75,8 +76,11 @@ Result<std::string, std::string> getFilePath(
|
|
|
75
76
|
return Result<std::string, std::string>::Err(result.unwrap_err());
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
auto filePath =
|
|
79
|
-
"{}/{}
|
|
79
|
+
auto filePath = fileNameOverride.length() > 0
|
|
80
|
+
? std::format("{}/{}.{}", subDirectory, fileNameOverride, extension)
|
|
81
|
+
: std::format(
|
|
82
|
+
"{}/{}_{}.{}", subDirectory, properties->fileNamePrefix, fileTimestamp, extension);
|
|
83
|
+
|
|
80
84
|
return Result<std::string, std::string>::Ok(filePath);
|
|
81
85
|
}
|
|
82
86
|
|
|
@@ -15,7 +15,7 @@ std::string getTimestampString();
|
|
|
15
15
|
|
|
16
16
|
std::string getDirectory(const std::shared_ptr<AudioFileProperties> &properties);
|
|
17
17
|
std::string getFileExtension(const std::shared_ptr<AudioFileProperties> &properties);
|
|
18
|
-
Result<std::string, std::string> getFilePath(const std::shared_ptr<AudioFileProperties> &properties);
|
|
18
|
+
Result<std::string, std::string> getFilePath(const std::shared_ptr<AudioFileProperties> &properties, const std::string &fileNameOverride);
|
|
19
19
|
|
|
20
20
|
} // namespace android::fileoptions
|
|
21
21
|
|
|
@@ -53,14 +53,16 @@ FFmpegAudioFileWriter::~FFmpegAudioFileWriter() {
|
|
|
53
53
|
OpenFileResult FFmpegAudioFileWriter::openFile(
|
|
54
54
|
float streamSampleRate,
|
|
55
55
|
int32_t streamChannelCount,
|
|
56
|
-
int32_t streamMaxBufferSize
|
|
56
|
+
int32_t streamMaxBufferSize,
|
|
57
|
+
const std::string &fileNameOverride) {
|
|
57
58
|
streamSampleRate_ = streamSampleRate;
|
|
58
59
|
streamChannelCount_ = streamChannelCount;
|
|
59
60
|
streamMaxBufferSize_ = streamMaxBufferSize;
|
|
60
61
|
framesWritten_.store(0, std::memory_order_release);
|
|
61
62
|
nextPts_ = 0;
|
|
62
63
|
Result<NoneType, std::string> result = Result<NoneType, std::string>::Ok(None);
|
|
63
|
-
Result<std::string, std::string> filePathResult =
|
|
64
|
+
Result<std::string, std::string> filePathResult =
|
|
65
|
+
fileoptions::getFilePath(fileProperties_, fileNameOverride);
|
|
64
66
|
|
|
65
67
|
if (!filePathResult.is_ok()) {
|
|
66
68
|
return OpenFileResult::Err(filePathResult.unwrap_err());
|
|
@@ -29,7 +29,7 @@ class FFmpegAudioFileWriter : public AndroidFileWriterBackend {
|
|
|
29
29
|
const std::shared_ptr<AudioFileProperties> &fileProperties);
|
|
30
30
|
~FFmpegAudioFileWriter();
|
|
31
31
|
|
|
32
|
-
OpenFileResult openFile(float streamSampleRate, int32_t streamChannelCount, int32_t streamMaxBufferSize) override;
|
|
32
|
+
OpenFileResult openFile(float streamSampleRate, int32_t streamChannelCount, int32_t streamMaxBufferSize, const std::string &fileNameOverride) override;
|
|
33
33
|
CloseFileResult closeFile() override;
|
|
34
34
|
|
|
35
35
|
bool writeAudioData(void *data, int numFrames) override;
|
package/android/src/main/cpp/audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.cpp
CHANGED
|
@@ -75,7 +75,8 @@ MiniAudioFileWriter::~MiniAudioFileWriter() {
|
|
|
75
75
|
OpenFileResult MiniAudioFileWriter::openFile(
|
|
76
76
|
float streamSampleRate,
|
|
77
77
|
int32_t streamChannelCount,
|
|
78
|
-
int32_t streamMaxBufferSize
|
|
78
|
+
int32_t streamMaxBufferSize,
|
|
79
|
+
const std::string &fileNameOverride) {
|
|
79
80
|
streamSampleRate_ = streamSampleRate;
|
|
80
81
|
streamChannelCount_ = streamChannelCount;
|
|
81
82
|
streamMaxBufferSize_ = streamMaxBufferSize;
|
|
@@ -95,7 +96,7 @@ OpenFileResult MiniAudioFileWriter::openFile(
|
|
|
95
96
|
"Failed to initialize converter" + std::string(ma_result_description(result)));
|
|
96
97
|
}
|
|
97
98
|
|
|
98
|
-
result = initializeEncoder();
|
|
99
|
+
result = initializeEncoder(fileNameOverride);
|
|
99
100
|
|
|
100
101
|
if (result != MA_SUCCESS) {
|
|
101
102
|
return OpenFileResult ::Err(
|
|
@@ -266,10 +267,10 @@ ma_result MiniAudioFileWriter::initializeConverterIfNeeded() {
|
|
|
266
267
|
/// This method sets up the audio encoder for writing to the file,
|
|
267
268
|
/// it should be called only on the JS thread. (during file opening)
|
|
268
269
|
/// @return MA_SUCCESS if initialization was successful, otherwise an error code.
|
|
269
|
-
ma_result MiniAudioFileWriter::initializeEncoder() {
|
|
270
|
+
ma_result MiniAudioFileWriter::initializeEncoder(const std::string &fileNameOverride) {
|
|
270
271
|
ma_result result;
|
|
271
272
|
Result<std::string, std::string> filePathResult =
|
|
272
|
-
android::fileoptions::getFilePath(fileProperties_);
|
|
273
|
+
android::fileoptions::getFilePath(fileProperties_, fileNameOverride);
|
|
273
274
|
|
|
274
275
|
if (!filePathResult.is_ok()) {
|
|
275
276
|
return MA_ERROR;
|
package/android/src/main/cpp/audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.h
CHANGED
|
@@ -17,7 +17,7 @@ class MiniAudioFileWriter : public AndroidFileWriterBackend {
|
|
|
17
17
|
const std::shared_ptr<AudioFileProperties> &fileProperties);
|
|
18
18
|
~MiniAudioFileWriter();
|
|
19
19
|
|
|
20
|
-
OpenFileResult openFile(float streamSampleRate, int32_t streamChannelCount, int32_t streamMaxBufferSize) override;
|
|
20
|
+
OpenFileResult openFile(float streamSampleRate, int32_t streamChannelCount, int32_t streamMaxBufferSize, const std::string &fileNameOverride) override;
|
|
21
21
|
CloseFileResult closeFile() override;
|
|
22
22
|
|
|
23
23
|
bool writeAudioData(void *data, int numFrames) override;
|
|
@@ -31,7 +31,7 @@ class MiniAudioFileWriter : public AndroidFileWriterBackend {
|
|
|
31
31
|
ma_uint64 processingBufferLength_{0};
|
|
32
32
|
|
|
33
33
|
ma_result initializeConverterIfNeeded();
|
|
34
|
-
ma_result initializeEncoder();
|
|
34
|
+
ma_result initializeEncoder(const std::string &fileNameOverride);
|
|
35
35
|
ma_uint64 convertBuffer(void *data, int numFrames);
|
|
36
36
|
|
|
37
37
|
bool isConverterRequired();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
package com.swmansion.audioapi
|
|
2
2
|
|
|
3
|
+
import androidx.annotation.RequiresPermission
|
|
3
4
|
import com.facebook.jni.HybridData
|
|
4
5
|
import com.facebook.react.bridge.Arguments
|
|
5
6
|
import com.facebook.react.bridge.LifecycleEventListener
|
|
@@ -149,6 +150,7 @@ class AudioAPIModule(
|
|
|
149
150
|
}
|
|
150
151
|
|
|
151
152
|
// Notification system methods
|
|
153
|
+
@RequiresPermission(android.Manifest.permission.POST_NOTIFICATIONS)
|
|
152
154
|
override fun showNotification(
|
|
153
155
|
type: String?,
|
|
154
156
|
key: String?,
|
|
@@ -19,6 +19,12 @@ AudioNodeHostObject::AudioNodeHostObject(const std::shared_ptr<AudioNode> &node)
|
|
|
19
19
|
JSI_EXPORT_FUNCTION(AudioNodeHostObject, disconnect));
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
// Explicitly define destructor here, as they to exist in order to act as a
|
|
23
|
+
// "key function" for the audio classes - this allow for RTTI to work
|
|
24
|
+
// properly across dynamic library boundaries (i.e. dynamic_cast that is used by
|
|
25
|
+
// isHostObject method), android specific issue
|
|
26
|
+
AudioNodeHostObject::~AudioNodeHostObject() = default;
|
|
27
|
+
|
|
22
28
|
JSI_PROPERTY_GETTER_IMPL(AudioNodeHostObject, numberOfInputs) {
|
|
23
29
|
return {node_->getNumberOfInputs()};
|
|
24
30
|
}
|
|
@@ -14,6 +14,7 @@ class AudioNode;
|
|
|
14
14
|
class AudioNodeHostObject : public JsiHostObject {
|
|
15
15
|
public:
|
|
16
16
|
explicit AudioNodeHostObject(const std::shared_ptr<AudioNode> &node);
|
|
17
|
+
~AudioNodeHostObject() override;
|
|
17
18
|
|
|
18
19
|
JSI_PROPERTY_GETTER_DECL(numberOfInputs);
|
|
19
20
|
JSI_PROPERTY_GETTER_DECL(numberOfOutputs);
|
|
@@ -62,6 +62,12 @@ BaseAudioContextHostObject::BaseAudioContextHostObject(
|
|
|
62
62
|
JSI_EXPORT_FUNCTION(BaseAudioContextHostObject, createWaveShaper));
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
// Explicitly define destructors here, as they to exist in order to act as a
|
|
66
|
+
// "key function" for the audio classes - this allow for RTTI to work
|
|
67
|
+
// properly across dynamic library boundaries (i.e. dynamic_cast that is used by
|
|
68
|
+
// isHostObject method), android specific issue
|
|
69
|
+
BaseAudioContextHostObject::~BaseAudioContextHostObject() = default;
|
|
70
|
+
|
|
65
71
|
JSI_PROPERTY_GETTER_IMPL(BaseAudioContextHostObject, destination) {
|
|
66
72
|
auto destination = std::make_shared<AudioDestinationNodeHostObject>(context_->getDestination());
|
|
67
73
|
return jsi::Object::createFromHostObject(runtime, destination);
|
|
@@ -21,6 +21,8 @@ class BaseAudioContextHostObject : public JsiHostObject {
|
|
|
21
21
|
jsi::Runtime *runtime,
|
|
22
22
|
const std::shared_ptr<react::CallInvoker> &callInvoker);
|
|
23
23
|
|
|
24
|
+
~BaseAudioContextHostObject() override;
|
|
25
|
+
|
|
24
26
|
JSI_PROPERTY_GETTER_DECL(destination);
|
|
25
27
|
JSI_PROPERTY_GETTER_DECL(state);
|
|
26
28
|
JSI_PROPERTY_GETTER_DECL(sampleRate);
|
|
@@ -41,7 +41,8 @@ AudioRecorderHostObject::AudioRecorderHostObject(
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
JSI_HOST_FUNCTION_IMPL(AudioRecorderHostObject, start) {
|
|
44
|
-
auto
|
|
44
|
+
auto fileNameOverride = count > 0 ? args[0].getString(runtime).utf8(runtime) : "";
|
|
45
|
+
auto result = audioRecorder_->start(fileNameOverride);
|
|
45
46
|
auto jsResult = jsi::Object(runtime);
|
|
46
47
|
|
|
47
48
|
jsResult.setProperty(
|
|
@@ -25,7 +25,7 @@ class AudioRecorder {
|
|
|
25
25
|
: audioEventHandlerRegistry_(audioEventHandlerRegistry) {}
|
|
26
26
|
virtual ~AudioRecorder() = default;
|
|
27
27
|
|
|
28
|
-
virtual Result<std::string, std::string> start() = 0;
|
|
28
|
+
virtual Result<std::string, std::string> start(const std::string &fileNameOverride) = 0;
|
|
29
29
|
virtual Result<std::tuple<std::string, double, double>, std::string> stop() = 0;
|
|
30
30
|
|
|
31
31
|
virtual Result<std::string, std::string> enableFileOutput(
|
|
@@ -123,7 +123,28 @@ RCT_EXPORT_METHOD(
|
|
|
123
123
|
resolve reject : (RCTPromiseRejectBlock)reject)
|
|
124
124
|
{
|
|
125
125
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
126
|
-
|
|
126
|
+
NSError *error = nil;
|
|
127
|
+
|
|
128
|
+
auto success = [self.audioSessionManager setActive:enabled error:&error];
|
|
129
|
+
|
|
130
|
+
if (!success) {
|
|
131
|
+
NSDictionary *meta = @{
|
|
132
|
+
@"nativeCode" : @(error.code),
|
|
133
|
+
@"nativeDomain" : error.domain ?: @"",
|
|
134
|
+
@"nativeDesc" : error.description ?: @"",
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
NSError *jsError =
|
|
138
|
+
[NSError errorWithDomain:@"AudioAPIModule"
|
|
139
|
+
code:error.code
|
|
140
|
+
userInfo:@{
|
|
141
|
+
NSLocalizedDescriptionKey : @"Failed to set audio session active state",
|
|
142
|
+
@"meta" : meta,
|
|
143
|
+
}];
|
|
144
|
+
|
|
145
|
+
reject(@"E_AUDIO_SESSION", @"Failed to set audio session active state", jsError);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
127
148
|
|
|
128
149
|
resolve(@(success));
|
|
129
150
|
});
|
|
@@ -27,7 +27,7 @@ class IOSAudioRecorder : public AudioRecorder {
|
|
|
27
27
|
IOSAudioRecorder(const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry);
|
|
28
28
|
~IOSAudioRecorder() override;
|
|
29
29
|
|
|
30
|
-
Result<std::string, std::string> start() override;
|
|
30
|
+
Result<std::string, std::string> start(const std::string &fileNameOverride = "") override;
|
|
31
31
|
Result<std::tuple<std::string, double, double>, std::string> stop() override;
|
|
32
32
|
|
|
33
33
|
Result<std::string, std::string> enableFileOutput(
|
|
@@ -71,7 +71,7 @@ IOSAudioRecorder::~IOSAudioRecorder()
|
|
|
71
71
|
/// @brief Starts the audio recording process and prepares necessary resources.
|
|
72
72
|
/// This method should be called from the JS thread only.
|
|
73
73
|
/// @returns Result containing the file path if recording started successfully, or an error message.
|
|
74
|
-
Result<std::string, std::string> IOSAudioRecorder::start()
|
|
74
|
+
Result<std::string, std::string> IOSAudioRecorder::start(const std::string &fileNameOverride)
|
|
75
75
|
{
|
|
76
76
|
if (!isIdle()) {
|
|
77
77
|
return Result<std::string, std::string>::Err("Recorder is already recording");
|
|
@@ -104,7 +104,7 @@ Result<std::string, std::string> IOSAudioRecorder::start()
|
|
|
104
104
|
|
|
105
105
|
if (usesFileOutput()) {
|
|
106
106
|
auto fileResult = std::static_pointer_cast<IOSFileWriter>(fileWriter_)
|
|
107
|
-
->openFile(inputFormat, maxInputBufferLength);
|
|
107
|
+
->openFile(inputFormat, maxInputBufferLength, fileNameOverride);
|
|
108
108
|
|
|
109
109
|
if (fileResult.is_err()) {
|
|
110
110
|
return Result<std::string, std::string>::Err(
|
|
@@ -191,8 +191,9 @@ Result<std::string, std::string> IOSAudioRecorder::enableFileOutput(
|
|
|
191
191
|
fileWriter_ = std::make_shared<IOSFileWriter>(audioEventHandlerRegistry_, properties);
|
|
192
192
|
|
|
193
193
|
if (!isIdle()) {
|
|
194
|
-
auto result =
|
|
195
|
-
|
|
194
|
+
auto result =
|
|
195
|
+
std::static_pointer_cast<IOSFileWriter>(fileWriter_)
|
|
196
|
+
->openFile([nativeRecorder_ getInputFormat], [nativeRecorder_ getBufferSize], "");
|
|
196
197
|
|
|
197
198
|
if (result.is_err()) {
|
|
198
199
|
return Result<std::string, std::string>::Err(
|
|
@@ -22,7 +22,9 @@ NSInteger getBitDepth(const std::shared_ptr<AudioFileProperties> &properties);
|
|
|
22
22
|
float getSampleRate(const std::shared_ptr<AudioFileProperties> &properties);
|
|
23
23
|
|
|
24
24
|
NSDictionary *getFileSettings(const std::shared_ptr<AudioFileProperties> &properties);
|
|
25
|
-
NSURL *getFileURL(
|
|
25
|
+
NSURL *getFileURL(
|
|
26
|
+
const std::shared_ptr<AudioFileProperties> &properties,
|
|
27
|
+
const std::string &fileNameOverride);
|
|
26
28
|
NSSearchPathDirectory getDirectory(const std::shared_ptr<AudioFileProperties> &properties);
|
|
27
29
|
|
|
28
30
|
NSString *getDateString();
|
|
@@ -139,7 +139,9 @@ NSDictionary *getFileSettings(const std::shared_ptr<AudioFileProperties> &proper
|
|
|
139
139
|
return settings;
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
NSURL *getFileURL(
|
|
142
|
+
NSURL *getFileURL(
|
|
143
|
+
const std::shared_ptr<AudioFileProperties> &properties,
|
|
144
|
+
const std::string &fileNameOverride)
|
|
143
145
|
{
|
|
144
146
|
NSError *error = nil;
|
|
145
147
|
|
|
@@ -164,8 +166,12 @@ NSURL *getFileURL(const std::shared_ptr<AudioFileProperties> &properties)
|
|
|
164
166
|
NSString *timestamp = getTimestampString();
|
|
165
167
|
NSString *fileExtension = getFileExtension(properties);
|
|
166
168
|
|
|
167
|
-
NSString *fileName =
|
|
168
|
-
[NSString stringWithFormat:@"
|
|
169
|
+
NSString *fileName = fileNameOverride.length() > 0
|
|
170
|
+
? [NSString stringWithFormat:@"%@.%@",
|
|
171
|
+
[NSString stringWithUTF8String:fileNameOverride.c_str()],
|
|
172
|
+
fileExtension]
|
|
173
|
+
: [NSString stringWithFormat:@"%@_%@.%@", fileNamePrefix, timestamp, fileExtension];
|
|
174
|
+
|
|
169
175
|
return [directoryURL URLByAppendingPathComponent:fileName];
|
|
170
176
|
}
|
|
171
177
|
|
|
@@ -29,7 +29,8 @@ class IOSFileWriter : public AudioFileWriter {
|
|
|
29
29
|
|
|
30
30
|
Result<std::string, std::string> openFile(
|
|
31
31
|
AVAudioFormat *bufferFormat,
|
|
32
|
-
size_t maxInputBufferLength
|
|
32
|
+
size_t maxInputBufferLength,
|
|
33
|
+
const std::string &fileNameOverride);
|
|
33
34
|
Result<std::tuple<double, double>, std::string> closeFile() override;
|
|
34
35
|
|
|
35
36
|
bool writeAudioData(const AudioBufferList *audioBufferList, int numFrames);
|
|
@@ -32,7 +32,10 @@ IOSFileWriter::~IOSFileWriter()
|
|
|
32
32
|
/// @param bufferFormat The audio format of the input buffer.
|
|
33
33
|
/// @param maxInputBufferLength The maximum length of the input buffer in frames.
|
|
34
34
|
/// @returns An OpenFileResult indicating success with the file path or an error message.
|
|
35
|
-
OpenFileResult IOSFileWriter::openFile(
|
|
35
|
+
OpenFileResult IOSFileWriter::openFile(
|
|
36
|
+
AVAudioFormat *bufferFormat,
|
|
37
|
+
size_t maxInputBufferLength,
|
|
38
|
+
const std::string &fileNameOverride)
|
|
36
39
|
{
|
|
37
40
|
@autoreleasepool {
|
|
38
41
|
if (audioFile_ != nil) {
|
|
@@ -44,7 +47,7 @@ OpenFileResult IOSFileWriter::openFile(AVAudioFormat *bufferFormat, size_t maxIn
|
|
|
44
47
|
|
|
45
48
|
NSError *error = nil;
|
|
46
49
|
NSDictionary *settings = ios::fileoptions::getFileSettings(fileProperties_);
|
|
47
|
-
fileURL_ = ios::fileoptions::getFileURL(fileProperties_);
|
|
50
|
+
fileURL_ = ios::fileoptions::getFileURL(fileProperties_, fileNameOverride);
|
|
48
51
|
|
|
49
52
|
if (fileProperties_->sampleRate == 0 || fileProperties_->channelCount == 0) {
|
|
50
53
|
return OpenFileResult::Err(
|
|
@@ -38,7 +38,7 @@ static AudioEngine *_sharedInstance = nil;
|
|
|
38
38
|
self.sourceFormats = nil;
|
|
39
39
|
self.inputNode = nil;
|
|
40
40
|
|
|
41
|
-
[self.sessionManager setActive:false];
|
|
41
|
+
[self.sessionManager setActive:false error:nil];
|
|
42
42
|
self.sessionManager = nil;
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -182,7 +182,9 @@ static AudioEngine *_sharedInstance = nil;
|
|
|
182
182
|
return true;
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
if (![self.sessionManager setActive:true]) {
|
|
185
|
+
if (![self.sessionManager setActive:true error:&error]) {
|
|
186
|
+
// TODO: return user facing error
|
|
187
|
+
NSLog(@"Error while activating audio session: %@", [error debugDescription]);
|
|
186
188
|
return false;
|
|
187
189
|
}
|
|
188
190
|
|
|
@@ -109,15 +109,14 @@ static AudioSessionManager *_sharedInstance = nil;
|
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
- (bool)setActive:(bool)active
|
|
112
|
+
- (bool)setActive:(bool)active error:(NSError **)error
|
|
113
113
|
{
|
|
114
|
+
bool success = false;
|
|
115
|
+
|
|
114
116
|
if (!self.shouldManageSession) {
|
|
115
117
|
return true;
|
|
116
118
|
}
|
|
117
119
|
|
|
118
|
-
NSError *error = nil;
|
|
119
|
-
bool success = false;
|
|
120
|
-
|
|
121
120
|
if (self.isActive == active) {
|
|
122
121
|
return true;
|
|
123
122
|
}
|
|
@@ -130,18 +129,12 @@ static AudioSessionManager *_sharedInstance = nil;
|
|
|
130
129
|
}
|
|
131
130
|
}
|
|
132
131
|
|
|
133
|
-
success = [self.audioSession setActive:active error
|
|
132
|
+
success = [self.audioSession setActive:active error:error];
|
|
134
133
|
|
|
135
134
|
if (success) {
|
|
136
135
|
self.isActive = active;
|
|
137
136
|
}
|
|
138
137
|
|
|
139
|
-
if (error != nil) {
|
|
140
|
-
NSLog(@"[AudioSessionManager] setting session as %@ failed", active ? @"ACTIVE" : @"INACTIVE");
|
|
141
|
-
} else {
|
|
142
|
-
NSLog(@"[AudioSessionManager] session is %@", active ? @"ACTIVE" : @"INACTIVE");
|
|
143
|
-
}
|
|
144
|
-
|
|
145
138
|
return success;
|
|
146
139
|
}
|
|
147
140
|
|