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
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#if !RN_AUDIO_API_FFMPEG_DISABLED
|
|
2
|
+
|
|
3
|
+
extern "C" {
|
|
4
|
+
#include <libavcodec/avcodec.h>
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
#include <audioapi/android/core/utils/ffmpegBackend/utils.h>
|
|
8
|
+
|
|
9
|
+
#include <audioapi/android/core/utils/FileOptions.h>
|
|
10
|
+
#include <audioapi/utils/AudioFileProperties.h>
|
|
11
|
+
|
|
12
|
+
#include <memory>
|
|
13
|
+
#include <string>
|
|
14
|
+
|
|
15
|
+
namespace audioapi::android::ffmpeg {
|
|
16
|
+
|
|
17
|
+
/// @brief Get the PCM codec ID based on the bit depth.
|
|
18
|
+
/// Note: This function returns only PCM codec IDs and its different from getSampleFormat. :)
|
|
19
|
+
/// @param properties The audio file properties.
|
|
20
|
+
/// @return The corresponding PCM AVCodecID.
|
|
21
|
+
AVCodecID getPCMCodecID(const std::shared_ptr<AudioFileProperties> &properties) {
|
|
22
|
+
switch (properties->bitDepth) {
|
|
23
|
+
case AudioFileProperties::BitDepth::Bit16:
|
|
24
|
+
return AV_CODEC_ID_PCM_S16LE;
|
|
25
|
+
case AudioFileProperties::BitDepth::Bit24:
|
|
26
|
+
return AV_CODEC_ID_PCM_S24LE;
|
|
27
|
+
case AudioFileProperties::BitDepth::Bit32:
|
|
28
|
+
return AV_CODEC_ID_PCM_F32LE;
|
|
29
|
+
default:
|
|
30
|
+
return AV_CODEC_ID_PCM_F32LE;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/// @brief Get the codec ID based on the audio file properties.
|
|
35
|
+
/// Note: PCM codec is used with wav and caf formats as both are uncompressed formats.
|
|
36
|
+
/// @param properties The audio file properties.
|
|
37
|
+
/// @return The corresponding AVCodecID.
|
|
38
|
+
AVCodecID getCodecID(const std::shared_ptr<AudioFileProperties> &properties) {
|
|
39
|
+
switch (properties->format) {
|
|
40
|
+
case AudioFileProperties::Format::WAV:
|
|
41
|
+
case AudioFileProperties::Format::CAF:
|
|
42
|
+
return getPCMCodecID(properties);
|
|
43
|
+
case AudioFileProperties::Format::M4A:
|
|
44
|
+
return AV_CODEC_ID_AAC;
|
|
45
|
+
case AudioFileProperties::Format::FLAC:
|
|
46
|
+
return AV_CODEC_ID_FLAC;
|
|
47
|
+
default:
|
|
48
|
+
return AV_CODEC_ID_AAC;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// @brief Returns the appropriate AVSampleFormat for codecs that require it. (non-PCM codecs)
|
|
53
|
+
/// @param properties The audio file properties.
|
|
54
|
+
/// @return The corresponding AVSampleFormat.
|
|
55
|
+
AVSampleFormat getSampleFormat(const std::shared_ptr<AudioFileProperties> &properties) {
|
|
56
|
+
if (properties->format == AudioFileProperties::Format::M4A) {
|
|
57
|
+
return AV_SAMPLE_FMT_FLTP;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
switch (properties->bitDepth) {
|
|
61
|
+
case AudioFileProperties::BitDepth::Bit16:
|
|
62
|
+
return AV_SAMPLE_FMT_S16;
|
|
63
|
+
case AudioFileProperties::BitDepth::Bit24:
|
|
64
|
+
return AV_SAMPLE_FMT_S32;
|
|
65
|
+
case AudioFileProperties::BitDepth::Bit32:
|
|
66
|
+
return AV_SAMPLE_FMT_FLT;
|
|
67
|
+
default:
|
|
68
|
+
return AV_SAMPLE_FMT_FLT;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/// @brief Finds the appropriate codec based on the audio file properties.
|
|
73
|
+
/// @param properties The audio file properties.
|
|
74
|
+
/// @return A pointer to the AVCodec.
|
|
75
|
+
const AVCodec *getCodec(const std::shared_ptr<AudioFileProperties> &properties) {
|
|
76
|
+
return avcodec_find_encoder(getCodecID(properties));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/// @brief Returns the appropriate muxer name based on the audio file properties.
|
|
80
|
+
/// Note: most of the time, the muxer name is same as the file extension, (M4A uses MP4 muxer :))
|
|
81
|
+
/// thus this is kept separate from format -> extension mapping.
|
|
82
|
+
/// @param properties The audio file properties.
|
|
83
|
+
/// @return The corresponding muxer name.
|
|
84
|
+
std::string getMuxerName(const std::shared_ptr<AudioFileProperties> &properties) {
|
|
85
|
+
switch (properties->format) {
|
|
86
|
+
case AudioFileProperties::Format::WAV:
|
|
87
|
+
return "wav";
|
|
88
|
+
case AudioFileProperties::Format::CAF:
|
|
89
|
+
return "caf";
|
|
90
|
+
case AudioFileProperties::Format::M4A:
|
|
91
|
+
return "mp4";
|
|
92
|
+
case AudioFileProperties::Format::FLAC:
|
|
93
|
+
return "flac";
|
|
94
|
+
default:
|
|
95
|
+
return "mp4";
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// @brief Parses the FFmpeg error int code into a human-readable string.
|
|
100
|
+
/// @param errorCode The FFmpeg error code.
|
|
101
|
+
/// @return A human-readable string describing the error.
|
|
102
|
+
std::string parseErrorCode(int errorCode) {
|
|
103
|
+
char errorBuffer[AV_ERROR_MAX_STRING_SIZE];
|
|
104
|
+
|
|
105
|
+
if (av_strerror(errorCode, errorBuffer, sizeof(errorBuffer)) < 0) {
|
|
106
|
+
return "Unknown FFmpeg error: " + std::to_string(errorCode);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return std::string(errorBuffer);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
} // namespace audioapi::android::ffmpeg
|
|
113
|
+
|
|
114
|
+
#endif // RN_AUDIO_API_FFMPEG_DISABLED
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
extern "C" {
|
|
4
|
+
#include <libavcodec/avcodec.h>
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
#include <string>
|
|
8
|
+
#include <memory>
|
|
9
|
+
|
|
10
|
+
namespace audioapi {
|
|
11
|
+
|
|
12
|
+
class AudioFileProperties;
|
|
13
|
+
|
|
14
|
+
namespace android::ffmpeg {
|
|
15
|
+
|
|
16
|
+
template <typename AVT>
|
|
17
|
+
struct AvDtor {
|
|
18
|
+
void operator()(AVT* ptr) const;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
template<typename AVT>
|
|
22
|
+
using av_unique_ptr = std::unique_ptr<AVT, AvDtor<AVT>>;
|
|
23
|
+
|
|
24
|
+
AVCodecID getPCMCodecID(const std::shared_ptr<AudioFileProperties> &properties);
|
|
25
|
+
AVCodecID getCodecID(const std::shared_ptr<AudioFileProperties> &properties);
|
|
26
|
+
AVSampleFormat getSampleFormat(const std::shared_ptr<AudioFileProperties> &properties);
|
|
27
|
+
const AVCodec* getCodec(const std::shared_ptr<AudioFileProperties> &properties);
|
|
28
|
+
std::string getMuxerName(const std::shared_ptr<AudioFileProperties> &properties);
|
|
29
|
+
|
|
30
|
+
std::string parseErrorCode(int errorCode);
|
|
31
|
+
|
|
32
|
+
} // namespace android::ffmpeg
|
|
33
|
+
|
|
34
|
+
} // namespace audioapi
|
package/android/src/main/cpp/audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.cpp
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
#include <android/log.h>
|
|
2
|
+
#include <audioapi/android/core/utils/AndroidFileWriterBackend.h>
|
|
3
|
+
#include <audioapi/android/core/utils/FileOptions.h>
|
|
4
|
+
#include <audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.h>
|
|
5
|
+
#include <audioapi/libs/miniaudio/miniaudio.h>
|
|
6
|
+
#include <audioapi/utils/AudioFileProperties.h>
|
|
7
|
+
#include <audioapi/utils/UnitConversion.h>
|
|
8
|
+
|
|
9
|
+
#include <cstdio>
|
|
10
|
+
#include <memory>
|
|
11
|
+
#include <string>
|
|
12
|
+
|
|
13
|
+
namespace audioapi {
|
|
14
|
+
|
|
15
|
+
/// @brief Get the encoding format based on the audio file properties (only WAV supported).
|
|
16
|
+
// Currently, miniaudio supports only WAV encoding, but out of convenience
|
|
17
|
+
// or potential future shenanigans, we keep this as a separate function.
|
|
18
|
+
inline ma_encoding_format getFormat(const std::shared_ptr<AudioFileProperties> &properties) {
|
|
19
|
+
return ma_encoding_format_wav;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/// @brief Get the data format based on the bit depth.
|
|
23
|
+
/// @param properties The audio file properties.
|
|
24
|
+
/// @return The corresponding ma_format.
|
|
25
|
+
inline ma_format getDataFormat(const std::shared_ptr<AudioFileProperties> &properties) {
|
|
26
|
+
switch (properties->bitDepth) {
|
|
27
|
+
case AudioFileProperties::BitDepth::Bit16:
|
|
28
|
+
return ma_format_s16;
|
|
29
|
+
|
|
30
|
+
case AudioFileProperties::BitDepth::Bit24:
|
|
31
|
+
return ma_format_s24;
|
|
32
|
+
|
|
33
|
+
case AudioFileProperties::BitDepth::Bit32:
|
|
34
|
+
return ma_format_f32;
|
|
35
|
+
|
|
36
|
+
default:
|
|
37
|
+
return ma_format_f32;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
MiniAudioFileWriter::MiniAudioFileWriter(
|
|
42
|
+
const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry,
|
|
43
|
+
const std::shared_ptr<AudioFileProperties> &fileProperties)
|
|
44
|
+
: AndroidFileWriterBackend(audioEventHandlerRegistry, fileProperties) {}
|
|
45
|
+
|
|
46
|
+
MiniAudioFileWriter::~MiniAudioFileWriter() {
|
|
47
|
+
isFileOpen_.store(false, std::memory_order_release);
|
|
48
|
+
fileProperties_.reset();
|
|
49
|
+
|
|
50
|
+
if (encoder_ != nullptr) {
|
|
51
|
+
ma_encoder_uninit(encoder_.get());
|
|
52
|
+
encoder_.reset();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (converter_ != nullptr) {
|
|
56
|
+
ma_data_converter_uninit(converter_.get(), NULL);
|
|
57
|
+
converter_.reset();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (processingBuffer_ != nullptr) {
|
|
61
|
+
ma_free(processingBuffer_, NULL);
|
|
62
|
+
processingBuffer_ = nullptr;
|
|
63
|
+
processingBufferLength_ = 0;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// @brief Opens the audio file for writing.
|
|
68
|
+
/// This method initializes the audio converter and encoder together with any
|
|
69
|
+
/// necessary buffers required during the writing process.
|
|
70
|
+
/// this method should be called only on the JS thread.
|
|
71
|
+
/// @param streamSampleRate The sample rate of the incoming audio stream.
|
|
72
|
+
/// @param streamChannelCount The channel count of the incoming audio stream.
|
|
73
|
+
/// @param streamMaxBufferSize The maximum buffer size of the incoming audio stream.
|
|
74
|
+
/// @return The status of the file opening operation.
|
|
75
|
+
OpenFileResult MiniAudioFileWriter::openFile(
|
|
76
|
+
float streamSampleRate,
|
|
77
|
+
int32_t streamChannelCount,
|
|
78
|
+
int32_t streamMaxBufferSize) {
|
|
79
|
+
streamSampleRate_ = streamSampleRate;
|
|
80
|
+
streamChannelCount_ = streamChannelCount;
|
|
81
|
+
streamMaxBufferSize_ = streamMaxBufferSize;
|
|
82
|
+
ma_result result;
|
|
83
|
+
framesWritten_.store(0, std::memory_order_release);
|
|
84
|
+
|
|
85
|
+
isConverterRequired_.store(
|
|
86
|
+
(streamSampleRate_ != fileProperties_->sampleRate) ||
|
|
87
|
+
(streamChannelCount_ != fileProperties_->channelCount) ||
|
|
88
|
+
(getDataFormat(fileProperties_) != ma_format_f32),
|
|
89
|
+
std::memory_order_release);
|
|
90
|
+
|
|
91
|
+
result = initializeConverterIfNeeded();
|
|
92
|
+
|
|
93
|
+
if (result != MA_SUCCESS) {
|
|
94
|
+
return OpenFileResult ::Err(
|
|
95
|
+
"Failed to initialize converter" + std::string(ma_result_description(result)));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
result = initializeEncoder();
|
|
99
|
+
|
|
100
|
+
if (result != MA_SUCCESS) {
|
|
101
|
+
return OpenFileResult ::Err(
|
|
102
|
+
"Failed to initialize encoder" + std::string(ma_result_description(result)));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
isFileOpen_.store(true, std::memory_order_release);
|
|
106
|
+
return OpenFileResult ::Ok(filePath_);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// @brief Closes the audio file.
|
|
110
|
+
/// This method finalizes the writing process, releases resources,
|
|
111
|
+
/// and retrieves the duration and size of the written audio file.
|
|
112
|
+
/// It should be called only on the JS thread.
|
|
113
|
+
/// @return The status of the file closing operation.
|
|
114
|
+
CloseFileResult MiniAudioFileWriter::closeFile() {
|
|
115
|
+
if (!isFileOpen()) {
|
|
116
|
+
return CloseFileResult ::Err("File is not open");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
isFileOpen_.store(false, std::memory_order_release);
|
|
120
|
+
|
|
121
|
+
if (encoder_ != nullptr) {
|
|
122
|
+
ma_encoder_uninit(encoder_.get());
|
|
123
|
+
encoder_.reset();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (converter_ != nullptr) {
|
|
127
|
+
ma_data_converter_uninit(converter_.get(), NULL);
|
|
128
|
+
converter_.reset();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (processingBuffer_ != nullptr) {
|
|
132
|
+
ma_free(processingBuffer_, NULL);
|
|
133
|
+
processingBuffer_ = nullptr;
|
|
134
|
+
processingBufferLength_ = 0;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Retrieve duration and file size
|
|
138
|
+
double durationInSeconds = 0.0;
|
|
139
|
+
double fileSizeInMB = 0.0;
|
|
140
|
+
|
|
141
|
+
ma_decoder decoder;
|
|
142
|
+
|
|
143
|
+
if (ma_decoder_init_file(filePath_.c_str(), NULL, &decoder) == MA_SUCCESS) {
|
|
144
|
+
ma_uint64 frameCount = 0;
|
|
145
|
+
|
|
146
|
+
if (ma_decoder_get_length_in_pcm_frames(&decoder, &frameCount) == MA_SUCCESS) {
|
|
147
|
+
durationInSeconds = static_cast<double>(frameCount) / decoder.outputSampleRate;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
ma_decoder_uninit(&decoder);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
FILE *file = fopen(filePath_.c_str(), "rb");
|
|
154
|
+
|
|
155
|
+
if (file != nullptr) {
|
|
156
|
+
fseek(file, 0, SEEK_END);
|
|
157
|
+
uint64_t fileSizeInBytes = ftell(file);
|
|
158
|
+
fclose(file);
|
|
159
|
+
fileSizeInMB = static_cast<double>(fileSizeInBytes) / MB_IN_BYTES;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
filePath_ = "";
|
|
163
|
+
return CloseFileResult ::Ok({fileSizeInMB, durationInSeconds});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/// @brief Writes audio data to the file.
|
|
167
|
+
/// If possible (sample format, channel count, and interleaving matches),
|
|
168
|
+
/// the data is written directly, otherwise in-memory conversion is performed first
|
|
169
|
+
/// It should be called only on the audio thread.
|
|
170
|
+
/// @param data Pointer to the audio data buffer. (Interleaved float32 format - as oboe likes it)
|
|
171
|
+
/// @param numFrames Number of audio frames to write.
|
|
172
|
+
/// @return True if the write operation was successful, false otherwise.
|
|
173
|
+
bool MiniAudioFileWriter::writeAudioData(void *data, int numFrames) {
|
|
174
|
+
ma_uint64 framesWritten = 0;
|
|
175
|
+
ma_result result;
|
|
176
|
+
|
|
177
|
+
if (!isFileOpen()) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (!isConverterRequired()) {
|
|
182
|
+
result = ma_encoder_write_pcm_frames(encoder_.get(), data, numFrames, &framesWritten);
|
|
183
|
+
|
|
184
|
+
if (result != MA_SUCCESS) {
|
|
185
|
+
invokeOnErrorCallback(
|
|
186
|
+
"Failed to write audio data to file: " + filePath_ +
|
|
187
|
+
std::string(ma_result_description(result)));
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
framesWritten_.fetch_add(numFrames, std::memory_order_acq_rel);
|
|
192
|
+
return result == MA_SUCCESS;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
ma_uint64 convertedFrameCount = convertBuffer(data, numFrames);
|
|
196
|
+
|
|
197
|
+
result = ma_encoder_write_pcm_frames(
|
|
198
|
+
encoder_.get(), processingBuffer_, convertedFrameCount, &framesWritten);
|
|
199
|
+
|
|
200
|
+
if (result != MA_SUCCESS) {
|
|
201
|
+
invokeOnErrorCallback(
|
|
202
|
+
"Failed to write converted audio data to file: " + filePath_ +
|
|
203
|
+
std::string(ma_result_description(result)));
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
framesWritten_.fetch_add(numFrames, std::memory_order_acq_rel);
|
|
208
|
+
return result == MA_SUCCESS;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/// @brief Converts the audio data buffer if necessary.
|
|
212
|
+
/// @param data Pointer to the audio data buffer.
|
|
213
|
+
/// @param numFrames Number of audio frames to convert.
|
|
214
|
+
/// @return The number of frames after conversion.
|
|
215
|
+
ma_uint64 MiniAudioFileWriter::convertBuffer(void *data, int numFrames) {
|
|
216
|
+
ma_uint64 inputFrameCount = numFrames;
|
|
217
|
+
ma_uint64 outputFrameCount = 0;
|
|
218
|
+
|
|
219
|
+
ma_data_converter_get_expected_output_frame_count(
|
|
220
|
+
converter_.get(), inputFrameCount, &outputFrameCount);
|
|
221
|
+
|
|
222
|
+
ma_data_converter_process_pcm_frames(
|
|
223
|
+
converter_.get(), data, &inputFrameCount, processingBuffer_, &outputFrameCount);
|
|
224
|
+
|
|
225
|
+
return outputFrameCount;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/// @brief Initializes the data converter if needed.
|
|
229
|
+
/// This method sets up the data converter and allocates, so it should be called
|
|
230
|
+
/// only on the JS thread. (during file opening)
|
|
231
|
+
/// @return MA_SUCCESS if initialization was successful, otherwise an error code.
|
|
232
|
+
ma_result MiniAudioFileWriter::initializeConverterIfNeeded() {
|
|
233
|
+
if (!isConverterRequired_) {
|
|
234
|
+
return MA_SUCCESS;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
ma_result result;
|
|
238
|
+
ma_format dataFormat = getDataFormat(fileProperties_);
|
|
239
|
+
|
|
240
|
+
ma_data_converter_config converterConfig = ma_data_converter_config_init(
|
|
241
|
+
ma_format_f32,
|
|
242
|
+
dataFormat,
|
|
243
|
+
streamChannelCount_,
|
|
244
|
+
fileProperties_->channelCount,
|
|
245
|
+
static_cast<int32_t>(streamSampleRate_),
|
|
246
|
+
fileProperties_->sampleRate);
|
|
247
|
+
|
|
248
|
+
converter_ = std::make_unique<ma_data_converter>();
|
|
249
|
+
result = ma_data_converter_init(&converterConfig, NULL, converter_.get());
|
|
250
|
+
|
|
251
|
+
if (result != MA_SUCCESS) {
|
|
252
|
+
return result;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
ma_data_converter_get_expected_output_frame_count(
|
|
256
|
+
converter_.get(), streamMaxBufferSize_, &processingBufferLength_);
|
|
257
|
+
|
|
258
|
+
processingBuffer_ = ma_malloc(
|
|
259
|
+
processingBufferLength_ * fileProperties_->channelCount * ma_get_bytes_per_sample(dataFormat),
|
|
260
|
+
NULL);
|
|
261
|
+
|
|
262
|
+
return MA_SUCCESS;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/// @brief Initializes the audio encoder.
|
|
266
|
+
/// This method sets up the audio encoder for writing to the file,
|
|
267
|
+
/// it should be called only on the JS thread. (during file opening)
|
|
268
|
+
/// @return MA_SUCCESS if initialization was successful, otherwise an error code.
|
|
269
|
+
ma_result MiniAudioFileWriter::initializeEncoder() {
|
|
270
|
+
ma_result result;
|
|
271
|
+
Result<std::string, std::string> filePathResult =
|
|
272
|
+
android::fileoptions::getFilePath(fileProperties_);
|
|
273
|
+
|
|
274
|
+
if (!filePathResult.is_ok()) {
|
|
275
|
+
return MA_ERROR;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
filePath_ = filePathResult.unwrap();
|
|
279
|
+
|
|
280
|
+
ma_encoder_config config = ma_encoder_config_init(
|
|
281
|
+
getFormat(fileProperties_),
|
|
282
|
+
getDataFormat(fileProperties_),
|
|
283
|
+
fileProperties_->channelCount,
|
|
284
|
+
fileProperties_->sampleRate);
|
|
285
|
+
|
|
286
|
+
encoder_ = std::make_unique<ma_encoder>();
|
|
287
|
+
result = ma_encoder_init_file(filePath_.c_str(), &config, encoder_.get());
|
|
288
|
+
|
|
289
|
+
return result;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
bool MiniAudioFileWriter::isConverterRequired() {
|
|
293
|
+
return isConverterRequired_.load(std::memory_order_acquire);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
} // namespace audioapi
|
package/android/src/main/cpp/audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.h
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <audioapi/android/core/utils/AndroidFileWriterBackend.h>
|
|
4
|
+
#include <audioapi/libs/miniaudio/miniaudio.h>
|
|
5
|
+
|
|
6
|
+
#include <atomic>
|
|
7
|
+
#include <string>
|
|
8
|
+
#include <memory>
|
|
9
|
+
#include <tuple>
|
|
10
|
+
|
|
11
|
+
namespace audioapi {
|
|
12
|
+
|
|
13
|
+
class MiniAudioFileWriter : public AndroidFileWriterBackend {
|
|
14
|
+
public:
|
|
15
|
+
explicit MiniAudioFileWriter(
|
|
16
|
+
const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry,
|
|
17
|
+
const std::shared_ptr<AudioFileProperties> &fileProperties);
|
|
18
|
+
~MiniAudioFileWriter();
|
|
19
|
+
|
|
20
|
+
OpenFileResult openFile(float streamSampleRate, int32_t streamChannelCount, int32_t streamMaxBufferSize) override;
|
|
21
|
+
CloseFileResult closeFile() override;
|
|
22
|
+
|
|
23
|
+
bool writeAudioData(void *data, int numFrames) override;
|
|
24
|
+
|
|
25
|
+
private:
|
|
26
|
+
std::atomic<bool> isConverterRequired_{false};
|
|
27
|
+
|
|
28
|
+
std::unique_ptr<ma_encoder> encoder_{nullptr};
|
|
29
|
+
std::unique_ptr<ma_data_converter> converter_{nullptr};
|
|
30
|
+
void *processingBuffer_{nullptr};
|
|
31
|
+
ma_uint64 processingBufferLength_{0};
|
|
32
|
+
|
|
33
|
+
ma_result initializeConverterIfNeeded();
|
|
34
|
+
ma_result initializeEncoder();
|
|
35
|
+
ma_uint64 convertBuffer(void *data, int numFrames);
|
|
36
|
+
|
|
37
|
+
bool isConverterRequired();
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
} // namespace audioapi
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <fbjni/fbjni.h>
|
|
4
|
+
#include <react/jni/CxxModuleWrapper.h>
|
|
5
|
+
#include <react/jni/JMessageQueueThread.h>
|
|
6
|
+
#include <memory>
|
|
7
|
+
#include <utility>
|
|
8
|
+
#include <string>
|
|
9
|
+
#include <unordered_map>
|
|
10
|
+
|
|
11
|
+
namespace audioapi {
|
|
12
|
+
|
|
13
|
+
using namespace facebook;
|
|
14
|
+
using namespace react;
|
|
15
|
+
|
|
16
|
+
class NativeFileInfo : public jni::JavaClass<NativeFileInfo> {
|
|
17
|
+
public:
|
|
18
|
+
static auto constexpr kJavaDescriptor =
|
|
19
|
+
"Lcom/swmansion/audioapi/system/NativeFileInfo;";
|
|
20
|
+
|
|
21
|
+
static std::string getFilesDir() {
|
|
22
|
+
static const auto method = javaClassStatic()->getStaticMethod<jni::JString()>("getFilesDir");
|
|
23
|
+
return method(javaClassStatic())->toStdString();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static std::string getCacheDir() {
|
|
27
|
+
static const auto method = javaClassStatic()->getStaticMethod<jni::JString()>("getCacheDir");
|
|
28
|
+
return method(javaClassStatic())->toStdString();
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
} // namespace audioapi
|
|
@@ -12,6 +12,7 @@ import com.facebook.react.module.annotations.ReactModule
|
|
|
12
12
|
import com.facebook.react.turbomodule.core.CallInvokerHolderImpl
|
|
13
13
|
import com.swmansion.audioapi.system.ForegroundServiceManager
|
|
14
14
|
import com.swmansion.audioapi.system.MediaSessionManager
|
|
15
|
+
import com.swmansion.audioapi.system.NativeFileInfo
|
|
15
16
|
import com.swmansion.audioapi.system.PermissionRequestListener
|
|
16
17
|
import java.lang.ref.WeakReference
|
|
17
18
|
|
|
@@ -63,6 +64,7 @@ class AudioAPIModule(
|
|
|
63
64
|
|
|
64
65
|
override fun install(): Boolean {
|
|
65
66
|
MediaSessionManager.initialize(WeakReference(this), reactContext)
|
|
67
|
+
NativeFileInfo.initialize(reactContext)
|
|
66
68
|
injectJSIBindings()
|
|
67
69
|
|
|
68
70
|
return true
|
|
@@ -13,6 +13,7 @@ class AudioFocusListener(
|
|
|
13
13
|
private val audioAPIModule: WeakReference<AudioAPIModule>,
|
|
14
14
|
) : AudioManager.OnAudioFocusChangeListener {
|
|
15
15
|
private var focusRequest: AudioFocusRequest? = null
|
|
16
|
+
private var isTransientLoss: Boolean = false
|
|
16
17
|
|
|
17
18
|
override fun onAudioFocusChange(focusChange: Int) {
|
|
18
19
|
Log.d("AudioFocusListener", "onAudioFocusChange: $focusChange")
|
|
@@ -21,7 +22,8 @@ class AudioFocusListener(
|
|
|
21
22
|
val body =
|
|
22
23
|
HashMap<String, Any>().apply {
|
|
23
24
|
put("type", "began")
|
|
24
|
-
put("
|
|
25
|
+
put("shouldResume", false)
|
|
26
|
+
isTransientLoss = false
|
|
25
27
|
}
|
|
26
28
|
audioAPIModule.get()?.invokeHandlerWithEventNameAndEventBody("interruption", body)
|
|
27
29
|
}
|
|
@@ -30,7 +32,8 @@ class AudioFocusListener(
|
|
|
30
32
|
val body =
|
|
31
33
|
HashMap<String, Any>().apply {
|
|
32
34
|
put("type", "began")
|
|
33
|
-
put("
|
|
35
|
+
put("shouldResume", false)
|
|
36
|
+
isTransientLoss = true
|
|
34
37
|
}
|
|
35
38
|
audioAPIModule.get()?.invokeHandlerWithEventNameAndEventBody("interruption", body)
|
|
36
39
|
}
|
|
@@ -39,7 +42,8 @@ class AudioFocusListener(
|
|
|
39
42
|
val body =
|
|
40
43
|
HashMap<String, Any>().apply {
|
|
41
44
|
put("type", "ended")
|
|
42
|
-
put("
|
|
45
|
+
put("shouldResume", isTransientLoss)
|
|
46
|
+
isTransientLoss = false
|
|
43
47
|
}
|
|
44
48
|
audioAPIModule.get()?.invokeHandlerWithEventNameAndEventBody("interruption", body)
|
|
45
49
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
package com.swmansion.audioapi.system
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
4
|
+
import java.lang.ref.WeakReference
|
|
5
|
+
|
|
6
|
+
object NativeFileInfo {
|
|
7
|
+
private lateinit var reactContext: WeakReference<ReactApplicationContext>
|
|
8
|
+
|
|
9
|
+
fun initialize(reactContext: WeakReference<ReactApplicationContext>) {
|
|
10
|
+
this.reactContext = reactContext
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@JvmStatic
|
|
14
|
+
fun getFilesDir(): String = reactContext.get()?.filesDir?.absolutePath ?: ""
|
|
15
|
+
|
|
16
|
+
@JvmStatic
|
|
17
|
+
fun getCacheDir(): String = reactContext.get()?.cacheDir?.absolutePath ?: ""
|
|
18
|
+
}
|
|
@@ -30,10 +30,12 @@ class RecordingNotificationReceiver : BroadcastReceiver() {
|
|
|
30
30
|
Log.d(TAG, "Recording notification dismissed by user")
|
|
31
31
|
audioAPIModule?.invokeHandlerWithEventNameAndEventBody("recordingNotificationDismissed", mapOf())
|
|
32
32
|
}
|
|
33
|
+
|
|
33
34
|
RecordingNotification.ACTION_START -> {
|
|
34
35
|
Log.d(TAG, "Start recording action received")
|
|
35
36
|
audioAPIModule?.invokeHandlerWithEventNameAndEventBody("recordingNotificationStart", mapOf())
|
|
36
37
|
}
|
|
38
|
+
|
|
37
39
|
RecordingNotification.ACTION_STOP -> {
|
|
38
40
|
Log.d(TAG, "Stop recording action received")
|
|
39
41
|
audioAPIModule?.invokeHandlerWithEventNameAndEventBody("recordingNotificationStop", mapOf())
|