react-native-audio-api 0.3.2 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +17 -11
  2. package/android/src/main/cpp/core/AudioPlayer.cpp +38 -11
  3. package/android/src/main/cpp/core/AudioPlayer.h +5 -1
  4. package/common/cpp/HostObjects/AnalyserNodeHostObject.h +151 -0
  5. package/common/cpp/HostObjects/AudioAPIInstallerHostObject.h +11 -3
  6. package/common/cpp/HostObjects/AudioNodeHostObject.h +5 -0
  7. package/common/cpp/HostObjects/BaseAudioContextHostObject.h +8 -0
  8. package/common/cpp/core/AnalyserNode.cpp +237 -0
  9. package/common/cpp/core/AnalyserNode.h +54 -0
  10. package/common/cpp/core/AudioContext.cpp +41 -1
  11. package/common/cpp/core/AudioContext.h +17 -0
  12. package/common/cpp/core/AudioNode.cpp +21 -8
  13. package/common/cpp/core/AudioNode.h +1 -0
  14. package/common/cpp/core/AudioScheduledSourceNode.cpp +1 -2
  15. package/common/cpp/core/BaseAudioContext.cpp +5 -40
  16. package/common/cpp/core/BaseAudioContext.h +7 -18
  17. package/common/cpp/core/Constants.h +11 -2
  18. package/common/cpp/core/PeriodicWave.cpp +2 -2
  19. package/common/cpp/jsi/JsiHostObject.h +1 -1
  20. package/common/cpp/jsi/JsiPromise.h +1 -0
  21. package/common/cpp/utils/AudioUtils.cpp +7 -0
  22. package/common/cpp/utils/AudioUtils.h +5 -1
  23. package/common/cpp/utils/FFTFrame.cpp +69 -23
  24. package/common/cpp/utils/FFTFrame.h +25 -10
  25. package/common/cpp/utils/VectorMath.cpp +10 -0
  26. package/common/cpp/utils/VectorMath.h +2 -0
  27. package/ios/core/AudioPlayer.h +3 -2
  28. package/ios/core/AudioPlayer.m +49 -15
  29. package/ios/core/IOSAudioPlayer.h +3 -1
  30. package/ios/core/IOSAudioPlayer.mm +46 -10
  31. package/lib/module/core/AnalyserNode.js +59 -0
  32. package/lib/module/core/AnalyserNode.js.map +1 -0
  33. package/lib/module/core/AudioContext.js +2 -2
  34. package/lib/module/core/AudioContext.js.map +1 -1
  35. package/lib/module/core/AudioNode.js +5 -5
  36. package/lib/module/core/AudioNode.js.map +1 -1
  37. package/lib/module/core/BaseAudioContext.js +8 -0
  38. package/lib/module/core/BaseAudioContext.js.map +1 -1
  39. package/lib/module/index.js +35 -6
  40. package/lib/module/index.js.map +1 -1
  41. package/lib/module/index.native.js +1 -0
  42. package/lib/module/index.native.js.map +1 -1
  43. package/lib/typescript/core/AnalyserNode.d.ts +18 -0
  44. package/lib/typescript/core/AnalyserNode.d.ts.map +1 -0
  45. package/lib/typescript/core/AudioContext.d.ts +1 -1
  46. package/lib/typescript/core/AudioContext.d.ts.map +1 -1
  47. package/lib/typescript/core/AudioNode.d.ts +2 -2
  48. package/lib/typescript/core/AudioNode.d.ts.map +1 -1
  49. package/lib/typescript/core/BaseAudioContext.d.ts +2 -0
  50. package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
  51. package/lib/typescript/index.d.ts +16 -3
  52. package/lib/typescript/index.d.ts.map +1 -1
  53. package/lib/typescript/index.native.d.ts +1 -0
  54. package/lib/typescript/index.native.d.ts.map +1 -1
  55. package/lib/typescript/interfaces.d.ts +13 -1
  56. package/lib/typescript/interfaces.d.ts.map +1 -1
  57. package/package.json +4 -2
  58. package/src/core/AnalyserNode.ts +85 -0
  59. package/src/core/AudioContext.ts +2 -2
  60. package/src/core/AudioNode.ts +5 -5
  61. package/src/core/BaseAudioContext.ts +10 -0
  62. package/src/index.native.ts +1 -0
  63. package/src/index.ts +57 -6
  64. package/src/interfaces.ts +15 -1
  65. package/src/specs/global.d.ts +1 -1
@@ -5,16 +5,56 @@
5
5
  #endif
6
6
 
7
7
  #include "AudioContext.h"
8
+ #include "AudioDecoder.h"
9
+ #include "AudioDestinationNode.h"
8
10
 
9
11
  namespace audioapi {
10
-
11
12
  AudioContext::AudioContext() : BaseAudioContext() {
13
+ #ifdef ANDROID
14
+ audioPlayer_ = std::make_shared<AudioPlayer>(this->renderAudio());
15
+ #else
16
+ audioPlayer_ = std::make_shared<IOSAudioPlayer>(this->renderAudio());
17
+ #endif
18
+ sampleRate_ = audioPlayer_->getSampleRate();
19
+ audioDecoder_ = std::make_shared<AudioDecoder>(sampleRate_);
20
+
21
+ audioPlayer_->start();
22
+ }
23
+
24
+ AudioContext::AudioContext(int sampleRate) : BaseAudioContext() {
25
+ #ifdef ANDROID
26
+ audioPlayer_ = std::make_shared<AudioPlayer>(this->renderAudio(), sampleRate);
27
+ #else
28
+ audioPlayer_ =
29
+ std::make_shared<IOSAudioPlayer>(this->renderAudio(), sampleRate);
30
+ #endif
31
+ sampleRate_ = audioPlayer_->getSampleRate();
32
+ audioDecoder_ = std::make_shared<AudioDecoder>(sampleRate_);
33
+
12
34
  audioPlayer_->start();
13
35
  }
14
36
 
37
+ AudioContext::~AudioContext() {
38
+ if (isRunning()) {
39
+ return;
40
+ }
41
+
42
+ close();
43
+ }
44
+
15
45
  void AudioContext::close() {
16
46
  state_ = ContextState::CLOSED;
17
47
  audioPlayer_->stop();
18
48
  }
19
49
 
50
+ std::function<void(AudioBus *, int)> AudioContext::renderAudio() {
51
+ if (!isRunning()) {
52
+ return [](AudioBus *, int) {};
53
+ }
54
+
55
+ return [this](AudioBus *data, int frames) {
56
+ destination_->renderAudio(data, frames);
57
+ };
58
+ }
59
+
20
60
  } // namespace audioapi
@@ -1,16 +1,33 @@
1
1
  #pragma once
2
2
 
3
3
  #include <memory>
4
+ #include <functional>
4
5
 
5
6
  #include "BaseAudioContext.h"
6
7
 
7
8
  namespace audioapi {
9
+ #ifdef ANDROID
10
+ class AudioPlayer;
11
+ #else
12
+ class IOSAudioPlayer;
13
+ #endif
8
14
 
9
15
  class AudioContext : public BaseAudioContext {
10
16
  public:
11
17
  AudioContext();
18
+ explicit AudioContext(int sampleRate);
19
+ ~AudioContext() override;
12
20
 
13
21
  void close();
22
+
23
+ std::function<void(AudioBus *, int)> renderAudio();
24
+
25
+ private:
26
+ #ifdef ANDROID
27
+ std::shared_ptr<AudioPlayer> audioPlayer_;
28
+ #else
29
+ std::shared_ptr<IOSAudioPlayer> audioPlayer_;
30
+ #endif
14
31
  };
15
32
 
16
33
  } // namespace audioapi
@@ -9,9 +9,7 @@ namespace audioapi {
9
9
 
10
10
  AudioNode::AudioNode(BaseAudioContext *context) : context_(context) {
11
11
  audioBus_ = std::make_shared<AudioBus>(
12
- context->getSampleRate(),
13
- context->getBufferSizeInFrames(),
14
- channelCount_);
12
+ context->getSampleRate(), RENDER_QUANTUM_SIZE, channelCount_);
15
13
  }
16
14
 
17
15
  AudioNode::~AudioNode() {
@@ -49,6 +47,12 @@ void AudioNode::connectNode(const std::shared_ptr<AudioNode> &node) {
49
47
  node->onInputConnected(this);
50
48
  }
51
49
 
50
+ void AudioNode::disconnect() {
51
+ for (auto &outputNode : outputNodes_) {
52
+ disconnectNode(outputNode);
53
+ }
54
+ }
55
+
52
56
  void AudioNode::disconnect(const std::shared_ptr<AudioNode> &node) {
53
57
  context_->getNodeManager()->addPendingConnection(
54
58
  shared_from_this(), node, AudioNodeManager::ConnectionType::DISCONNECT);
@@ -126,7 +130,7 @@ AudioBus *AudioNode::processAudio(AudioBus *outputBus, int framesToProcess) {
126
130
  // - it has more than one output, so each output node can get the processed
127
131
  // data without re-calculating the node.
128
132
  bool canUseOutputBus =
129
- outputBus != 0 && inputNodes_.size() < 2 && outputNodes_.size() < 2;
133
+ outputBus != nullptr && inputNodes_.size() < 2 && outputNodes_.size() < 2;
130
134
 
131
135
  if (isAlreadyProcessed) {
132
136
  // If it was already processed in the rendering quantum, return it.
@@ -144,8 +148,9 @@ AudioBus *AudioNode::processAudio(AudioBus *outputBus, int framesToProcess) {
144
148
  }
145
149
 
146
150
  if (inputNodes_.empty()) {
147
- // If there are no connected inputs, process the node just to advance the
148
- // audio params. The node will output silence anyway.
151
+ // If there are no connected inputs, if processing node is the source node,
152
+ // it will fill processing bus with the audio data, otherwise it will return
153
+ // silence.
149
154
  processNode(processingBus, framesToProcess);
150
155
  return processingBus;
151
156
  }
@@ -200,6 +205,10 @@ void AudioNode::onInputDisabled() {
200
205
  }
201
206
 
202
207
  void AudioNode::onInputConnected(AudioNode *node) {
208
+ if (!isInitialized_) {
209
+ return;
210
+ }
211
+
203
212
  inputNodes_.push_back(node);
204
213
 
205
214
  if (node->isEnabled()) {
@@ -208,13 +217,17 @@ void AudioNode::onInputConnected(AudioNode *node) {
208
217
  }
209
218
 
210
219
  void AudioNode::onInputDisconnected(AudioNode *node) {
220
+ if (!isInitialized_) {
221
+ return;
222
+ }
223
+
211
224
  auto position = std::find(inputNodes_.begin(), inputNodes_.end(), node);
212
225
 
213
226
  if (position != inputNodes_.end()) {
214
227
  inputNodes_.erase(position);
215
228
  }
216
229
 
217
- if (inputNodes_.size() > 0) {
230
+ if (!inputNodes_.empty()) {
218
231
  return;
219
232
  }
220
233
 
@@ -222,7 +235,7 @@ void AudioNode::onInputDisconnected(AudioNode *node) {
222
235
  node->onInputDisabled();
223
236
  }
224
237
 
225
- for (auto outputNode : outputNodes_) {
238
+ for (const auto &outputNode : outputNodes_) {
226
239
  disconnectNode(outputNode);
227
240
  }
228
241
  }
@@ -23,6 +23,7 @@ class AudioNode : public std::enable_shared_from_this<AudioNode> {
23
23
  std::string getChannelCountMode() const;
24
24
  std::string getChannelInterpretation() const;
25
25
  void connect(const std::shared_ptr<AudioNode> &node);
26
+ void disconnect();
26
27
  void disconnect(const std::shared_ptr<AudioNode> &node);
27
28
 
28
29
  bool isEnabled() const;
@@ -85,8 +85,7 @@ void AudioScheduledSourceNode::updatePlaybackInfo(
85
85
  startOffset = std::max(startFrame, firstFrame) - firstFrame > 0
86
86
  ? std::max(startFrame, firstFrame) - firstFrame
87
87
  : 0;
88
- nonSilentFramesToProcess =
89
- std::min(lastFrame, stopFrame) - startFrame;
88
+ nonSilentFramesToProcess = std::min(lastFrame, stopFrame) - startFrame;
90
89
  processingBus->zero(0, startOffset);
91
90
  return;
92
91
  }
@@ -1,11 +1,6 @@
1
- #ifdef ANDROID
2
- #include "AudioPlayer.h"
3
- #else
4
- #include "IOSAudioPlayer.h"
5
- #endif
6
-
7
1
  #include "BaseAudioContext.h"
8
2
 
3
+ #include "AnalyserNode.h"
9
4
  #include "AudioArray.h"
10
5
  #include "AudioBuffer.h"
11
6
  #include "AudioBufferSourceNode.h"
@@ -22,30 +17,10 @@
22
17
  namespace audioapi {
23
18
 
24
19
  BaseAudioContext::BaseAudioContext() {
25
- #ifdef ANDROID
26
- audioPlayer_ = std::make_shared<AudioPlayer>(this->renderAudio());
27
- #else
28
- audioPlayer_ = std::make_shared<IOSAudioPlayer>(this->renderAudio());
29
- #endif
30
-
31
- audioDecoder_ = std::make_shared<AudioDecoder>(audioPlayer_->getSampleRate());
32
-
33
- sampleRate_ = audioPlayer_->getSampleRate();
34
- bufferSizeInFrames_ = audioPlayer_->getBufferSizeInFrames();
35
-
36
20
  nodeManager_ = std::make_shared<AudioNodeManager>();
37
21
  destination_ = std::make_shared<AudioDestinationNode>(this);
38
22
  }
39
23
 
40
- BaseAudioContext::~BaseAudioContext() {
41
- if (isRunning()) {
42
- return;
43
- }
44
-
45
- state_ = ContextState::CLOSED;
46
- audioPlayer_->stop();
47
- }
48
-
49
24
  std::string BaseAudioContext::getState() {
50
25
  return BaseAudioContext::toString(state_);
51
26
  }
@@ -54,10 +29,6 @@ int BaseAudioContext::getSampleRate() const {
54
29
  return sampleRate_;
55
30
  }
56
31
 
57
- int BaseAudioContext::getBufferSizeInFrames() const {
58
- return bufferSizeInFrames_;
59
- }
60
-
61
32
  std::size_t BaseAudioContext::getCurrentSampleFrame() const {
62
33
  return destination_->getCurrentSampleFrame();
63
34
  }
@@ -106,22 +77,16 @@ std::shared_ptr<PeriodicWave> BaseAudioContext::createPeriodicWave(
106
77
  sampleRate_, real, imag, length, disableNormalization);
107
78
  }
108
79
 
80
+ std::shared_ptr<AnalyserNode> BaseAudioContext::createAnalyser() {
81
+ return std::make_shared<AnalyserNode>(this);
82
+ }
83
+
109
84
  std::shared_ptr<AudioBuffer> BaseAudioContext::decodeAudioDataSource(
110
85
  const std::string &path) {
111
86
  auto audioBus = audioDecoder_->decodeWithFilePath(path);
112
87
  return std::make_shared<AudioBuffer>(audioBus);
113
88
  }
114
89
 
115
- std::function<void(AudioBus *, int)> BaseAudioContext::renderAudio() {
116
- if (!isRunning()) {
117
- return [](AudioBus *, int) {};
118
- }
119
-
120
- return [this](AudioBus *data, int frames) {
121
- destination_->renderAudio(data, frames);
122
- };
123
- }
124
-
125
90
  AudioNodeManager *BaseAudioContext::getNodeManager() {
126
91
  return nodeManager_.get();
127
92
  }
@@ -22,22 +22,16 @@ class BiquadFilterNode;
22
22
  class AudioDestinationNode;
23
23
  class AudioBufferSourceNode;
24
24
  class AudioDecoder;
25
-
26
- #ifdef ANDROID
27
- class AudioPlayer;
28
- #else
29
- class IOSAudioPlayer;
30
- #endif
25
+ class AnalyserNode;
31
26
 
32
27
  class BaseAudioContext {
33
28
  public:
34
29
  BaseAudioContext();
35
- ~BaseAudioContext();
30
+ virtual ~BaseAudioContext() = default;
36
31
 
37
32
  std::string getState();
38
33
  [[nodiscard]] int getSampleRate() const;
39
34
  [[nodiscard]] double getCurrentTime() const;
40
- [[nodiscard]] int getBufferSizeInFrames() const;
41
35
  [[nodiscard]] std::size_t getCurrentSampleFrame() const;
42
36
  std::shared_ptr<AudioDestinationNode> getDestination();
43
37
 
@@ -53,10 +47,10 @@ class BaseAudioContext {
53
47
  float *imag,
54
48
  bool disableNormalization,
55
49
  int length);
50
+ std::shared_ptr<AnalyserNode> createAnalyser();
56
51
 
57
52
  std::shared_ptr<AudioBuffer> decodeAudioDataSource(const std::string &path);
58
53
  std::shared_ptr<PeriodicWave> getBasicWaveForm(OscillatorType type);
59
- std::function<void(AudioBus *, int)> renderAudio();
60
54
  AudioNodeManager *getNodeManager();
61
55
  [[nodiscard]] bool isRunning() const;
62
56
  [[nodiscard]] bool isClosed() const;
@@ -64,16 +58,11 @@ class BaseAudioContext {
64
58
  protected:
65
59
  static std::string toString(ContextState state);
66
60
  std::shared_ptr<AudioDestinationNode> destination_;
67
- std::shared_ptr<AudioDecoder> audioDecoder_;
68
-
69
- #ifdef ANDROID
70
- std::shared_ptr<AudioPlayer> audioPlayer_;
71
- #else
72
- std::shared_ptr<IOSAudioPlayer> audioPlayer_;
73
- #endif
61
+ // init in AudioContext or OfflineContext constructor
62
+ std::shared_ptr<AudioDecoder> audioDecoder_ {};
74
63
 
75
- int sampleRate_;
76
- int bufferSizeInFrames_;
64
+ // init in AudioContext or OfflineContext constructor
65
+ int sampleRate_ {};
77
66
  ContextState state_ = ContextState::RUNNING;
78
67
  std::shared_ptr<AudioNodeManager> nodeManager_;
79
68
 
@@ -6,11 +6,14 @@
6
6
  // https://webaudio.github.io/web-audio-api/
7
7
 
8
8
  namespace audioapi {
9
- constexpr int SAMPLE_RATE = 44100;
9
+ constexpr int DEFAULT_SAMPLE_RATE = 48000;
10
+ constexpr int RENDER_QUANTUM_SIZE = 128;
10
11
  constexpr int CHANNEL_COUNT = 2;
12
+
11
13
  constexpr float MOST_POSITIVE_SINGLE_FLOAT = static_cast<float>(std::numeric_limits<float>::max());
12
14
  constexpr float MOST_NEGATIVE_SINGLE_FLOAT = static_cast<float>(std::numeric_limits<float>::lowest());
13
- constexpr float NYQUIST_FREQUENCY = SAMPLE_RATE / 2.0;
15
+
16
+ constexpr float NYQUIST_FREQUENCY = DEFAULT_SAMPLE_RATE / 2.0;
14
17
  static float MAX_DETUNE = 1200 * std::log2(MOST_POSITIVE_SINGLE_FLOAT);
15
18
  constexpr float MAX_GAIN = MOST_POSITIVE_SINGLE_FLOAT;
16
19
  constexpr float MAX_PAN = 1.0;
@@ -19,4 +22,10 @@ constexpr float MAX_FILTER_FREQUENCY = NYQUIST_FREQUENCY;
19
22
  constexpr float MIN_FILTER_FREQUENCY = 0.0;
20
23
  static float MAX_FILTER_GAIN = 40 * std::log10(MOST_POSITIVE_SINGLE_FLOAT);
21
24
  constexpr float MIN_FILTER_GAIN = -MAX_GAIN;
25
+
26
+ constexpr int MAX_FFT_SIZE = 32768;
27
+ constexpr int DEFAULT_FFT_SIZE = 2048;
28
+ constexpr double DEFAULT_MAX_DECIBELS = -30;
29
+ constexpr double DEFAULT_MIN_DECIBELS = -100;
30
+ const double DEFAULT_SMOOTHING_TIME_CONSTANT = 0.8;
22
31
  } // namespace audioapi
@@ -226,7 +226,7 @@ void PeriodicWave::createBandLimitedTables(
226
226
  0.0f);
227
227
  }
228
228
 
229
- // Zero out the nquist and DC components.
229
+ // Zero out the DC and nquist components.
230
230
  realFFTFrameData[0] = 0.0f;
231
231
  imaginaryFFTFrameData[0] = 0.0f;
232
232
 
@@ -234,7 +234,7 @@ void PeriodicWave::createBandLimitedTables(
234
234
 
235
235
  // Perform the inverse FFT to get the time domain representation of the
236
236
  // band-limited waveform.
237
- fftFrame.inverse(bandLimitedTables_[rangeIndex]);
237
+ fftFrame.doInverseFFT(bandLimitedTables_[rangeIndex]);
238
238
 
239
239
  if (!disableNormalization_ && rangeIndex == 0) {
240
240
  float maxValue =
@@ -13,7 +13,7 @@
13
13
  #define JSI_HOST_FUNCTION(NAME) \
14
14
  jsi::Value NAME( \
15
15
  jsi::Runtime &runtime, \
16
- const jsi::Value &thisVal, \
16
+ const jsi::Value &thisValue, \
17
17
  const jsi::Value *args, \
18
18
  size_t count)
19
19
 
@@ -5,6 +5,7 @@
5
5
  #include <memory>
6
6
  #include <string>
7
7
  #include <utility>
8
+ #include <functional>
8
9
 
9
10
  namespace audioapi {
10
11
 
@@ -23,4 +23,11 @@ float linearInterpolate(
23
23
  factor * (source[secondIndex] - source[firstIndex]);
24
24
  }
25
25
 
26
+ float linearToDecibels(float value) {
27
+ return 20 * log10f(value);
28
+ }
29
+
30
+ float decibelsToLinear(float value) {
31
+ return powf(10, value / 20);
32
+ }
26
33
  } // namespace audioapi::AudioUtils
@@ -1,12 +1,16 @@
1
1
  #pragma once
2
2
 
3
3
  #include <cstddef>
4
+ #include <cstdint>
5
+ #include <cmath>
4
6
 
5
7
  namespace audioapi::AudioUtils {
6
- size_t timeToSampleFrame(double time, int sampleRate);
7
8
 
9
+ size_t timeToSampleFrame(double time, int sampleRate);
8
10
  double sampleFrameToTime(int sampleFrame, int sampleRate);
9
11
 
10
12
  float linearInterpolate(const float *source, size_t firstIndex, size_t secondIndex, float factor);
11
13
 
14
+ float linearToDecibels(float value);
15
+ float decibelsToLinear(float value);
12
16
  } // namespace audioapi::AudioUtils
@@ -10,46 +10,92 @@
10
10
 
11
11
  namespace audioapi {
12
12
  #if defined(HAVE_ACCELERATE)
13
+ static std::unordered_map<size_t, FFTSetup> fftSetups_;
13
14
 
14
- void FFTFrame::inverse(float *timeDomainData) {
15
- FFTSetup fftSetup_ = vDSP_create_fftsetup(log2Size_, FFT_RADIX2);
16
- DSPSplitComplex freqDomainData;
17
- freqDomainData.realp = realData_;
18
- freqDomainData.imagp = imaginaryData_;
15
+ FFTFrame::FFTFrame(int size)
16
+ : size_(size),
17
+ log2Size_(static_cast<int>(log2(size))),
18
+ realData_(new float[size]),
19
+ imaginaryData_(new float[size]) {
20
+ fftSetup_ = getFFTSetupForSize(log2Size_);
21
+ frame_.realp = realData_;
22
+ frame_.imagp = imaginaryData_;
23
+ }
24
+
25
+ FFTFrame::~FFTFrame() {
26
+ delete[] realData_;
27
+ delete[] imaginaryData_;
28
+ }
19
29
 
20
- vDSP_fft_zrip(fftSetup_, &freqDomainData, 1, log2Size_, FFT_INVERSE);
21
- vDSP_ztoc(
22
- &freqDomainData,
23
- 1,
24
- reinterpret_cast<DSPComplex *>(timeDomainData),
25
- 2,
26
- size_ / 2);
30
+ FFTSetup FFTFrame::getFFTSetupForSize(size_t log2FFTSize) {
31
+ if (!fftSetups_.contains(log2FFTSize)) {
32
+ fftSetups_.emplace(
33
+ log2FFTSize, vDSP_create_fftsetup(log2FFTSize, FFT_RADIX2));
34
+ }
35
+
36
+ return fftSetups_.at(log2FFTSize);
37
+ }
38
+
39
+ void FFTFrame::doFFT(float *data) {
40
+ vDSP_ctoz(reinterpret_cast<DSPComplex *>(data), 2, &frame_, 1, size_ / 2);
41
+ vDSP_fft_zrip(fftSetup_, &frame_, 1, log2Size_, FFT_FORWARD);
42
+
43
+ VectorMath::multiplyByScalar(realData_, 0.5f, realData_, size_ / 2);
44
+ VectorMath::multiplyByScalar(imaginaryData_, 0.5f, imaginaryData_, size_ / 2);
45
+ }
46
+
47
+ void FFTFrame::doInverseFFT(float *data) {
48
+ vDSP_fft_zrip(fftSetup_, &frame_, 1, log2Size_, FFT_INVERSE);
49
+ vDSP_ztoc(&frame_, 1, reinterpret_cast<DSPComplex *>(data), 2, size_ / 2);
27
50
 
28
51
  // Scale the FFT data, beacuse of
29
52
  // https://developer.apple.com/library/archive/documentation/Performance/Conceptual/vDSP_Programming_Guide/UsingFourierTransforms/UsingFourierTransforms.html#//apple_ref/doc/uid/TP40005147-CH3-15892
30
53
  VectorMath::multiplyByScalar(
31
- timeDomainData, 1.0f / static_cast<float>(size_), timeDomainData, size_);
32
-
33
- vDSP_destroy_fftsetup(fftSetup_);
54
+ data, 1.0f / static_cast<float>(size_), data, size_);
34
55
  }
35
56
 
36
57
  #elif defined(ANDROID)
37
58
 
38
- void FFTFrame::inverse(float *timeDomainData) {
39
- fftwf_complex *freqDomainData = fftwf_alloc_complex(size_ / 2);
59
+ FFTFrame::FFTFrame(int size)
60
+ : size_(size),
61
+ log2Size_(static_cast<int>(log2(size))),
62
+ realData_(new float[size]),
63
+ imaginaryData_(new float[size]) {
64
+ frame_ = fftwf_alloc_complex(size / 2);
65
+ }
66
+
67
+ FFTFrame::~FFTFrame() {
68
+ delete[] realData_;
69
+ delete[] imaginaryData_;
70
+ fftwf_free(frame_);
71
+ }
72
+
73
+ void FFTFrame::doFFT(float *data) {
74
+ auto plan = fftwf_plan_dft_r2c_1d(size_, data, frame_, FFTW_ESTIMATE);
75
+ fftwf_execute(plan);
76
+ fftwf_destroy_plan(plan);
77
+
78
+ for (int i = 0; i < size_ / 2; ++i) {
79
+ realData_[i] = frame_[i][0];
80
+ imaginaryData_[i] = frame_[i][1];
81
+ }
82
+
83
+ VectorMath::multiplyByScalar(realData_, 0.5f, realData_, size_ / 2);
84
+ VectorMath::multiplyByScalar(imaginaryData_, 0.5f, imaginaryData_, size_ / 2);
85
+ }
86
+
87
+ void FFTFrame::doInverseFFT(float *data) {
40
88
  for (int i = 0; i < size_ / 2; i++) {
41
- freqDomainData[i][0] = realData_[i];
42
- freqDomainData[i][1] = imaginaryData_[i];
89
+ frame_[i][0] = realData_[i];
90
+ frame_[i][1] = imaginaryData_[i];
43
91
  }
44
92
 
45
- auto plan = fftwf_plan_dft_c2r_1d(
46
- size_, freqDomainData, timeDomainData, FFTW_ESTIMATE);
93
+ auto plan = fftwf_plan_dft_c2r_1d(size_, frame_, data, FFTW_ESTIMATE);
47
94
  fftwf_execute(plan);
48
95
  fftwf_destroy_plan(plan);
49
- fftwf_free(freqDomainData);
50
96
 
51
97
  VectorMath::multiplyByScalar(
52
- timeDomainData, 1.0f / static_cast<float>(size_), timeDomainData, size_);
98
+ data, 1.0f / static_cast<float>(size_), data, size_);
53
99
  }
54
100
 
55
101
  #endif
@@ -31,22 +31,24 @@
31
31
  #include <algorithm>
32
32
  #include <cmath>
33
33
  #include <utility>
34
+ #include <unordered_map>
34
35
 
35
36
  #include "VectorMath.h"
36
37
 
38
+ #if defined(HAVE_ACCELERATE)
39
+ #include <Accelerate/Accelerate.h>
40
+ #endif
41
+
42
+ #if defined(ANDROID)
43
+ #include <fftw3.h>
44
+ #endif
45
+
37
46
  namespace audioapi {
38
47
 
39
48
  class FFTFrame {
40
49
  public:
41
- explicit FFTFrame(int size)
42
- : size_(size),
43
- log2Size_(static_cast<int>(log2(size))),
44
- realData_(new float[size]),
45
- imaginaryData_(new float[size]) {}
46
- ~FFTFrame() {
47
- delete[] realData_;
48
- delete[] imaginaryData_;
49
- }
50
+ explicit FFTFrame(int size);
51
+ ~FFTFrame();
50
52
 
51
53
  [[nodiscard]] float *getRealData() const {
52
54
  return realData_;
@@ -55,13 +57,26 @@ class FFTFrame {
55
57
  return imaginaryData_;
56
58
  }
57
59
 
58
- void inverse(float *data);
60
+ void doFFT(float *data);
61
+
62
+ void doInverseFFT(float *data);
59
63
 
60
64
  private:
61
65
  int size_;
62
66
  int log2Size_;
63
67
  float *realData_;
64
68
  float *imaginaryData_;
69
+
70
+ #if defined(HAVE_ACCELERATE)
71
+ FFTSetup fftSetup_;
72
+ DSPSplitComplex frame_;
73
+
74
+ static FFTSetup getFFTSetupForSize(size_t log2FFTSize);
75
+ #endif
76
+
77
+ #if defined(ANDROID)
78
+ fftwf_complex *frame_;
79
+ #endif
65
80
  };
66
81
 
67
82
  } // namespace audioapi
@@ -24,6 +24,7 @@
24
24
  */
25
25
 
26
26
  #include "VectorMath.h"
27
+ #include "AudioUtils.h"
27
28
 
28
29
  #if defined(HAVE_ACCELERATE)
29
30
  #include <Accelerate/Accelerate.h>
@@ -691,4 +692,13 @@ void multiplyByScalarThenAddToOutput(
691
692
  }
692
693
 
693
694
  #endif
695
+
696
+ void linearToDecibels(
697
+ const float *inputVector,
698
+ float *outputVector,
699
+ size_t numberOfElementsToProcess) {
700
+ for (int i = 0; i < numberOfElementsToProcess; i++) {
701
+ outputVector[i] = AudioUtils::linearToDecibels(inputVector[i]);
702
+ }
703
+ }
694
704
  } // namespace audioapi::VectorMath
@@ -42,4 +42,6 @@ void multiply(const float *inputVector1, const float *inputVector2, float *outpu
42
42
 
43
43
  // Finds the maximum magnitude of a float vector.
44
44
  float maximumMagnitude(const float *inputVector, size_t numberOfElementsToProcess);
45
+
46
+ void linearToDecibels(const float *inputVector, float *outputVector, size_t numberOfElementsToProcess);
45
47
  } // namespace audioapi::VectorMath
@@ -12,12 +12,13 @@ typedef void (^RenderAudioBlock)(AudioBufferList *outputBuffer, int numFrames);
12
12
  @property (nonatomic, strong) AVAudioFormat *format;
13
13
  @property (nonatomic, strong) AVAudioSourceNode *sourceNode;
14
14
  @property (nonatomic, copy) RenderAudioBlock renderAudio;
15
+ @property (nonatomic, assign) int sampleRate;
15
16
 
16
17
  - (instancetype)initWithRenderAudioBlock:(RenderAudioBlock)renderAudio;
17
18
 
18
- - (int)getSampleRate;
19
+ - (instancetype)initWithRenderAudioBlock:(RenderAudioBlock)renderAudio sampleRate:(int)sampleRate;
19
20
 
20
- - (int)getBufferSizeInFrames;
21
+ - (int)getSampleRate;
21
22
 
22
23
  - (void)start;
23
24