react-native-audio-api 0.2.0 → 0.3.0-rc1

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 (78) hide show
  1. package/android/src/main/cpp/AudioPlayer/AudioPlayer.cpp +16 -7
  2. package/android/src/main/cpp/AudioPlayer/AudioPlayer.h +3 -2
  3. package/common/cpp/AudioAPIInstaller/AudioAPIInstallerHostObject.cpp +5 -3
  4. package/common/cpp/AudioAPIInstaller/AudioAPIInstallerHostObject.h +5 -1
  5. package/common/cpp/HostObjects/AudioBufferHostObject.cpp +6 -0
  6. package/common/cpp/HostObjects/AudioContextHostObject.cpp +2 -2
  7. package/common/cpp/HostObjects/AudioContextHostObject.h +4 -3
  8. package/common/cpp/HostObjects/BaseAudioContextHostObject.cpp +28 -2
  9. package/common/cpp/HostObjects/BaseAudioContextHostObject.h +3 -1
  10. package/common/cpp/core/AudioArray.cpp +28 -14
  11. package/common/cpp/core/AudioArray.h +20 -14
  12. package/common/cpp/core/AudioBuffer.cpp +14 -11
  13. package/common/cpp/core/AudioBuffer.h +1 -0
  14. package/common/cpp/core/AudioBufferSourceNode.cpp +29 -19
  15. package/common/cpp/core/AudioBufferSourceNode.h +1 -1
  16. package/common/cpp/core/AudioBus.cpp +276 -115
  17. package/common/cpp/core/AudioBus.h +29 -9
  18. package/common/cpp/core/AudioContext.cpp +5 -9
  19. package/common/cpp/core/AudioDestinationNode.cpp +10 -8
  20. package/common/cpp/core/AudioDestinationNode.h +4 -4
  21. package/common/cpp/core/AudioNode.cpp +25 -17
  22. package/common/cpp/core/AudioNode.h +5 -5
  23. package/common/cpp/core/AudioNodeManager.cpp +10 -7
  24. package/common/cpp/core/AudioNodeManager.h +11 -4
  25. package/common/cpp/core/AudioScheduledSourceNode.cpp +2 -2
  26. package/common/cpp/core/BaseAudioContext.cpp +46 -12
  27. package/common/cpp/core/BaseAudioContext.h +12 -5
  28. package/common/cpp/core/BiquadFilterNode.cpp +5 -3
  29. package/common/cpp/core/GainNode.cpp +1 -1
  30. package/common/cpp/core/OscillatorNode.cpp +4 -4
  31. package/common/cpp/core/OscillatorNode.h +2 -2
  32. package/common/cpp/core/StereoPannerNode.cpp +10 -7
  33. package/common/cpp/core/StereoPannerNode.h +1 -1
  34. package/common/cpp/utils/FFTFrame.h +5 -1
  35. package/common/cpp/utils/JsiPromise.cpp +59 -0
  36. package/common/cpp/utils/JsiPromise.h +42 -0
  37. package/common/cpp/utils/Locker.h +8 -6
  38. package/common/cpp/utils/VectorMath.cpp +71 -55
  39. package/common/cpp/utils/android/FFTFrame.cpp +12 -11
  40. package/common/cpp/utils/ios/FFTFrame.cpp +6 -1
  41. package/common/cpp/wrappers/BaseAudioContextWrapper.cpp +7 -0
  42. package/common/cpp/wrappers/BaseAudioContextWrapper.h +1 -0
  43. package/ios/AudioAPIModule.mm +3 -1
  44. package/ios/AudioDecoder/AudioDecoder.h +17 -0
  45. package/ios/AudioDecoder/AudioDecoder.m +167 -0
  46. package/ios/AudioDecoder/IOSAudioDecoder.h +26 -0
  47. package/ios/AudioDecoder/IOSAudioDecoder.mm +40 -0
  48. package/ios/AudioPlayer/AudioPlayer.h +1 -1
  49. package/ios/AudioPlayer/AudioPlayer.m +0 -1
  50. package/ios/AudioPlayer/IOSAudioPlayer.h +5 -5
  51. package/ios/AudioPlayer/IOSAudioPlayer.mm +4 -3
  52. package/lib/module/core/BaseAudioContext.js +5 -0
  53. package/lib/module/core/BaseAudioContext.js.map +1 -1
  54. package/lib/module/index.js +235 -17
  55. package/lib/module/index.js.map +1 -1
  56. package/lib/module/index.native.js +18 -0
  57. package/lib/module/index.native.js.map +1 -0
  58. package/lib/module/utils/resolveAudioSource.js +10 -0
  59. package/lib/module/utils/resolveAudioSource.js.map +1 -0
  60. package/lib/typescript/core/BaseAudioContext.d.ts +2 -1
  61. package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
  62. package/lib/typescript/core/types.d.ts +6 -0
  63. package/lib/typescript/core/types.d.ts.map +1 -1
  64. package/lib/typescript/index.d.ts +100 -13
  65. package/lib/typescript/index.d.ts.map +1 -1
  66. package/lib/typescript/index.native.d.ts +14 -0
  67. package/lib/typescript/index.native.d.ts.map +1 -0
  68. package/lib/typescript/interfaces.d.ts +1 -0
  69. package/lib/typescript/interfaces.d.ts.map +1 -1
  70. package/lib/typescript/utils/resolveAudioSource.d.ts +3 -0
  71. package/lib/typescript/utils/resolveAudioSource.d.ts.map +1 -0
  72. package/package.json +4 -2
  73. package/src/core/BaseAudioContext.ts +12 -1
  74. package/src/core/types.ts +7 -0
  75. package/src/index.native.ts +25 -0
  76. package/src/index.ts +413 -19
  77. package/src/interfaces.ts +1 -0
  78. package/src/utils/resolveAudioSource.ts +14 -0
@@ -0,0 +1,59 @@
1
+ #include "JsiPromise.h"
2
+
3
+ #include <jsi/jsi.h>
4
+ #include <ReactCommon/CallInvoker.h>
5
+ #include <functional>
6
+
7
+ namespace JsiPromise {
8
+
9
+ using namespace facebook;
10
+
11
+ jsi::Value PromiseVendor::createPromise(std::function<void(std::shared_ptr<Promise>)> func) {
12
+ if (_runtime == nullptr) {
13
+ throw new std::runtime_error("Runtime was null!");
14
+ }
15
+ auto& runtime = *_runtime;
16
+ auto callInvoker = _callInvoker;
17
+
18
+ // get Promise constructor
19
+ auto promiseCtor = runtime.global().getPropertyAsFunction(runtime, "Promise");
20
+
21
+ // create a "run" function (first Promise arg)
22
+ auto runPromise = jsi::Function::createFromHostFunction(runtime,
23
+ jsi::PropNameID::forUtf8(runtime, "runPromise"),
24
+ 2,
25
+ [callInvoker, func](jsi::Runtime& runtime,
26
+ const jsi::Value& thisValue,
27
+ const jsi::Value* arguments,
28
+ size_t count) -> jsi::Value {
29
+ auto resolveLocal = arguments[0].asObject(runtime).asFunction(runtime);
30
+ auto resolve = std::make_shared<jsi::Function>(std::move(resolveLocal));
31
+ auto rejectLocal = arguments[1].asObject(runtime).asFunction(runtime);
32
+ auto reject = std::make_shared<jsi::Function>(std::move(rejectLocal));
33
+
34
+ auto resolveWrapper = [resolve, &runtime, callInvoker](jsi::Value value) -> void {
35
+ auto valueShared = std::make_shared<jsi::Value>(std::move(value));
36
+ callInvoker->invokeAsync([resolve, &runtime, valueShared]() -> void {
37
+ resolve->call(runtime, *valueShared);
38
+ });
39
+ };
40
+
41
+ auto rejectWrapper = [reject, &runtime, callInvoker](const std::string& errorMessage) -> void {
42
+ auto error = jsi::JSError(runtime, errorMessage);
43
+ auto errorShared = std::make_shared<jsi::JSError>(error);
44
+ callInvoker->invokeAsync([reject, &runtime, errorShared]() -> void {
45
+ reject->call(runtime, errorShared->value());
46
+ });
47
+ };
48
+
49
+ auto promise = std::make_shared<Promise>(resolveWrapper, rejectWrapper);
50
+ func(promise);
51
+
52
+ return jsi::Value::undefined();
53
+ });
54
+
55
+ // return new Promise((resolve, reject) => ...)
56
+ return promiseCtor.callAsConstructor(runtime, runPromise);
57
+ }
58
+
59
+ } // namespace JsiPromise
@@ -0,0 +1,42 @@
1
+ #pragma once
2
+
3
+ #include <ReactCommon/CallInvoker.h>
4
+ #include <jsi/jsi.h>
5
+ #include <utility>
6
+ #include <memory>
7
+ #include <string>
8
+
9
+ namespace JsiPromise {
10
+
11
+ using namespace facebook;
12
+
13
+ class Promise {
14
+ public:
15
+ Promise(std::function<void(jsi::Value)> resolve, std::function<void(const std::string&)> reject): _resolve(std::move(resolve)), _reject(std::move(reject)) {}
16
+
17
+ bool isResolved;
18
+ void resolve(jsi::Value&& value) {
19
+ _resolve(std::forward<jsi::Value>(value));
20
+ }
21
+ void reject(const std::string& errorMessage) {
22
+ _reject(errorMessage);
23
+ }
24
+
25
+ private:
26
+ std::function<void(jsi::Value)> _resolve;
27
+ std::function<void(const std::string&)> _reject;
28
+ };
29
+
30
+ class PromiseVendor {
31
+ public:
32
+ PromiseVendor(jsi::Runtime* runtime, std::shared_ptr<react::CallInvoker> callInvoker): _runtime(runtime), _callInvoker(callInvoker) {}
33
+
34
+ public:
35
+ jsi::Value createPromise(std::function<void(std::shared_ptr<Promise>)> func);
36
+
37
+ private:
38
+ jsi::Runtime* _runtime;
39
+ std::shared_ptr<react::CallInvoker> _callInvoker;
40
+ };
41
+
42
+ } // namespace JsiPromise
@@ -7,8 +7,8 @@ namespace audioapi {
7
7
  // Small easy interface to manage locking
8
8
  class Locker {
9
9
  public:
10
- Locker(): lockPtr_(0) {}
11
- explicit Locker(std::mutex& lockPtr): lockPtr_(&lockPtr) {
10
+ Locker() : lockPtr_(0) {}
11
+ explicit Locker(std::mutex &lockPtr) : lockPtr_(&lockPtr) {
12
12
  lock();
13
13
  }
14
14
 
@@ -16,7 +16,9 @@ class Locker {
16
16
  unlock();
17
17
  }
18
18
 
19
- explicit operator bool() const { return !!lockPtr_; }
19
+ explicit operator bool() const {
20
+ return !!lockPtr_;
21
+ }
20
22
 
21
23
  void lock() {
22
24
  if (lockPtr_) {
@@ -26,11 +28,11 @@ class Locker {
26
28
 
27
29
  void unlock() {
28
30
  if (lockPtr_) {
29
- lockPtr_->unlock();
31
+ lockPtr_->unlock();
30
32
  }
31
33
  }
32
34
 
33
- static Locker tryLock(std::mutex& lock) {
35
+ static Locker tryLock(std::mutex &lock) {
34
36
  Locker result = Locker();
35
37
 
36
38
  if (lock.try_lock()) {
@@ -41,7 +43,7 @@ class Locker {
41
43
  }
42
44
 
43
45
  private:
44
- std::mutex* lockPtr_;
46
+ std::mutex *lockPtr_;
45
47
  };
46
48
 
47
49
  } // namespace audioapi
@@ -115,8 +115,20 @@ float maximumMagnitude(
115
115
  return maximumValue;
116
116
  }
117
117
 
118
- void multiplyByScalarThenAddToOutput(const float* inputVector, float scalar, float* outputVector, size_t numberOfElementsToProcess) {
119
- vDSP_vsma(inputVector, 1, &scalar, outputVector, 1, outputVector, 1, numberOfElementsToProcess);
118
+ void multiplyByScalarThenAddToOutput(
119
+ const float *inputVector,
120
+ float scalar,
121
+ float *outputVector,
122
+ size_t numberOfElementsToProcess) {
123
+ vDSP_vsma(
124
+ inputVector,
125
+ 1,
126
+ &scalar,
127
+ outputVector,
128
+ 1,
129
+ outputVector,
130
+ 1,
131
+ numberOfElementsToProcess);
120
132
  }
121
133
 
122
134
  #else
@@ -609,69 +621,73 @@ float maximumMagnitude(
609
621
  return max;
610
622
  }
611
623
 
612
- void multiplyByScalarThenAddToOutput(const float* inputVector, float scalar, float* outputVector, size_t numberOfElementsToProcess) {
613
- size_t n = numberOfElementsToProcess;
624
+ void multiplyByScalarThenAddToOutput(
625
+ const float *inputVector,
626
+ float scalar,
627
+ float *outputVector,
628
+ size_t numberOfElementsToProcess) {
629
+ size_t n = numberOfElementsToProcess;
614
630
 
615
631
  #if HAVE_X86_SSE2
616
- // If the inputVector address is not 16-byte aligned, the first several frames (at most three) should be processed separately.
617
- while (!is16ByteAligned(inputVector) && n) {
618
- *outputVector += scalar * *inputVector;
619
- inputVector++;
620
- outputVector++;
621
- n--;
622
- }
632
+ // If the inputVector address is not 16-byte aligned, the first several frames
633
+ // (at most three) should be processed separately.
634
+ while (!is16ByteAligned(inputVector) && n) {
635
+ *outputVector += scalar * *inputVector;
636
+ inputVector++;
637
+ outputVector++;
638
+ n--;
639
+ }
623
640
 
624
- // Now the inputVector is aligned, use SSE.
625
- size_t tailFrames = n % 4;
626
- const float* endP = outputVector + n - tailFrames;
627
-
628
- __m128 pSource;
629
- __m128 dest;
630
- __m128 temp;
631
- __m128 mScale = _mm_set_ps1(scalar);
632
-
633
- bool destAligned = is16ByteAligned(outputVector);
634
-
635
- #define SSE2_MULT_ADD(loadInstr, storeInstr) \
636
- while (outputVector < endP) \
637
- { \
638
- pSource = _mm_load_ps(inputVector); \
639
- temp = _mm_mul_ps(pSource, mScale); \
640
- dest = _mm_##loadInstr##_ps(outputVector); \
641
- dest = _mm_add_ps(dest, temp); \
642
- _mm_##storeInstr##_ps(outputVector, dest); \
643
- inputVector += 4; \
644
- outputVector += 4; \
645
- }
641
+ // Now the inputVector is aligned, use SSE.
642
+ size_t tailFrames = n % 4;
643
+ const float *endP = outputVector + n - tailFrames;
644
+
645
+ __m128 pSource;
646
+ __m128 dest;
647
+ __m128 temp;
648
+ __m128 mScale = _mm_set_ps1(scalar);
649
+
650
+ bool destAligned = is16ByteAligned(outputVector);
651
+
652
+ #define SSE2_MULT_ADD(loadInstr, storeInstr) \
653
+ while (outputVector < endP) { \
654
+ pSource = _mm_load_ps(inputVector); \
655
+ temp = _mm_mul_ps(pSource, mScale); \
656
+ dest = _mm_##loadInstr##_ps(outputVector); \
657
+ dest = _mm_add_ps(dest, temp); \
658
+ _mm_##storeInstr##_ps(outputVector, dest); \
659
+ inputVector += 4; \
660
+ outputVector += 4; \
661
+ }
646
662
 
647
- if (destAligned)
648
- SSE2_MULT_ADD(load, store)
649
- else
650
- SSE2_MULT_ADD(loadu, storeu)
663
+ if (destAligned)
664
+ SSE2_MULT_ADD(load, store)
665
+ else
666
+ SSE2_MULT_ADD(loadu, storeu)
651
667
 
652
- n = tailFrames;
668
+ n = tailFrames;
653
669
  #elif HAVE_ARM_NEON_INTRINSICS
654
- size_t tailFrames = n % 4;
655
- const float* endP = outputVector + n - tailFrames;
670
+ size_t tailFrames = n % 4;
671
+ const float *endP = outputVector + n - tailFrames;
656
672
 
657
- float32x4_t k = vdupq_n_f32(scalar);
658
- while (outputVector < endP) {
659
- float32x4_t source = vld1q_f32(inputVector);
660
- float32x4_t dest = vld1q_f32(outputVector);
673
+ float32x4_t k = vdupq_n_f32(scalar);
674
+ while (outputVector < endP) {
675
+ float32x4_t source = vld1q_f32(inputVector);
676
+ float32x4_t dest = vld1q_f32(outputVector);
661
677
 
662
- dest = vmlaq_f32(dest, source, k);
663
- vst1q_f32(outputVector, dest);
678
+ dest = vmlaq_f32(dest, source, k);
679
+ vst1q_f32(outputVector, dest);
664
680
 
665
- inputVector += 4;
666
- outputVector += 4;
667
- }
668
- n = tailFrames;
681
+ inputVector += 4;
682
+ outputVector += 4;
683
+ }
684
+ n = tailFrames;
669
685
  #endif
670
- while (n--) {
671
- *outputVector += *inputVector * scalar;
672
- ++inputVector;
673
- ++outputVector;
674
- }
686
+ while (n--) {
687
+ *outputVector += *inputVector * scalar;
688
+ ++inputVector;
689
+ ++outputVector;
690
+ }
675
691
  }
676
692
 
677
693
  #endif
@@ -4,19 +4,20 @@
4
4
 
5
5
  namespace audioapi {
6
6
  void FFTFrame::inverse(float *timeDomainData) {
7
- fftwf_complex *freqDomainData = fftwf_alloc_complex(size_ / 2);
8
- for(int i = 0; i < size_ / 2; i++) {
9
- freqDomainData[i][0] = realData_[i];
10
- freqDomainData[i][1] = imaginaryData_[i];
11
- }
7
+ fftwf_complex *freqDomainData = fftwf_alloc_complex(size_ / 2);
8
+ for (int i = 0; i < size_ / 2; i++) {
9
+ freqDomainData[i][0] = realData_[i];
10
+ freqDomainData[i][1] = imaginaryData_[i];
11
+ }
12
12
 
13
- auto plan = fftwf_plan_dft_c2r_1d(size_, freqDomainData, timeDomainData, FFTW_ESTIMATE);
14
- fftwf_execute(plan);
15
- fftwf_destroy_plan(plan);
16
- fftwf_free(freqDomainData);
13
+ auto plan = fftwf_plan_dft_c2r_1d(
14
+ size_, freqDomainData, timeDomainData, FFTW_ESTIMATE);
15
+ fftwf_execute(plan);
16
+ fftwf_destroy_plan(plan);
17
+ fftwf_free(freqDomainData);
17
18
 
18
- VectorMath::multiplyByScalar(
19
- timeDomainData, 1.0f / static_cast<float>(size_), timeDomainData, size_);
19
+ VectorMath::multiplyByScalar(
20
+ timeDomainData, 1.0f / static_cast<float>(size_), timeDomainData, size_);
20
21
  }
21
22
  } // namespace audioapi
22
23
  #endif
@@ -11,7 +11,12 @@ void FFTFrame::inverse(float *timeDomainData) {
11
11
  freqDomainData.imagp = imaginaryData_;
12
12
 
13
13
  vDSP_fft_zrip(fftSetup_, &freqDomainData, 1, log2Size_, FFT_INVERSE);
14
- vDSP_ztoc(&freqDomainData, 1, reinterpret_cast<DSPComplex *>(timeDomainData), 2, size_ / 2);
14
+ vDSP_ztoc(
15
+ &freqDomainData,
16
+ 1,
17
+ reinterpret_cast<DSPComplex *>(timeDomainData),
18
+ 2,
19
+ size_ / 2);
15
20
 
16
21
  // Scale the FFT data, beacuse of
17
22
  // https://developer.apple.com/library/archive/documentation/Performance/Conceptual/vDSP_Programming_Guide/UsingFourierTransforms/UsingFourierTransforms.html#//apple_ref/doc/uid/TP40005147-CH3-15892
@@ -73,4 +73,11 @@ BaseAudioContextWrapper::createPeriodicWave(
73
73
  context_->createPeriodicWave(real, imag, disableNormalization, length);
74
74
  return std::make_shared<PeriodicWaveWrapper>(periodicWave);
75
75
  }
76
+
77
+ std::shared_ptr<AudioBufferWrapper>
78
+ BaseAudioContextWrapper::decodeAudioDataSource(const std::string &source) {
79
+ return std::make_shared<AudioBufferWrapper>(
80
+ context_->decodeAudioDataSource(source));
81
+ }
82
+
76
83
  } // namespace audioapi
@@ -41,6 +41,7 @@ class BaseAudioContextWrapper {
41
41
  float *imag,
42
42
  bool disableNormalization,
43
43
  int length);
44
+ std::shared_ptr<AudioBufferWrapper> decodeAudioDataSource(const std::string &source);
44
45
 
45
46
  protected:
46
47
  std::shared_ptr<AudioDestinationNodeWrapper> destination_;
@@ -1,6 +1,8 @@
1
1
  #import "AudioAPIModule.h"
2
2
 
3
+ #import <ReactCommon/RCTTurboModule.h>
3
4
  #import <React/RCTBridge+Private.h>
5
+ #import <React/RCTBridge.h>
4
6
  #import <React/RCTUtils.h>
5
7
  #import <jsi/jsi.h>
6
8
 
@@ -33,7 +35,7 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install)
33
35
  auto &runtime = *jsRuntime;
34
36
 
35
37
  auto wrapper = std::make_shared<audioapi::AudioAPIInstallerWrapper>();
36
- auto hostObject = std::make_shared<audioapi::AudioAPIInstallerHostObject>(wrapper);
38
+ auto hostObject = std::make_shared<audioapi::AudioAPIInstallerHostObject>(wrapper, jsRuntime, cxxBridge.jsCallInvoker);
37
39
  auto object = jsi::Object::createFromHostObject(runtime, hostObject);
38
40
  runtime.global().setProperty(runtime, "__AudioAPIInstaller", std::move(object));
39
41
 
@@ -0,0 +1,17 @@
1
+ #pragma once
2
+
3
+ #import <AVFoundation/AVFoundation.h>
4
+ #import <Foundation/Foundation.h>
5
+
6
+ @interface AudioDecoder : NSObject
7
+
8
+ @property (nonatomic, strong) AVAudioPCMBuffer *buffer;
9
+ @property (nonatomic, assign) int sampleRate;
10
+
11
+ - (instancetype)initWithSampleRate:(int)sampleRate;
12
+
13
+ - (const AudioBufferList *)decode:(NSString *)pathOrURL;
14
+
15
+ - (void)cleanup;
16
+
17
+ @end
@@ -0,0 +1,167 @@
1
+ #import <AudioDecoder.h>
2
+
3
+ @implementation AudioDecoder
4
+
5
+ - (instancetype)initWithSampleRate:(int)sampleRate
6
+ {
7
+ if (self = [super init]) {
8
+ self.sampleRate = sampleRate;
9
+ }
10
+ return self;
11
+ }
12
+
13
+ - (const AudioBufferList *)decode:(NSString *)pathOrURL
14
+ {
15
+ // check if the input is a URL or a local file path
16
+ NSURL *url = [NSURL URLWithString:pathOrURL];
17
+
18
+ if (url && url.scheme) {
19
+ self.buffer = [self decodeWithURL:url];
20
+ } else {
21
+ self.buffer = [self decodeWithFilePath:pathOrURL];
22
+ }
23
+
24
+ return self.buffer.audioBufferList;
25
+ }
26
+
27
+ - (AVAudioPCMBuffer *)decodeWithFilePath:(NSString *)path
28
+ {
29
+ NSError *error = nil;
30
+ NSURL *fileURL = [NSURL fileURLWithPath:path];
31
+ AVAudioFile *audioFile = [[AVAudioFile alloc] initForReading:fileURL error:&error];
32
+
33
+ if (error) {
34
+ NSLog(@"Error occurred while opening the audio file: %@", [error localizedDescription]);
35
+ return nil;
36
+ }
37
+ AVAudioPCMBuffer *buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:[audioFile processingFormat]
38
+ frameCapacity:[audioFile length]];
39
+
40
+ AVAudioFormat *format = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32
41
+ sampleRate:self.sampleRate
42
+ channels:buffer.audioBufferList->mNumberBuffers
43
+ interleaved:NO];
44
+
45
+ [audioFile readIntoBuffer:buffer error:&error];
46
+ if (error) {
47
+ NSLog(@"Error occurred while reading the audio file: %@", [error localizedDescription]);
48
+ return nil;
49
+ }
50
+
51
+ if (self.sampleRate != audioFile.processingFormat.sampleRate) {
52
+ return [self convertBuffer:buffer ToFormat:format];
53
+ }
54
+
55
+ return buffer;
56
+ }
57
+
58
+ - (AVAudioPCMBuffer *)decodeWithURL:(NSURL *)url
59
+ {
60
+ __block NSURL *tempFileURL = nil;
61
+
62
+ dispatch_group_t group = dispatch_group_create();
63
+
64
+ dispatch_group_enter(group);
65
+
66
+ [self downloadFileFromURL:url
67
+ completion:^(NSURL *downloadedFileURL, NSError *downloadError) {
68
+ if (downloadError) {
69
+ NSLog(@"Error downloading file: %@", downloadError.localizedDescription);
70
+ tempFileURL = nil;
71
+ } else {
72
+ tempFileURL = downloadedFileURL;
73
+ }
74
+
75
+ dispatch_group_leave(group);
76
+ }];
77
+
78
+ dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
79
+
80
+ if (!tempFileURL) {
81
+ NSLog(@"Cannot process given url");
82
+ return nil;
83
+ }
84
+
85
+ return [self decodeWithFilePath:tempFileURL.path];
86
+ }
87
+
88
+ - (void)downloadFileFromURL:(NSURL *)url completion:(void (^)(NSURL *tempFileURL, NSError *error))completion
89
+ {
90
+ // get unique file path in temporary dir
91
+ NSString *tempDirectory = NSTemporaryDirectory();
92
+ NSString *timestamp = [NSString stringWithFormat:@"_%@", @((long long)[[NSDate date] timeIntervalSince1970])];
93
+ NSString *fileNameWithTimestamp = [url.lastPathComponent stringByDeletingPathExtension];
94
+ fileNameWithTimestamp = [fileNameWithTimestamp stringByAppendingString:timestamp];
95
+ NSString *fileExtension =
96
+ [url.pathExtension length] > 0 ? [NSString stringWithFormat:@".%@", url.pathExtension] : @"";
97
+ NSString *tempFilePath =
98
+ [tempDirectory stringByAppendingPathComponent:[fileNameWithTimestamp stringByAppendingString:fileExtension]];
99
+ NSURL *tempFileURL = [NSURL fileURLWithPath:tempFilePath];
100
+
101
+ // download file
102
+ NSURLSession *session = [NSURLSession sharedSession];
103
+ NSURLSessionDownloadTask *downloadTask = [session
104
+ downloadTaskWithURL:url
105
+ completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
106
+ if (error) {
107
+ NSLog(@"Error downloading file: %@", error.localizedDescription);
108
+ if (completion) {
109
+ completion(nil, error);
110
+ }
111
+ return;
112
+ }
113
+
114
+ // move to generated file path in temporary dir
115
+ NSError *fileError = nil;
116
+ BOOL success = [[NSFileManager defaultManager] moveItemAtURL:location toURL:tempFileURL error:&fileError];
117
+ if (success) {
118
+ NSLog(@"File downloaded successfully to %@", tempFileURL.path);
119
+ if (completion) {
120
+ completion(tempFileURL, nil);
121
+ }
122
+ } else {
123
+ NSLog(@"Error moving downloaded file: %@", fileError.localizedDescription);
124
+ if (completion) {
125
+ completion(nil, fileError);
126
+ }
127
+ }
128
+ }];
129
+
130
+ [downloadTask resume];
131
+ }
132
+
133
+ - (AVAudioPCMBuffer *)convertBuffer:(AVAudioPCMBuffer *)buffer ToFormat:(AVAudioFormat *)format
134
+ {
135
+ NSError *error = nil;
136
+ AVAudioConverter *converter = [[AVAudioConverter alloc] initFromFormat:buffer.format toFormat:format];
137
+ AVAudioPCMBuffer *convertedBuffer =
138
+ [[AVAudioPCMBuffer alloc] initWithPCMFormat:format frameCapacity:(AVAudioFrameCount)buffer.frameCapacity];
139
+
140
+ AVAudioConverterInputBlock inputBlock =
141
+ ^AVAudioBuffer *(AVAudioPacketCount inNumberOfPackets, AVAudioConverterInputStatus *outStatus)
142
+ {
143
+ if (buffer.frameLength > 0) {
144
+ *outStatus = AVAudioConverterInputStatus_HaveData;
145
+ return buffer;
146
+ } else {
147
+ *outStatus = AVAudioConverterInputStatus_NoDataNow;
148
+ return nil;
149
+ }
150
+ };
151
+
152
+ [converter convertToBuffer:convertedBuffer error:&error withInputFromBlock:inputBlock];
153
+
154
+ if (error) {
155
+ NSLog(@"Error occurred while converting the audio file: %@", [error localizedDescription]);
156
+ return nil;
157
+ }
158
+
159
+ return convertedBuffer;
160
+ }
161
+
162
+ - (void)cleanup
163
+ {
164
+ self.buffer = nil;
165
+ }
166
+
167
+ @end
@@ -0,0 +1,26 @@
1
+ #pragma once
2
+
3
+ #include <string>
4
+
5
+ #ifdef __OBJC__ // when compiled as Objective-C++
6
+ #import <AudioDecoder.h>
7
+ #else // when compiled as C++
8
+ typedef struct objc_object AudioDecoder;
9
+ #endif // __OBJC__
10
+
11
+ namespace audioapi {
12
+
13
+ class AudioBus;
14
+
15
+ class IOSAudioDecoder {
16
+ protected:
17
+ AudioDecoder *audioDecoder_;
18
+ int sampleRate_;
19
+
20
+ public:
21
+ IOSAudioDecoder(int sampleRate);
22
+ ~IOSAudioDecoder();
23
+
24
+ AudioBus *decodeWithFilePath(const std::string &path);
25
+ };
26
+ } // namespace audioapi
@@ -0,0 +1,40 @@
1
+ #import <AVFoundation/AVFoundation.h>
2
+
3
+ #include <AudioArray.h>
4
+ #include <AudioBus.h>
5
+ #include <IOSAudioDecoder.h>
6
+
7
+ namespace audioapi {
8
+
9
+ IOSAudioDecoder::IOSAudioDecoder(int sampleRate) : sampleRate_(sampleRate)
10
+ {
11
+ audioDecoder_ = [[AudioDecoder alloc] initWithSampleRate:sampleRate_];
12
+ }
13
+
14
+ IOSAudioDecoder::~IOSAudioDecoder()
15
+ {
16
+ [audioDecoder_ cleanup];
17
+ audioDecoder_ = nullptr;
18
+ }
19
+
20
+ AudioBus *IOSAudioDecoder::decodeWithFilePath(const std::string &path)
21
+ {
22
+ auto bufferList = [audioDecoder_ decode:[NSString stringWithUTF8String:path.c_str()]];
23
+ AudioBus *audioBus;
24
+ if (bufferList) {
25
+ auto numberOfChannels = bufferList->mNumberBuffers;
26
+ auto size = bufferList->mBuffers[0].mDataByteSize / sizeof(float);
27
+
28
+ audioBus = new AudioBus(sampleRate_, size, numberOfChannels);
29
+
30
+ for (int i = 0; i < numberOfChannels; i++) {
31
+ float *data = (float *)bufferList->mBuffers[i].mData;
32
+ memcpy(audioBus->getChannel(i)->getData(), data, sizeof(float) * size);
33
+ }
34
+ } else {
35
+ audioBus = new AudioBus(sampleRate_, 1, 1);
36
+ }
37
+
38
+ return audioBus;
39
+ }
40
+ } // namespace audioapi
@@ -3,7 +3,7 @@
3
3
  #import <AVFoundation/AVFoundation.h>
4
4
  #import <Foundation/Foundation.h>
5
5
 
6
- typedef void (^RenderAudioBlock)(AudioBufferList* outputBuffer, int numFrames);
6
+ typedef void (^RenderAudioBlock)(AudioBufferList *outputBuffer, int numFrames);
7
7
 
8
8
  @interface AudioPlayer : NSObject
9
9
 
@@ -70,7 +70,6 @@
70
70
  [self.audioEngine attachNode:self.sourceNode];
71
71
  [self.audioEngine connect:self.sourceNode to:self.audioEngine.mainMixerNode format:self.format];
72
72
 
73
-
74
73
  if (!self.audioEngine.isRunning) {
75
74
  NSError *error = nil;
76
75
  if (![self.audioEngine startAndReturnError:&error]) {