react-native-audio-api 0.12.1 → 0.12.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.
Files changed (107) hide show
  1. package/README.md +8 -3
  2. package/RNAudioAPI.podspec +0 -2
  3. package/android/src/main/cpp/audioapi/CMakeLists.txt +8 -2
  4. package/android/src/main/cpp/audioapi/android/AudioAPIModule.cpp +7 -29
  5. package/android/src/main/cpp/audioapi/android/JniEventPayloadParser.cpp +83 -0
  6. package/android/src/main/cpp/audioapi/android/JniEventPayloadParser.h +14 -0
  7. package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +4 -3
  8. package/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +1 -0
  9. package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp +37 -21
  10. package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h +1 -1
  11. package/common/cpp/audioapi/AudioAPIModuleInstaller.h +21 -0
  12. package/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.cpp +26 -4
  13. package/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.h +1 -0
  14. package/common/cpp/audioapi/HostObjects/sources/AudioFileSourceNodeHostObject.cpp +2 -2
  15. package/common/cpp/audioapi/HostObjects/utils/AudioDecoderHostObject.cpp +3 -3
  16. package/common/cpp/audioapi/HostObjects/utils/AudioFileUtilsHostObject.cpp +60 -0
  17. package/common/cpp/audioapi/HostObjects/utils/AudioFileUtilsHostObject.h +24 -0
  18. package/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h +2 -2
  19. package/common/cpp/audioapi/core/OfflineAudioContext.cpp +0 -1
  20. package/common/cpp/audioapi/core/OfflineAudioContext.h +0 -1
  21. package/common/cpp/audioapi/core/effects/ConvolverNode.cpp +4 -10
  22. package/common/cpp/audioapi/core/effects/ConvolverNode.h +0 -4
  23. package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp +13 -11
  24. package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h +5 -9
  25. package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp +47 -139
  26. package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h +11 -8
  27. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +29 -114
  28. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h +6 -8
  29. package/common/cpp/audioapi/core/sources/AudioFileSourceNode.cpp +9 -11
  30. package/common/cpp/audioapi/core/sources/AudioFileSourceNode.h +1 -1
  31. package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +2 -2
  32. package/common/cpp/audioapi/core/types/AudioFormat.h +3 -1
  33. package/common/cpp/audioapi/core/utils/AudioDecoder.cpp +120 -91
  34. package/common/cpp/audioapi/core/utils/AudioDecoder.h +24 -101
  35. package/common/cpp/audioapi/core/utils/AudioFileConcatenator.cpp +862 -0
  36. package/common/cpp/audioapi/core/utils/AudioFileConcatenator.h +164 -0
  37. package/common/cpp/audioapi/core/utils/AudioFileWriter.cpp +2 -4
  38. package/common/cpp/audioapi/core/utils/AudioRecorderCallback.cpp +7 -12
  39. package/common/cpp/audioapi/core/utils/AudioRecorderCallback.h +2 -0
  40. package/common/cpp/audioapi/core/utils/buffer/BufferProcessingDirection.h +6 -0
  41. package/common/cpp/audioapi/core/utils/buffer/BufferProcessorBase.cpp +110 -0
  42. package/common/cpp/audioapi/core/utils/buffer/BufferProcessorBase.h +75 -0
  43. package/common/cpp/audioapi/core/utils/buffer/QueueBufferProcessor.cpp +129 -0
  44. package/common/cpp/audioapi/core/utils/buffer/QueueBufferProcessor.h +55 -0
  45. package/common/cpp/audioapi/core/utils/buffer/SingleBufferProcessor.cpp +95 -0
  46. package/common/cpp/audioapi/core/utils/buffer/SingleBufferProcessor.h +52 -0
  47. package/common/cpp/audioapi/events/AudioEventHandlerRegistry.cpp +65 -157
  48. package/common/cpp/audioapi/events/AudioEventHandlerRegistry.h +52 -33
  49. package/common/cpp/audioapi/events/AudioEventPayload.h +87 -0
  50. package/common/cpp/audioapi/events/IAudioEventHandlerRegistry.h +12 -12
  51. package/common/cpp/audioapi/libs/decoding/IncrementalAudioDecoder.h +12 -10
  52. package/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp +152 -78
  53. package/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.h +6 -6
  54. package/common/cpp/audioapi/libs/miniaudio/MiniAudioDecoding.cpp +34 -20
  55. package/common/cpp/audioapi/libs/miniaudio/MiniAudioDecoding.h +4 -4
  56. package/common/cpp/audioapi/utils/AudioArray.hpp +6 -1
  57. package/common/cpp/audioapi/utils/CrossThreadEventScheduler.hpp +1 -1
  58. package/common/cpp/audioapi/utils/TaskOffloader.hpp +3 -5
  59. package/ios/audioapi/ios/AudioAPIModule.h +2 -1
  60. package/ios/audioapi/ios/AudioAPIModule.mm +4 -25
  61. package/ios/audioapi/ios/core/IOSAudioPlayer.h +16 -2
  62. package/ios/audioapi/ios/core/IOSAudioPlayer.mm +90 -24
  63. package/ios/audioapi/ios/core/IOSAudioRecorder.mm +1 -1
  64. package/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm +64 -20
  65. package/ios/audioapi/ios/system/AudioSessionManager.mm +18 -7
  66. package/ios/audioapi/ios/system/SystemNotificationManager.mm +22 -22
  67. package/ios/audioapi/ios/system/notification/PlaybackNotification.mm +10 -13
  68. package/lib/commonjs/AudioAPIModule/AudioAPIModule.js +1 -1
  69. package/lib/commonjs/AudioAPIModule/AudioAPIModule.js.map +1 -1
  70. package/lib/commonjs/api.js +8 -0
  71. package/lib/commonjs/api.js.map +1 -1
  72. package/lib/commonjs/api.web.js +5 -0
  73. package/lib/commonjs/api.web.js.map +1 -1
  74. package/lib/commonjs/core/AudioFileUtils.js +35 -0
  75. package/lib/commonjs/core/AudioFileUtils.js.map +1 -0
  76. package/lib/commonjs/mock/index.js +15 -1
  77. package/lib/commonjs/mock/index.js.map +1 -1
  78. package/lib/module/AudioAPIModule/AudioAPIModule.js +1 -1
  79. package/lib/module/AudioAPIModule/AudioAPIModule.js.map +1 -1
  80. package/lib/module/api.js +1 -0
  81. package/lib/module/api.js.map +1 -1
  82. package/lib/module/api.web.js +3 -0
  83. package/lib/module/api.web.js.map +1 -1
  84. package/lib/module/core/AudioFileUtils.js +31 -0
  85. package/lib/module/core/AudioFileUtils.js.map +1 -0
  86. package/lib/module/mock/index.js +14 -1
  87. package/lib/module/mock/index.js.map +1 -1
  88. package/lib/typescript/AudioAPIModule/AudioAPIModule.d.ts.map +1 -1
  89. package/lib/typescript/api.d.ts +1 -0
  90. package/lib/typescript/api.d.ts.map +1 -1
  91. package/lib/typescript/api.web.d.ts +1 -0
  92. package/lib/typescript/api.web.d.ts.map +1 -1
  93. package/lib/typescript/core/AudioFileUtils.d.ts +2 -0
  94. package/lib/typescript/core/AudioFileUtils.d.ts.map +1 -0
  95. package/lib/typescript/interfaces.d.ts +3 -0
  96. package/lib/typescript/interfaces.d.ts.map +1 -1
  97. package/lib/typescript/mock/index.d.ts +3 -1
  98. package/lib/typescript/mock/index.d.ts.map +1 -1
  99. package/package.json +3 -3
  100. package/scripts/download-prebuilt-binaries.sh +34 -1
  101. package/src/AudioAPIModule/AudioAPIModule.ts +1 -0
  102. package/src/AudioAPIModule/globals.d.ts +3 -0
  103. package/src/api.ts +1 -0
  104. package/src/api.web.ts +7 -0
  105. package/src/core/AudioFileUtils.ts +49 -0
  106. package/src/interfaces.ts +7 -0
  107. package/src/mock/index.ts +29 -0
@@ -10,8 +10,6 @@
10
10
  #include <algorithm>
11
11
  #include <cassert>
12
12
  #include <memory>
13
- #include <string>
14
- #include <unordered_map>
15
13
 
16
14
  #if !RN_AUDIO_API_FFMPEG_DISABLED
17
15
  #include <audioapi/libs/ffmpeg/FFmpegDecoding.h>
@@ -69,10 +67,10 @@ void AudioFileSourceNode::sendOnPositionChangedEvent(int framesPlayed) {
69
67
  if (onPositionChangedCallbackId_ != 0 &&
70
68
  (onPositionChangedFlush_.load(std::memory_order_acquire) ||
71
69
  onPositionChangedTime_ > onPositionChangedInterval_)) {
72
- std::unordered_map<std::string, EventValue> body = {{"value", getCurrentTime()}};
73
-
74
- audioEventHandlerRegistry_->invokeHandlerWithEventBody(
75
- AudioEvent::POSITION_CHANGED, onPositionChangedCallbackId_, body);
70
+ audioEventHandlerRegistry_->dispatchEvent(
71
+ AudioEvent::POSITION_CHANGED,
72
+ onPositionChangedCallbackId_,
73
+ DoubleValuePayload{.value = getCurrentTime()});
76
74
 
77
75
  onPositionChangedTime_ = 0;
78
76
  onPositionChangedFlush_.store(false, std::memory_order_release);
@@ -85,7 +83,7 @@ void AudioFileSourceNode::initDecoders(
85
83
  bool useFilePath,
86
84
  const std::shared_ptr<BaseAudioContext> &context,
87
85
  const std::shared_ptr<AudioFileDecoderState> &state) {
88
- bool ok = false;
86
+ decoding::DecoderResult openResult = Ok(None);
89
87
  if (requiresFFmpeg_) {
90
88
  #if !RN_AUDIO_API_FFMPEG_DISABLED
91
89
  decoder_ = std::make_unique<ffmpegdecoder::FFmpegDecoder>();
@@ -94,14 +92,14 @@ void AudioFileSourceNode::initDecoders(
94
92
  decoder_ = std::make_unique<miniaudio_decoder::MiniAudioDecoder>();
95
93
  }
96
94
  if (useFilePath) {
97
- ok = decoder_->openFile(static_cast<int>(context->getSampleRate()), state->filePath);
95
+ openResult = decoder_->openFile(static_cast<int>(context->getSampleRate()), state->filePath);
98
96
  } else {
99
- ok = decoder_->openMemory(
97
+ openResult = decoder_->openMemory(
100
98
  static_cast<int>(context->getSampleRate()),
101
99
  state->memoryData.data(),
102
100
  state->memoryData.size());
103
101
  }
104
- if (ok) {
102
+ if (openResult.is_ok()) {
105
103
  state->channels = decoder_->outputChannels();
106
104
  state->sampleRate = static_cast<float>(decoder_->outputSampleRate());
107
105
  duration_ = static_cast<double>(decoder_->getDurationInSeconds());
@@ -142,7 +140,7 @@ size_t AudioFileSourceNode::readFrames(float *buf, size_t frameCount) {
142
140
  }
143
141
 
144
142
  bool AudioFileSourceNode::seekDecoderToTime(double seconds) {
145
- return decoder_->seekToTime(seconds);
143
+ return decoder_->seekToTime(seconds).is_ok();
146
144
  }
147
145
 
148
146
  void AudioFileSourceNode::applyPlaybackStateAfterSuccessfulSeek(double seconds) {
@@ -81,7 +81,7 @@ class AudioFileSourceNode : public AudioScheduledSourceNode {
81
81
  const std::shared_ptr<AudioFileDecoderState> &state);
82
82
 
83
83
  std::shared_ptr<AudioFileDecoderState> decoderState_;
84
- std::unique_ptr<decoding::IIncrementalAudioDecoder> decoder_;
84
+ std::unique_ptr<decoding::IncrementalAudioDecoder> decoder_;
85
85
  float volume_;
86
86
  bool requiresFFmpeg_;
87
87
  bool filePaused_{false};
@@ -159,8 +159,8 @@ void AudioScheduledSourceNode::disable() {
159
159
  AudioNode::disable();
160
160
 
161
161
  if (onEndedCallbackId_ != 0) {
162
- audioEventHandlerRegistry_->invokeHandlerWithEventBody(
163
- AudioEvent::ENDED, onEndedCallbackId_, {});
162
+ audioEventHandlerRegistry_->dispatchEvent(
163
+ AudioEvent::ENDED, onEndedCallbackId_, EmptyPayload{});
164
164
  }
165
165
  }
166
166
 
@@ -1,6 +1,8 @@
1
1
  #pragma once
2
2
 
3
+ #include <cstdint>
4
+
3
5
  namespace audioapi {
4
6
 
5
- enum class AudioFormat { UNKNOWN, WAV, OGG, FLAC, AAC, MP3, M4A, MP4, MOV };
7
+ enum class AudioFormat : uint8_t { UNKNOWN, WAV, OGG, FLAC, AAC, MP3, M4A, MP4, MOV };
6
8
  } // namespace audioapi
@@ -1,142 +1,171 @@
1
1
  #include <audioapi/core/utils/AudioDecoder.h>
2
- #include <audioapi/dsp/VectorMath.h>
3
2
  #include <audioapi/libs/base64/base64.h>
4
- #include <audioapi/utils/AudioArray.hpp>
5
-
6
- #include <audioapi/libs/miniaudio/decoders/libopus/miniaudio_libopus.h>
7
- #include <audioapi/libs/miniaudio/decoders/libvorbis/miniaudio_libvorbis.h>
8
- #include <audioapi/libs/miniaudio/miniaudio.h>
3
+ #include <audioapi/libs/decoding/IncrementalAudioDecoder.h>
4
+ #include <audioapi/libs/miniaudio/MiniAudioDecoding.h>
9
5
 
10
6
  #if !RN_AUDIO_API_FFMPEG_DISABLED
11
7
  #include <audioapi/libs/ffmpeg/FFmpegDecoding.h>
12
8
  #endif // RN_AUDIO_API_FFMPEG_DISABLED
13
9
 
10
+ #include <algorithm>
11
+ #include <cctype>
12
+ #include <cstdint>
13
+ #include <cstring>
14
14
  #include <memory>
15
15
  #include <string>
16
16
  #include <utility>
17
17
  #include <vector>
18
18
 
19
- namespace audioapi {
19
+ namespace audioapi::audiodecoder {
20
+
21
+ // Drains an incremental decoder into an AudioBuffer. Total frame count is not
22
+ // known up front for some formats (e.g. Vorbis), so we read in fixed-size
23
+ // chunks and grow the interleaved accumulator until the decoder reports EOF.
24
+ AudioBufferResult decodeAll(decoding::IncrementalAudioDecoder &decoder) {
25
+ const int channels = std::max(1, decoder.outputChannels());
26
+ const auto outputSampleRate = static_cast<float>(decoder.outputSampleRate());
20
27
 
21
- // Decoding audio in fixed-size chunks because total frame count can't be
22
- // determined in advance. Note: ma_decoder_get_length_in_pcm_frames() always
23
- // returns 0 for Vorbis decoders.
24
- Result<std::vector<float>, std::string> AudioDecoder::readAllPcmFrames(
25
- ma_decoder &decoder,
26
- int outputChannels) {
27
- std::vector<float> buffer;
28
- std::vector<float> temp(CHUNK_SIZE * outputChannels);
29
- ma_uint64 outFramesRead = 0;
28
+ std::vector<float> interleaved;
29
+ std::vector<float> chunk(
30
+ decoding::IncrementalAudioDecoder::CHUNK_SIZE * static_cast<size_t>(channels));
30
31
 
31
32
  while (true) {
32
- ma_uint64 tempFramesDecoded = 0;
33
- ma_decoder_read_pcm_frames(&decoder, temp.data(), CHUNK_SIZE, &tempFramesDecoded);
34
- if (tempFramesDecoded == 0) {
33
+ const size_t framesRead =
34
+ decoder.readPcmFrames(chunk.data(), decoding::IncrementalAudioDecoder::CHUNK_SIZE);
35
+ if (framesRead == 0) {
35
36
  break;
36
37
  }
37
-
38
- buffer.insert(buffer.end(), temp.data(), temp.data() + tempFramesDecoded * outputChannels);
39
- outFramesRead += tempFramesDecoded;
38
+ interleaved.insert(
39
+ interleaved.end(),
40
+ chunk.begin(),
41
+ chunk.begin() + static_cast<std::ptrdiff_t>(framesRead * static_cast<size_t>(channels)));
40
42
  }
41
43
 
42
- if (outFramesRead == 0) {
44
+ if (interleaved.empty()) {
43
45
  return Err("Failed to decode any frames");
44
46
  }
45
47
 
46
- return Ok(std::move(buffer));
48
+ const size_t outputFrames = interleaved.size() / static_cast<size_t>(channels);
49
+ auto audioBuffer = std::make_shared<AudioBuffer>(outputFrames, channels, outputSampleRate);
50
+ audioBuffer->deinterleaveFrom(interleaved.data(), outputFrames);
51
+ return Ok(std::move(audioBuffer));
47
52
  }
48
53
 
49
- AudioBufferResult AudioDecoder::makeAudioBufferFromFloatBuffer(
50
- const std::vector<float> &buffer,
51
- float outputSampleRate,
52
- int outputChannels) {
53
- if (buffer.empty()) {
54
- return Err("Buffer is empty");
54
+ // NOLINTBEGIN(readability-magic-numbers, cppcoreguidelines-avoid-magic-numbers)
55
+ AudioFormat detectAudioFormat(const void *data, size_t size) {
56
+ if (size < 12) {
57
+ return AudioFormat::UNKNOWN;
55
58
  }
59
+ const auto *bytes = static_cast<const unsigned char *>(data);
56
60
 
57
- auto outputFrames = buffer.size() / outputChannels;
58
- auto audioBuffer = std::make_shared<AudioBuffer>(outputFrames, outputChannels, outputSampleRate);
59
-
60
- audioBuffer->deinterleaveFrom(buffer.data(), outputFrames);
61
-
62
- return Ok(std::move(audioBuffer));
63
- }
61
+ // WAV/RIFF
62
+ if (std::memcmp(bytes, "RIFF", 4) == 0 && std::memcmp(bytes + 8, "WAVE", 4) == 0) {
63
+ return AudioFormat::WAV;
64
+ }
64
65
 
65
- AudioBufferResult AudioDecoder::decodeWithMiniaudio(float sampleRate, DecoderSource source) {
66
- ma_decoder decoder;
67
- ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 0, static_cast<int>(sampleRate));
68
- ma_decoding_backend_vtable *customBackends[] = {
69
- ma_decoding_backend_libvorbis, ma_decoding_backend_libopus};
70
-
71
- config.ppCustomBackendVTables = customBackends;
72
- config.customBackendCount = sizeof(customBackends) / sizeof(customBackends[0]);
73
-
74
- ma_result initResult = std::visit(
75
- [&config, &decoder](auto &&arg) -> ma_result {
76
- using T = std::decay_t<decltype(arg)>;
77
- if constexpr (std::is_same_v<T, MemorySource>) {
78
- return ma_decoder_init_memory(arg.data, arg.size, &config, &decoder);
79
- } else if constexpr (std::is_same_v<T, std::string>) {
80
- return ma_decoder_init_file(arg.c_str(), &config, &decoder);
81
- } else {
82
- return MA_INVALID_ARGS;
83
- }
84
- },
85
- source);
86
-
87
- if (initResult != MA_SUCCESS) {
88
- return Err(
89
- "Failed to initialize miniaudio decoder: " +
90
- std::string(ma_result_description(initResult)));
66
+ // OGG
67
+ if (std::memcmp(bytes, "OggS", 4) == 0) {
68
+ return AudioFormat::OGG;
91
69
  }
92
70
 
93
- auto outputSampleRate = static_cast<float>(decoder.outputSampleRate);
94
- auto outputChannels = static_cast<int>(decoder.outputChannels);
71
+ // FLAC
72
+ if (std::memcmp(bytes, "fLaC", 4) == 0) {
73
+ return AudioFormat::FLAC;
74
+ }
95
75
 
96
- auto result = readAllPcmFrames(decoder, outputChannels)
97
- .and_then([outputSampleRate, outputChannels](std::vector<float> &&buffer) {
98
- return makeAudioBufferFromFloatBuffer(
99
- std::move(buffer), outputSampleRate, outputChannels);
100
- });
76
+ // AAC starts with 0xFF 0xF1 or 0xFF 0xF9
77
+ if (bytes[0] == 0xFF && (bytes[1] & 0xF6) == 0xF0) {
78
+ return AudioFormat::AAC;
79
+ }
101
80
 
102
- ma_decoder_uninit(&decoder);
81
+ // MP3: "ID3" or 11-bit frame sync (0xFF 0xE0)
82
+ if (std::memcmp(bytes, "ID3", 3) == 0) {
83
+ return AudioFormat::MP3;
84
+ }
85
+ if (bytes[0] == 0xFF && (bytes[1] & 0xE0) == 0xE0) {
86
+ return AudioFormat::MP3;
87
+ }
103
88
 
104
- return result;
89
+ if (std::memcmp(bytes + 4, "ftyp", 4) == 0) {
90
+ if (std::memcmp(bytes + 8, "M4A ", 4) == 0) {
91
+ return AudioFormat::M4A;
92
+ }
93
+ if (std::memcmp(bytes + 8, "qt ", 4) == 0) {
94
+ return AudioFormat::MOV;
95
+ }
96
+ return AudioFormat::MP4;
97
+ }
98
+ return AudioFormat::UNKNOWN;
105
99
  }
100
+ // NOLINTEND(readability-magic-numbers, cppcoreguidelines-avoid-magic-numbers)
101
+
102
+ bool pathHasExtension(const std::string &path, const std::vector<std::string> &extensions) {
103
+ std::string pathLower = path;
104
+ std::ranges::transform(pathLower, pathLower.begin(), [](unsigned char c) {
105
+ return static_cast<char>(std::tolower(c));
106
+ });
107
+ return std::ranges::any_of(
108
+ extensions, [&pathLower](const std::string &ext) { return pathLower.ends_with(ext); });
109
+ }
110
+
111
+ AudioBufferResult decodeWithFilePath(const std::string &path, float sampleRate) {
112
+ const int sr = static_cast<int>(sampleRate);
106
113
 
107
- AudioBufferResult AudioDecoder::decodeWithFilePath(const std::string &path, float sampleRate) {
108
- if (AudioDecoder::pathHasExtension(path, {".mp4", ".m4a", ".aac"})) {
114
+ if (needsFFmpegByPath(path)) {
109
115
  #if !RN_AUDIO_API_FFMPEG_DISABLED
110
- auto buffer = ffmpegdecoder::decodeWithFilePath(path, static_cast<int>(sampleRate));
111
- if (buffer == nullptr) {
112
- return Err("Failed to decode with file path using FFmpeg");
116
+ ffmpegdecoder::FFmpegDecoder decoder;
117
+ const auto openResult = decoder.openFile(sr, path);
118
+ if (openResult.is_err()) {
119
+ return Err("Failed to open file with FFmpeg decoder: " + openResult.unwrap_err());
113
120
  }
114
- return Ok(std::move(buffer));
121
+ auto result = decodeAll(decoder);
122
+ decoder.close();
123
+ return result;
115
124
  #else
116
125
  return Err("FFmpeg is disabled, cannot decode with file path");
117
126
  #endif // RN_AUDIO_API_FFMPEG_DISABLED
118
127
  }
119
- return decodeWithMiniaudio(sampleRate, path);
128
+
129
+ miniaudio_decoder::MiniAudioDecoder decoder;
130
+ const auto openResult = decoder.openFile(sr, path);
131
+ if (openResult.is_err()) {
132
+ return Err("Failed to open file with miniaudio decoder: " + openResult.unwrap_err());
133
+ }
134
+ auto result = decodeAll(decoder);
135
+ decoder.close();
136
+ return result;
120
137
  }
121
138
 
122
- AudioBufferResult
123
- AudioDecoder::decodeWithMemoryBlock(const void *data, size_t size, float sampleRate) {
124
- const AudioFormat format = AudioDecoder::detectAudioFormat(data, size);
125
- if (format == AudioFormat::MP4 || format == AudioFormat::M4A || format == AudioFormat::AAC) {
139
+ AudioBufferResult decodeWithMemoryBlock(const void *data, size_t size, float sampleRate) {
140
+ const int sr = static_cast<int>(sampleRate);
141
+ const AudioFormat format = detectAudioFormat(data, size);
142
+
143
+ if (needsFFmpeg(format)) {
126
144
  #if !RN_AUDIO_API_FFMPEG_DISABLED
127
- auto buffer = ffmpegdecoder::decodeWithMemoryBlock(data, size, static_cast<int>(sampleRate));
128
- if (buffer == nullptr) {
129
- return Err("Failed to decode with memory block using FFmpeg");
145
+ ffmpegdecoder::FFmpegDecoder decoder;
146
+ const auto openResult = decoder.openMemory(sr, data, size);
147
+ if (openResult.is_err()) {
148
+ return Err("Failed to open memory block with FFmpeg decoder: " + openResult.unwrap_err());
130
149
  }
131
- return Ok(std::move(buffer));
150
+ auto result = decodeAll(decoder);
151
+ decoder.close();
152
+ return result;
132
153
  #else
133
154
  return Err("FFmpeg is disabled, cannot decode memory block");
134
155
  #endif // RN_AUDIO_API_FFMPEG_DISABLED
135
156
  }
136
- return decodeWithMiniaudio(sampleRate, MemorySource{.data = data, .size = size});
157
+
158
+ miniaudio_decoder::MiniAudioDecoder decoder;
159
+ const auto openResult = decoder.openMemory(sr, data, size);
160
+ if (openResult.is_err()) {
161
+ return Err("Failed to open memory block with miniaudio decoder: " + openResult.unwrap_err());
162
+ }
163
+ auto result = decodeAll(decoder);
164
+ decoder.close();
165
+ return result;
137
166
  }
138
167
 
139
- AudioBufferResult AudioDecoder::decodeWithPCMInBase64(
168
+ AudioBufferResult decodeWithPCMInBase64(
140
169
  const std::string &data,
141
170
  float inputSampleRate,
142
171
  int inputChannelCount,
@@ -148,7 +177,7 @@ AudioBufferResult AudioDecoder::decodeWithPCMInBase64(
148
177
  auto audioBuffer =
149
178
  std::make_shared<AudioBuffer>(numFramesDecoded, inputChannelCount, inputSampleRate);
150
179
 
151
- for (size_t ch = 0; ch < inputChannelCount; ++ch) {
180
+ for (int ch = 0; ch < inputChannelCount; ++ch) {
152
181
  auto channelData = audioBuffer->getChannel(ch)->span();
153
182
 
154
183
  for (size_t i = 0; i < numFramesDecoded; ++i) {
@@ -168,4 +197,4 @@ AudioBufferResult AudioDecoder::decodeWithPCMInBase64(
168
197
  return Ok(std::move(audioBuffer));
169
198
  }
170
199
 
171
- } // namespace audioapi
200
+ } // namespace audioapi::audiodecoder
@@ -1,119 +1,42 @@
1
1
  #pragma once
2
2
 
3
3
  #include <audioapi/core/types/AudioFormat.h>
4
- #include <audioapi/libs/miniaudio/miniaudio.h>
5
4
  #include <audioapi/utils/AudioBuffer.hpp>
6
5
  #include <audioapi/utils/Result.hpp>
7
- #include <algorithm>
8
- #include <cstring>
6
+ #include <cstddef>
9
7
  #include <memory>
10
8
  #include <string>
11
- #include <variant>
12
9
  #include <vector>
13
10
 
14
- namespace audioapi {
11
+ namespace audioapi::audiodecoder {
15
12
 
16
13
  using AudioBufferResult = Result<std::shared_ptr<AudioBuffer>, std::string>;
17
14
 
18
- struct MemorySource {
19
- const void *data;
20
- size_t size;
21
- };
15
+ [[nodiscard]] AudioBufferResult decodeWithFilePath(const std::string &path, float sampleRate);
16
+ [[nodiscard]] AudioBufferResult
17
+ decodeWithMemoryBlock(const void *data, size_t size, float sampleRate);
18
+ [[nodiscard]] AudioBufferResult decodeWithPCMInBase64(
19
+ const std::string &data,
20
+ float inputSampleRate,
21
+ int inputChannelCount,
22
+ bool interleaved);
22
23
 
23
- using DecoderSource = std::variant<MemorySource, std::string>;
24
+ [[nodiscard]] AudioFormat detectAudioFormat(const void *data, size_t size);
24
25
 
25
- static constexpr int CHUNK_SIZE = 4096;
26
+ [[nodiscard]] bool pathHasExtension(
27
+ const std::string &path,
28
+ const std::vector<std::string> &extensions);
26
29
 
27
- class AudioDecoder {
28
- public:
29
- AudioDecoder() = delete;
30
+ [[nodiscard]] inline bool needsFFmpeg(AudioFormat format) {
31
+ return format == AudioFormat::MP4 || format == AudioFormat::M4A || format == AudioFormat::AAC;
32
+ }
30
33
 
31
- [[nodiscard]] static AudioBufferResult decodeWithFilePath(
32
- const std::string &path,
33
- float sampleRate);
34
- [[nodiscard]] static AudioBufferResult
35
- decodeWithMemoryBlock(const void *data, size_t size, float sampleRate);
36
- [[nodiscard]] static AudioBufferResult decodeWithPCMInBase64(
37
- const std::string &data,
38
- float inputSampleRate,
39
- int inputChannelCount,
40
- bool interleaved);
34
+ [[nodiscard]] inline bool needsFFmpegByPath(const std::string &path) {
35
+ return pathHasExtension(path, {".mp4", ".m4a", ".aac"});
36
+ }
41
37
 
42
- // NOLINTBEGIN(readability-magic-numbers, cppcoreguidelines-avoid-magic-numbers)
43
- static AudioFormat detectAudioFormat(const void *data, size_t size) {
44
- if (size < 12) {
45
- return AudioFormat::UNKNOWN;
46
- }
47
- const auto *bytes = static_cast<const unsigned char *>(data);
38
+ [[nodiscard]] inline float uint8ToFloat(uint8_t byte1, uint8_t byte2) {
39
+ return static_cast<float>(static_cast<int16_t>((byte2 << CHAR_BIT) | byte1)) / INT16_MAX;
40
+ }
48
41
 
49
- // WAV/RIFF
50
- if (std::memcmp(bytes, "RIFF", 4) == 0 && std::memcmp(bytes + 8, "WAVE", 4) == 0) {
51
- return AudioFormat::WAV;
52
- }
53
-
54
- // OGG
55
- if (std::memcmp(bytes, "OggS", 4) == 0) {
56
- return AudioFormat::OGG;
57
- }
58
-
59
- // FLAC
60
- if (std::memcmp(bytes, "fLaC", 4) == 0) {
61
- return AudioFormat::FLAC;
62
- }
63
-
64
- // AAC starts with 0xFF 0xF1 or 0xFF 0xF9
65
- if (bytes[0] == 0xFF && (bytes[1] & 0xF6) == 0xF0) {
66
- return AudioFormat::AAC;
67
- }
68
-
69
- // MP3: "ID3" or 11-bit frame sync (0xFF 0xE0)
70
- if (std::memcmp(bytes, "ID3", 3) == 0) {
71
- return AudioFormat::MP3;
72
- }
73
- if (bytes[0] == 0xFF && (bytes[1] & 0xE0) == 0xE0) {
74
- return AudioFormat::MP3;
75
- }
76
-
77
- if (std::memcmp(bytes + 4, "ftyp", 4) == 0) {
78
- if (std::memcmp(bytes + 8, "M4A ", 4) == 0) {
79
- return AudioFormat::M4A;
80
- }
81
- if (std::memcmp(bytes + 8, "qt ", 4) == 0) {
82
- return AudioFormat::MOV;
83
- }
84
- return AudioFormat::MP4;
85
- }
86
- return AudioFormat::UNKNOWN;
87
- }
88
-
89
- // NOLINTEND(readability-magic-numbers, cppcoreguidelines-avoid-magic-numbers)
90
- static bool pathHasExtension(
91
- const std::string &path,
92
- const std::vector<std::string> &extensions) {
93
- std::string pathLower = path;
94
- std::ranges::transform(pathLower, pathLower.begin(), ::tolower);
95
- return std::ranges::any_of(
96
- extensions, [&pathLower](const std::string &ext) { return pathLower.ends_with(ext); });
97
- }
98
-
99
- private:
100
- static AudioBufferResult decodeWithMiniaudio(float sampleRate, DecoderSource source);
101
- static Result<std::vector<float>, std::string> readAllPcmFrames(
102
- ma_decoder &decoder,
103
- int outputChannels);
104
- static AudioBufferResult makeAudioBufferFromFloatBuffer(
105
- const std::vector<float> &buffer,
106
- float outputSampleRate,
107
- int outputChannels);
108
- [[nodiscard]] static int16_t floatToInt16(float sample) {
109
- return static_cast<int16_t>(sample * INT16_MAX);
110
- }
111
- [[nodiscard]] static float int16ToFloat(int16_t sample) {
112
- return static_cast<float>(sample) / INT16_MAX;
113
- }
114
- [[nodiscard]] static float uint8ToFloat(uint8_t byte1, uint8_t byte2) {
115
- return static_cast<float>(static_cast<int16_t>((byte2 << CHAR_BIT) | byte1)) / INT16_MAX;
116
- }
117
- };
118
-
119
- } // namespace audioapi
42
+ } // namespace audioapi::audiodecoder