Skip to content

Commit 4d86c14

Browse files
vltkvMaciej Makowskimdydek
authored
Feat/opus decoder (#580)
* feat: add custom decoder * feat: add necessary opus libs * feat: add cmakelists * feat: add necessary opus libs * feat: statically link precompiled libraries for iOS Simulator * feat: add opus decoding example * feat: add versions for android * feat: fix CMakeLists * feat: add vorbis library * feat: add vorbis library * feat: add versions for all supported architectures * feat: remove source code of external libraries * feat: cleanups * fix: comment android logs * fix: apply review suggestions * fix: test fix --------- Co-authored-by: Maciej Makowski <maciej.makowski2608@gmail.com> Co-authored-by: michal <dydmichal@gmail.com>
1 parent 5a218c6 commit 4d86c14

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+9150
-162
lines changed

apps/fabric-example/ios/Podfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2961,7 +2961,7 @@ SPEC CHECKSUMS:
29612961
ReactAppDependencyProvider: 3267432b637c9b38e86961b287f784ee1b08dde0
29622962
ReactCodegen: d82f538f70f00484d418803f74b5a0ea09cc8689
29632963
ReactCommon: b028d09a66e60ebd83ca59d8cc9a1216360db147
2964-
RNAudioAPI: 5d67008b0e2d36e255efb1b157c801e6f2dfa938
2964+
RNAudioAPI: 7ba48681da626677a851e7b91d7910b776410976
29652965
RNGestureHandler: eeb622199ef1fb3a076243131095df1c797072f0
29662966
RNReanimated: 402e6a3b84071df4da6264630a1b99962a113d2d
29672967
RNScreens: ee2abe7e0c548eed14e92742e81ed991165c56aa

packages/audiodocs/docs/core/base-audio-context.mdx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ The above method lets you create `StereoPannerNode`.
136136

137137
#### Returns [`StereoPannerNode`](/docs/effects/stereo-panner-node).
138138

139-
### `createBiquadFilter
139+
### `createBiquadFilter`
140140

141141
The above method lets you create `BiquadFilterNode`.
142142

@@ -147,6 +147,8 @@ Supported file formats:
147147
- mp3
148148
- wav
149149
- flac
150+
- opus
151+
- ogg
150152
:::
151153

152154
### `decodeAudioData`

packages/react-native-audio-api/RNAudioAPI.podspec

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,39 @@ Pod::Spec.new do |s|
3535

3636
s.compiler_flags = "#{folly_flags}"
3737

38-
s.pod_target_xcconfig = {
39-
"USE_HEADERMAP" => "YES",
40-
"CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
41-
"GCC_PREPROCESSOR_DEFINITIONS" => '$(inherited) HAVE_ACCELERATE=1',
42-
"HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/common/cpp\" \"$(PODS_TARGET_SRCROOT)/ios\"",
43-
'OTHER_CFLAGS' => "$(inherited) #{folly_flags} #{fabric_flags} #{version_flag}"
44-
}
38+
external_dir = "$(PODS_TARGET_SRCROOT)/common/cpp/audioapi/external"
39+
# lib_dir = "$(PODS_TARGET_SRCROOT)/common/cpp/audioapi/external/$(PLATFORM_NAME)"
40+
lib_dir = "$(SRCROOT)/../../../packages/react-native-audio-api/common/cpp/audioapi/external/$(PLATFORM_NAME)"
4541

42+
43+
s.pod_target_xcconfig = {
44+
"USE_HEADERMAP" => "YES",
45+
"CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
46+
"GCC_PREPROCESSOR_DEFINITIONS" => '$(inherited) HAVE_ACCELERATE=1',
47+
"HEADER_SEARCH_PATHS" => %W[
48+
$(PODS_TARGET_SRCROOT)/common/cpp
49+
$(PODS_TARGET_SRCROOT)/ios
50+
#{external_dir}/include
51+
#{external_dir}/include/opus
52+
#{external_dir}/include/vorbis
53+
].join(" "),
54+
'OTHER_CFLAGS' => "$(inherited) #{folly_flags} #{fabric_flags} #{version_flag}"
55+
}
56+
57+
s.user_target_xcconfig = {
58+
'OTHER_LDFLAGS' => %W[
59+
$(inherited)
60+
-force_load #{lib_dir}/libopusfile.a
61+
-force_load #{lib_dir}/libopus.a
62+
-force_load #{lib_dir}/libogg.a
63+
-force_load #{lib_dir}/libvorbis.a
64+
-force_load #{lib_dir}/libvorbisenc.a
65+
-force_load #{lib_dir}/libvorbisfile.a
66+
].join(" ")
67+
}
4668
# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
4769
# See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
4870
install_modules_dependencies(s)
4971
end
72+
73+

packages/react-native-audio-api/android/src/main/cpp/audioapi/CMakeLists.txt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,17 @@ cmake_minimum_required(VERSION 3.12.0)
33
file(GLOB_RECURSE ANDROID_CPP_SOURCES CONFIGURE_DEPENDS "${ANDROID_CPP_DIR}/audioapi/*.cpp")
44
file(GLOB_RECURSE COMMON_CPP_SOURCES CONFIGURE_DEPENDS "${COMMON_CPP_DIR}/audioapi/*.cpp" "${COMMON_CPP_DIR}/audioapi/*.c")
55

6+
set(EXTERNAL_DIR ${COMMON_CPP_DIR}/audioapi/external)
7+
set(ARCH_LIB_DIR ${EXTERNAL_DIR}/${ANDROID_ABI})
8+
set(INCLUDE_DIR ${EXTERNAL_DIR}/include)
9+
610
add_library(react-native-audio-api SHARED ${ANDROID_CPP_SOURCES} ${COMMON_CPP_SOURCES})
711

12+
foreach(lib IN ITEMS opus opusfile ogg vorbis vorbisenc vorbisfile)
13+
add_library(${lib} STATIC IMPORTED)
14+
set_target_properties(${lib} PROPERTIES IMPORTED_LOCATION ${ARCH_LIB_DIR}/lib${lib}.a)
15+
endforeach()
16+
817
find_package(ReactAndroid REQUIRED CONFIG)
918
find_package(fbjni REQUIRED CONFIG)
1019
find_package(oboe REQUIRED CONFIG)
@@ -14,6 +23,9 @@ target_include_directories(
1423
PRIVATE
1524
"${COMMON_CPP_DIR}"
1625
"${ANDROID_CPP_DIR}"
26+
"${INCLUDE_DIR}"
27+
"${INCLUDE_DIR}/opus"
28+
"${INCLUDE_DIR}/vorbis"
1729
"${REACT_NATIVE_DIR}/ReactCommon"
1830
"${REACT_NATIVE_DIR}/ReactAndroid/src/main/jni/react/turbomodule"
1931
"${REACT_NATIVE_DIR}/ReactCommon/callinvoker"
@@ -40,4 +52,13 @@ else()
4052
)
4153
endif()
4254

43-
target_link_libraries(react-native-audio-api ${LINK_LIBRARIES} ${RN_VERSION_LINK_LIBRARIES})
55+
target_link_libraries(react-native-audio-api
56+
${LINK_LIBRARIES}
57+
${RN_VERSION_LINK_LIBRARIES}
58+
opusfile
59+
opus
60+
ogg
61+
vorbis
62+
vorbisenc
63+
vorbisfile
64+
)

packages/react-native-audio-api/android/src/main/cpp/audioapi/android/core/AudioDecoder.cpp

Lines changed: 81 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -7,59 +7,90 @@
77
#define MINIAUDIO_IMPLEMENTATION
88
#include <audioapi/libs/miniaudio/miniaudio.h>
99

10+
#include <audioapi/libs/miniaudio/decoders/libopus/miniaudio_libopus.h>
11+
#include <audioapi/libs/miniaudio/decoders/libvorbis/miniaudio_libvorbis.h>
12+
1013
// #include <android/log.h>
1114

1215
namespace audioapi {
1316

17+
// Decoding audio in fixed-size chunks because total frame count can't be
18+
// determined in advance. Note: ma_decoder_get_length_in_pcm_frames() always
19+
// returns 0 for Vorbis decoders.
20+
std::vector<int16_t> AudioDecoder::readAllPcmFrames(
21+
ma_decoder &decoder,
22+
int numChannels,
23+
ma_uint64 &outFramesRead) const {
24+
std::vector<int16_t> buffer;
25+
int16_t temp[CHUNK_SIZE * numChannels];
26+
outFramesRead = 0;
27+
28+
while (true) {
29+
ma_uint64 tempFramesDecoded = 0;
30+
ma_decoder_read_pcm_frames(&decoder, temp, CHUNK_SIZE, &tempFramesDecoded);
31+
if (tempFramesDecoded == 0) {
32+
break;
33+
}
34+
35+
buffer.insert(buffer.end(), temp, temp + tempFramesDecoded * numChannels);
36+
outFramesRead += tempFramesDecoded;
37+
}
38+
39+
return buffer;
40+
}
41+
42+
std::shared_ptr<AudioBus> AudioDecoder::makeAudioBusFromInt16Buffer(
43+
const std::vector<int16_t> &buffer,
44+
int numChannels,
45+
float sampleRate) const {
46+
auto outputFrames = buffer.size() / numChannels;
47+
auto audioBus =
48+
std::make_shared<AudioBus>(outputFrames, numChannels, sampleRate);
49+
50+
for (int ch = 0; ch < numChannels; ++ch) {
51+
auto channelData = audioBus->getChannel(ch)->getData();
52+
for (int i = 0; i < outputFrames; ++i) {
53+
channelData[i] = int16ToFloat(buffer[i * numChannels + ch]);
54+
}
55+
}
56+
return audioBus;
57+
}
58+
1459
std::shared_ptr<AudioBus> AudioDecoder::decodeWithFilePath(
1560
const std::string &path) const {
1661
ma_decoder decoder;
1762
ma_decoder_config config = ma_decoder_config_init(
1863
ma_format_s16, numChannels_, static_cast<int>(sampleRate_));
19-
ma_result result = ma_decoder_init_file(path.c_str(), &config, &decoder);
20-
if (result != MA_SUCCESS) {
64+
#ifndef AUDIO_API_TEST_SUITE
65+
ma_decoding_backend_vtable *customBackends[] = {
66+
ma_decoding_backend_libvorbis, ma_decoding_backend_libopus};
67+
68+
config.ppCustomBackendVTables = customBackends;
69+
config.customBackendCount =
70+
sizeof(customBackends) / sizeof(customBackends[0]);
71+
#endif
72+
73+
if (ma_decoder_init_file(path.c_str(), &config, &decoder) != MA_SUCCESS) {
2174
// __android_log_print(
22-
// ANDROID_LOG_ERROR,
23-
// "AudioDecoder",
24-
// "Failed to initialize decoder for file: %s",
25-
// path.c_str());
75+
// ANDROID_LOG_ERROR,
76+
// "AudioDecoder",
77+
// "Failed to initialize decoder for file: %s",
78+
// path.c_str());
2679
ma_decoder_uninit(&decoder);
27-
2880
return nullptr;
2981
}
3082

31-
ma_uint64 totalFrameCount;
32-
ma_decoder_get_length_in_pcm_frames(&decoder, &totalFrameCount);
33-
34-
std::vector<int16_t> buffer(totalFrameCount * numChannels_);
35-
36-
ma_uint64 framesDecoded;
37-
ma_decoder_read_pcm_frames(
38-
&decoder, buffer.data(), totalFrameCount, &framesDecoded);
39-
40-
if (framesDecoded == 0) {
83+
ma_uint64 framesRead = 0;
84+
auto buffer = readAllPcmFrames(decoder, numChannels_, framesRead);
85+
if (framesRead == 0) {
4186
// __android_log_print(ANDROID_LOG_ERROR, "AudioDecoder", "Failed to
4287
// decode");
43-
4488
ma_decoder_uninit(&decoder);
4589
return nullptr;
4690
}
4791

48-
auto outputFrames = buffer.size() / numChannels_;
49-
auto audioBus =
50-
std::make_shared<AudioBus>(outputFrames, numChannels_, sampleRate_);
51-
52-
for (int i = 0; i < numChannels_; ++i) {
53-
auto channelData = audioBus->getChannel(i)->getData();
54-
55-
for (ma_uint64 j = 0; j < outputFrames; ++j) {
56-
channelData[j] = int16ToFloat(buffer[j * numChannels_ + i]);
57-
}
58-
}
59-
6092
ma_decoder_uninit(&decoder);
61-
62-
return audioBus;
93+
return makeAudioBusFromInt16Buffer(buffer, numChannels_, sampleRate_);
6394
}
6495

6596
std::shared_ptr<AudioBus> AudioDecoder::decodeWithMemoryBlock(
@@ -68,49 +99,36 @@ std::shared_ptr<AudioBus> AudioDecoder::decodeWithMemoryBlock(
6899
ma_decoder decoder;
69100
ma_decoder_config config = ma_decoder_config_init(
70101
ma_format_s16, numChannels_, static_cast<int>(sampleRate_));
71-
ma_result result = ma_decoder_init_memory(data, size, &config, &decoder);
72-
if (result != MA_SUCCESS) {
102+
103+
#ifndef AUDIO_API_TEST_SUITE
104+
ma_decoding_backend_vtable *customBackends[] = {
105+
ma_decoding_backend_libvorbis, ma_decoding_backend_libopus};
106+
107+
config.ppCustomBackendVTables = customBackends;
108+
config.customBackendCount =
109+
sizeof(customBackends) / sizeof(customBackends[0]);
110+
#endif
111+
112+
if (ma_decoder_init_memory(data, size, &config, &decoder) != MA_SUCCESS) {
73113
// __android_log_print(
74-
// ANDROID_LOG_ERROR,
75-
// "AudioDecoder",
76-
// "Failed to initialize decoder for memory block");
114+
// ANDROID_LOG_ERROR,
115+
// "AudioDecoder",
116+
// "Failed to initialize decoder for memory block");
77117
ma_decoder_uninit(&decoder);
78-
79118
return nullptr;
80119
}
81120

82-
ma_uint64 totalFrameCount;
83-
ma_decoder_get_length_in_pcm_frames(&decoder, &totalFrameCount);
84-
85-
std::vector<int16_t> buffer(totalFrameCount * numChannels_);
86-
87-
ma_uint64 framesDecoded;
88-
ma_decoder_read_pcm_frames(
89-
&decoder, buffer.data(), totalFrameCount, &framesDecoded);
90-
91-
if (framesDecoded == 0) {
121+
ma_uint64 framesRead = 0;
122+
auto buffer = readAllPcmFrames(decoder, numChannels_, framesRead);
123+
if (framesRead == 0) {
92124
// __android_log_print(ANDROID_LOG_ERROR, "AudioDecoder", "Failed to
93125
// decode");
94-
95126
ma_decoder_uninit(&decoder);
96127
return nullptr;
97128
}
98129

99-
auto outputFrames = buffer.size() / numChannels_;
100-
auto audioBus =
101-
std::make_shared<AudioBus>(outputFrames, numChannels_, sampleRate_);
102-
103-
for (int i = 0; i < numChannels_; ++i) {
104-
auto channelData = audioBus->getChannel(i)->getData();
105-
106-
for (ma_uint64 j = 0; j < outputFrames; ++j) {
107-
channelData[j] = int16ToFloat(buffer[j * numChannels_ + i]);
108-
}
109-
}
110-
111130
ma_decoder_uninit(&decoder);
112-
113-
return audioBus;
131+
return makeAudioBusFromInt16Buffer(buffer, numChannels_, sampleRate_);
114132
}
115133

116134
std::shared_ptr<AudioBus> AudioDecoder::decodeWithPCMInBase64(

0 commit comments

Comments
 (0)