react-native-audio-api 0.6.0-rc.4 → 0.6.0

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 (206) hide show
  1. package/android/src/main/cpp/audioapi/android/AudioAPIModule.cpp +35 -2
  2. package/android/src/main/cpp/audioapi/android/AudioAPIModule.h +4 -0
  3. package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +31 -17
  4. package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.h +1 -3
  5. package/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt +56 -2
  6. package/android/src/main/java/com/swmansion/audioapi/AudioAPIPackage.kt +0 -12
  7. package/android/src/main/java/com/swmansion/audioapi/system/AudioFocusListener.kt +35 -12
  8. package/android/src/main/java/com/swmansion/audioapi/system/LockScreenManager.kt +30 -29
  9. package/android/src/main/java/com/swmansion/audioapi/system/MediaNotificationManager.kt +20 -22
  10. package/android/src/main/java/com/swmansion/audioapi/system/MediaReceiver.kt +19 -9
  11. package/android/src/main/java/com/swmansion/audioapi/system/MediaSessionCallback.kt +30 -11
  12. package/android/src/main/java/com/swmansion/audioapi/system/MediaSessionManager.kt +53 -26
  13. package/android/src/main/java/com/swmansion/audioapi/system/VolumeChangeListener.kt +10 -5
  14. package/android/src/oldarch/NativeAudioAPIModuleSpec.java +39 -0
  15. package/common/cpp/audioapi/AudioAPIModuleInstaller.h +20 -14
  16. package/common/cpp/audioapi/HostObjects/AudioBufferSourceNodeHostObject.h +2 -3
  17. package/common/cpp/audioapi/HostObjects/AudioNodeHostObject.h +24 -11
  18. package/common/cpp/audioapi/HostObjects/AudioParamHostObject.h +1 -0
  19. package/common/cpp/audioapi/HostObjects/AudioRecorderHostObject.h +10 -89
  20. package/common/cpp/audioapi/HostObjects/AudioScheduledSourceNodeHostObject.h +3 -24
  21. package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.h +2 -2
  22. package/common/cpp/audioapi/HostObjects/OscillatorNodeHostObject.h +2 -3
  23. package/common/cpp/audioapi/core/AudioContext.cpp +4 -1
  24. package/common/cpp/audioapi/core/AudioContext.h +1 -1
  25. package/common/cpp/audioapi/core/AudioNode.cpp +31 -2
  26. package/common/cpp/audioapi/core/AudioNode.h +7 -1
  27. package/common/cpp/audioapi/core/AudioParam.cpp +84 -2
  28. package/common/cpp/audioapi/core/AudioParam.h +14 -1
  29. package/common/cpp/audioapi/core/BaseAudioContext.cpp +7 -1
  30. package/common/cpp/audioapi/core/BaseAudioContext.h +7 -1
  31. package/common/cpp/audioapi/core/OfflineAudioContext.cpp +3 -2
  32. package/common/cpp/audioapi/core/OfflineAudioContext.h +1 -1
  33. package/common/cpp/audioapi/core/analysis/AnalyserNode.cpp +19 -56
  34. package/common/cpp/audioapi/core/analysis/AnalyserNode.h +3 -2
  35. package/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp +28 -26
  36. package/common/cpp/audioapi/core/effects/GainNode.cpp +9 -9
  37. package/common/cpp/audioapi/core/effects/StereoPannerNode.cpp +5 -2
  38. package/common/cpp/audioapi/core/inputs/AudioRecorder.cpp +56 -0
  39. package/common/cpp/audioapi/core/inputs/AudioRecorder.h +13 -13
  40. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +14 -10
  41. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h +1 -1
  42. package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +7 -6
  43. package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h +5 -3
  44. package/common/cpp/audioapi/core/sources/OscillatorNode.cpp +15 -10
  45. package/common/cpp/audioapi/core/utils/AudioNodeManager.cpp +35 -1
  46. package/common/cpp/audioapi/core/utils/AudioNodeManager.h +15 -1
  47. package/common/cpp/audioapi/events/AudioEventHandlerRegistry.cpp +108 -0
  48. package/common/cpp/audioapi/events/AudioEventHandlerRegistry.h +62 -0
  49. package/common/cpp/audioapi/events/AudioEventHandlerRegistryHostObject.h +48 -0
  50. package/common/cpp/audioapi/jsi/JsiPromise.cpp +3 -4
  51. package/common/cpp/audioapi/utils/AudioArray.h +1 -1
  52. package/common/cpp/audioapi/utils/CircularAudioArray.cpp +94 -0
  53. package/common/cpp/audioapi/utils/CircularAudioArray.h +26 -0
  54. package/ios/audioapi/ios/AudioAPIModule.h +13 -0
  55. package/ios/audioapi/ios/AudioAPIModule.mm +115 -6
  56. package/ios/audioapi/ios/core/IOSAudioRecorder.h +3 -10
  57. package/ios/audioapi/ios/core/IOSAudioRecorder.mm +15 -52
  58. package/ios/audioapi/ios/core/NativeAudioPlayer.m +2 -0
  59. package/ios/audioapi/ios/core/NativeAudioRecorder.m +7 -3
  60. package/ios/audioapi/ios/events/IOSAudioEventHandlerRegistry.h +7 -0
  61. package/ios/audioapi/ios/events/IOSAudioEventHandlerRegistry.mm +12 -0
  62. package/ios/audioapi/ios/system/AudioEngine.h +5 -0
  63. package/ios/audioapi/ios/system/AudioEngine.mm +7 -15
  64. package/ios/audioapi/ios/system/AudioSessionManager.h +3 -1
  65. package/ios/audioapi/ios/system/AudioSessionManager.mm +36 -17
  66. package/ios/audioapi/ios/system/LockScreenManager.h +3 -3
  67. package/ios/audioapi/ios/system/LockScreenManager.mm +36 -48
  68. package/ios/audioapi/ios/system/NotificationManager.h +3 -3
  69. package/ios/audioapi/ios/system/NotificationManager.mm +21 -29
  70. package/lib/commonjs/api.js +4 -4
  71. package/lib/commonjs/api.js.map +1 -1
  72. package/lib/commonjs/core/AudioBufferSourceNode.js +2 -2
  73. package/lib/commonjs/core/AudioBufferSourceNode.js.map +1 -1
  74. package/lib/commonjs/core/AudioNode.js +8 -2
  75. package/lib/commonjs/core/AudioNode.js.map +1 -1
  76. package/lib/commonjs/core/AudioParam.js +2 -1
  77. package/lib/commonjs/core/AudioParam.js.map +1 -1
  78. package/lib/commonjs/core/AudioRecorder.js +11 -28
  79. package/lib/commonjs/core/AudioRecorder.js.map +1 -1
  80. package/lib/commonjs/core/AudioScheduledSourceNode.js +4 -1
  81. package/lib/commonjs/core/AudioScheduledSourceNode.js.map +1 -1
  82. package/lib/commonjs/core/BiquadFilterNode.js +4 -4
  83. package/lib/commonjs/core/BiquadFilterNode.js.map +1 -1
  84. package/lib/commonjs/core/GainNode.js +1 -1
  85. package/lib/commonjs/core/GainNode.js.map +1 -1
  86. package/lib/commonjs/core/OscillatorNode.js +2 -2
  87. package/lib/commonjs/core/OscillatorNode.js.map +1 -1
  88. package/lib/commonjs/core/StereoPannerNode.js +1 -1
  89. package/lib/commonjs/core/StereoPannerNode.js.map +1 -1
  90. package/lib/commonjs/events/AudioEventEmitter.js +22 -0
  91. package/lib/commonjs/events/AudioEventEmitter.js.map +1 -0
  92. package/lib/commonjs/events/AudioEventSubscription.js +20 -0
  93. package/lib/commonjs/events/AudioEventSubscription.js.map +1 -0
  94. package/lib/commonjs/events/index.js +21 -0
  95. package/lib/commonjs/events/index.js.map +1 -0
  96. package/lib/commonjs/events/types.js +6 -0
  97. package/lib/commonjs/events/types.js.map +1 -0
  98. package/lib/commonjs/hooks/useSytemVolume.js +1 -1
  99. package/lib/commonjs/hooks/useSytemVolume.js.map +1 -1
  100. package/lib/commonjs/specs/NativeAudioAPIModule.js +3 -3
  101. package/lib/commonjs/specs/NativeAudioAPIModule.js.map +1 -1
  102. package/lib/commonjs/specs/index.js +2 -16
  103. package/lib/commonjs/specs/index.js.map +1 -1
  104. package/lib/commonjs/system/AudioManager.js +26 -26
  105. package/lib/commonjs/system/AudioManager.js.map +1 -1
  106. package/lib/module/api.js +2 -2
  107. package/lib/module/api.js.map +1 -1
  108. package/lib/module/core/AudioBufferSourceNode.js +2 -2
  109. package/lib/module/core/AudioBufferSourceNode.js.map +1 -1
  110. package/lib/module/core/AudioNode.js +7 -2
  111. package/lib/module/core/AudioNode.js.map +1 -1
  112. package/lib/module/core/AudioParam.js +2 -1
  113. package/lib/module/core/AudioParam.js.map +1 -1
  114. package/lib/module/core/AudioRecorder.js +11 -28
  115. package/lib/module/core/AudioRecorder.js.map +1 -1
  116. package/lib/module/core/AudioScheduledSourceNode.js +4 -1
  117. package/lib/module/core/AudioScheduledSourceNode.js.map +1 -1
  118. package/lib/module/core/BiquadFilterNode.js +4 -4
  119. package/lib/module/core/BiquadFilterNode.js.map +1 -1
  120. package/lib/module/core/GainNode.js +1 -1
  121. package/lib/module/core/GainNode.js.map +1 -1
  122. package/lib/module/core/OscillatorNode.js +2 -2
  123. package/lib/module/core/OscillatorNode.js.map +1 -1
  124. package/lib/module/core/StereoPannerNode.js +1 -1
  125. package/lib/module/core/StereoPannerNode.js.map +1 -1
  126. package/lib/module/events/AudioEventEmitter.js +16 -0
  127. package/lib/module/events/AudioEventEmitter.js.map +1 -0
  128. package/lib/module/events/AudioEventSubscription.js +15 -0
  129. package/lib/module/events/AudioEventSubscription.js.map +1 -0
  130. package/lib/module/events/index.js +6 -0
  131. package/lib/module/events/index.js.map +1 -0
  132. package/lib/module/events/types.js +4 -0
  133. package/lib/module/events/types.js.map +1 -0
  134. package/lib/module/hooks/useSytemVolume.js +1 -1
  135. package/lib/module/hooks/useSytemVolume.js.map +1 -1
  136. package/lib/module/specs/NativeAudioAPIModule.js +3 -2
  137. package/lib/module/specs/NativeAudioAPIModule.js.map +1 -1
  138. package/lib/module/specs/index.js +2 -3
  139. package/lib/module/specs/index.js.map +1 -1
  140. package/lib/module/system/AudioManager.js +27 -27
  141. package/lib/module/system/AudioManager.js.map +1 -1
  142. package/lib/typescript/api.d.ts +2 -1
  143. package/lib/typescript/api.d.ts.map +1 -1
  144. package/lib/typescript/core/AudioNode.d.ts +2 -1
  145. package/lib/typescript/core/AudioNode.d.ts.map +1 -1
  146. package/lib/typescript/core/AudioParam.d.ts +4 -2
  147. package/lib/typescript/core/AudioParam.d.ts.map +1 -1
  148. package/lib/typescript/core/AudioRecorder.d.ts +4 -14
  149. package/lib/typescript/core/AudioRecorder.d.ts.map +1 -1
  150. package/lib/typescript/core/AudioScheduledSourceNode.d.ts +3 -1
  151. package/lib/typescript/core/AudioScheduledSourceNode.d.ts.map +1 -1
  152. package/lib/typescript/events/AudioEventEmitter.d.ts +10 -0
  153. package/lib/typescript/events/AudioEventEmitter.d.ts.map +1 -0
  154. package/lib/typescript/events/AudioEventSubscription.d.ts +11 -0
  155. package/lib/typescript/events/AudioEventSubscription.d.ts.map +1 -0
  156. package/lib/typescript/events/index.d.ts +4 -0
  157. package/lib/typescript/events/index.d.ts.map +1 -0
  158. package/lib/typescript/events/types.d.ts +50 -0
  159. package/lib/typescript/events/types.d.ts.map +1 -0
  160. package/lib/typescript/hooks/useSytemVolume.d.ts.map +1 -1
  161. package/lib/typescript/interfaces.d.ts +10 -10
  162. package/lib/typescript/interfaces.d.ts.map +1 -1
  163. package/lib/typescript/specs/NativeAudioAPIModule.d.ts +15 -3
  164. package/lib/typescript/specs/NativeAudioAPIModule.d.ts.map +1 -1
  165. package/lib/typescript/specs/index.d.ts +2 -3
  166. package/lib/typescript/specs/index.d.ts.map +1 -1
  167. package/lib/typescript/system/AudioManager.d.ts +8 -4
  168. package/lib/typescript/system/AudioManager.d.ts.map +1 -1
  169. package/lib/typescript/system/types.d.ts +1 -34
  170. package/lib/typescript/system/types.d.ts.map +1 -1
  171. package/lib/typescript/types.d.ts +0 -1
  172. package/lib/typescript/types.d.ts.map +1 -1
  173. package/package.json +3 -3
  174. package/src/api.ts +6 -3
  175. package/src/core/AudioBufferSourceNode.ts +2 -2
  176. package/src/core/AudioNode.ts +8 -3
  177. package/src/core/AudioParam.ts +5 -2
  178. package/src/core/AudioRecorder.ts +22 -62
  179. package/src/core/AudioScheduledSourceNode.ts +13 -2
  180. package/src/core/BiquadFilterNode.ts +4 -4
  181. package/src/core/GainNode.ts +1 -1
  182. package/src/core/OscillatorNode.ts +2 -2
  183. package/src/core/StereoPannerNode.ts +1 -1
  184. package/src/events/AudioEventEmitter.ts +29 -0
  185. package/src/events/AudioEventSubscription.ts +26 -0
  186. package/src/events/index.ts +4 -0
  187. package/src/events/types.ts +64 -0
  188. package/src/hooks/useSytemVolume.ts +2 -1
  189. package/src/interfaces.ts +19 -20
  190. package/src/specs/NativeAudioAPIModule.ts +23 -2
  191. package/src/specs/index.ts +2 -4
  192. package/src/system/AudioManager.ts +39 -38
  193. package/src/system/types.ts +1 -41
  194. package/src/types.ts +0 -8
  195. package/android/src/main/java/com/swmansion/audioapi/AudioManagerModule.kt +0 -64
  196. package/android/src/main/java/com/swmansion/audioapi/system/MediaSessionEventEmitter.kt +0 -88
  197. package/android/src/oldarch/NativeAudioManagerModuleSpec.java +0 -99
  198. package/ios/audioapi/ios/AudioManagerModule.h +0 -18
  199. package/ios/audioapi/ios/AudioManagerModule.mm +0 -93
  200. package/lib/commonjs/specs/NativeAudioManagerModule.js +0 -36
  201. package/lib/commonjs/specs/NativeAudioManagerModule.js.map +0 -1
  202. package/lib/module/specs/NativeAudioManagerModule.js +0 -33
  203. package/lib/module/specs/NativeAudioManagerModule.js.map +0 -1
  204. package/lib/typescript/specs/NativeAudioManagerModule.d.ts +0 -15
  205. package/lib/typescript/specs/NativeAudioManagerModule.d.ts.map +0 -1
  206. package/src/specs/NativeAudioManagerModule.ts +0 -45
@@ -10,7 +10,10 @@ AudioAPIModule::AudioAPIModule(
10
10
  const std::shared_ptr<facebook::react::CallInvoker> &jsCallInvoker)
11
11
  : javaPart_(make_global(jThis)),
12
12
  jsiRuntime_(jsiRuntime),
13
- jsCallInvoker_(jsCallInvoker) {}
13
+ jsCallInvoker_(jsCallInvoker) {
14
+ audioEventHandlerRegistry_ =
15
+ std::make_shared<AudioEventHandlerRegistry>(jsiRuntime, jsCallInvoker);
16
+ }
14
17
 
15
18
  jni::local_ref<AudioAPIModule::jhybriddata> AudioAPIModule::initHybrid(
16
19
  jni::alias_ref<jhybridobject> jThis,
@@ -26,10 +29,40 @@ void AudioAPIModule::registerNatives() {
26
29
  registerHybrid({
27
30
  makeNativeMethod("initHybrid", AudioAPIModule::initHybrid),
28
31
  makeNativeMethod("injectJSIBindings", AudioAPIModule::injectJSIBindings),
32
+ makeNativeMethod(
33
+ "invokeHandlerWithEventNameAndEventBody",
34
+ AudioAPIModule::invokeHandlerWithEventNameAndEventBody),
29
35
  });
30
36
  }
31
37
 
32
38
  void AudioAPIModule::injectJSIBindings() {
33
- AudioAPIModuleInstaller::injectJSIBindings(jsiRuntime_, jsCallInvoker_);
39
+ AudioAPIModuleInstaller::injectJSIBindings(
40
+ jsiRuntime_, jsCallInvoker_, audioEventHandlerRegistry_);
41
+ }
42
+
43
+ void AudioAPIModule::invokeHandlerWithEventNameAndEventBody(
44
+ jni::alias_ref<jni::JString> eventName,
45
+ jni::alias_ref<jni::JMap<jstring, jobject>> eventBody) {
46
+ std::unordered_map<std::string, EventValue> body = {};
47
+
48
+ for (const auto &entry : *eventBody) {
49
+ std::string name = entry.first->toStdString();
50
+ auto value = entry.second;
51
+
52
+ if (value->isInstanceOf(jni::JString::javaClassStatic())) {
53
+ body[name] = jni::static_ref_cast<jni::JString>(value)->toStdString();
54
+ } else if (value->isInstanceOf(jni::JInteger::javaClassStatic())) {
55
+ body[name] = jni::static_ref_cast<jni::JInteger>(value)->value();
56
+ } else if (value->isInstanceOf(jni::JDouble::javaClassStatic())) {
57
+ body[name] = jni::static_ref_cast<jni::JDouble>(value)->value();
58
+ } else if (value->isInstanceOf(jni::JFloat::javaClassStatic())) {
59
+ body[name] = jni::static_ref_cast<jni::JFloat>(value)->value();
60
+ } else if (value->isInstanceOf(jni::JBoolean::javaClassStatic())) {
61
+ body[name] = jni::static_ref_cast<jni::JBoolean>(value)->value();
62
+ }
63
+ }
64
+
65
+ audioEventHandlerRegistry_->invokeHandlerWithEventBody(
66
+ eventName->toStdString(), body);
34
67
  }
35
68
  } // namespace audioapi
@@ -1,6 +1,7 @@
1
1
  #pragma once
2
2
 
3
3
  #include <audioapi/AudioAPIModuleInstaller.h>
4
+ #include <audioapi/events/AudioEventHandlerRegistry.h>
4
5
 
5
6
  #include <ReactCommon/CallInvokerHolder.h>
6
7
  #include <fbjni/fbjni.h>
@@ -8,6 +9,7 @@
8
9
  #include <react/jni/JMessageQueueThread.h>
9
10
  #include <memory>
10
11
  #include <utility>
12
+ #include <unordered_map>
11
13
 
12
14
  namespace audioapi {
13
15
 
@@ -28,6 +30,7 @@ class AudioAPIModule : public jni::HybridClass<AudioAPIModule> {
28
30
  static void registerNatives();
29
31
 
30
32
  void injectJSIBindings();
33
+ void invokeHandlerWithEventNameAndEventBody(jni::alias_ref<jni::JString> eventName, jni::alias_ref<jni::JMap<jstring, jobject>> eventBody);
31
34
 
32
35
  private:
33
36
  friend HybridBase;
@@ -35,6 +38,7 @@ class AudioAPIModule : public jni::HybridClass<AudioAPIModule> {
35
38
  jni::global_ref<AudioAPIModule::javaobject> javaPart_;
36
39
  jsi::Runtime *jsiRuntime_;
37
40
  std::shared_ptr<facebook::react::CallInvoker> jsCallInvoker_;
41
+ std::shared_ptr<AudioEventHandlerRegistry> audioEventHandlerRegistry_;
38
42
 
39
43
  explicit AudioAPIModule(
40
44
  jni::alias_ref<AudioAPIModule::jhybridobject> &jThis,
@@ -1,23 +1,17 @@
1
1
  #include <audioapi/android/core/AndroidAudioRecorder.h>
2
2
  #include <audioapi/core/Constants.h>
3
+ #include <audioapi/events/AudioEventHandlerRegistry.h>
3
4
  #include <audioapi/utils/AudioArray.h>
4
5
  #include <audioapi/utils/AudioBus.h>
6
+ #include <audioapi/utils/CircularAudioArray.h>
5
7
 
6
8
  namespace audioapi {
7
9
 
8
10
  AndroidAudioRecorder::AndroidAudioRecorder(
9
11
  float sampleRate,
10
12
  int bufferLength,
11
- const std::function<void(void)> &onError,
12
- const std::function<void(void)> &onStatusChange,
13
- const std::function<void(std::shared_ptr<AudioBus>, int, double)>
14
- &onAudioReady)
15
- : AudioRecorder(
16
- sampleRate,
17
- bufferLength,
18
- onError,
19
- onStatusChange,
20
- onAudioReady) {
13
+ const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry)
14
+ : AudioRecorder(sampleRate, bufferLength, audioEventHandlerRegistry) {
21
15
  AudioStreamBuilder builder;
22
16
  builder.setSharingMode(SharingMode::Exclusive)
23
17
  ->setDirection(Direction::Input)
@@ -28,7 +22,6 @@ AndroidAudioRecorder::AndroidAudioRecorder(
28
22
  ->setSampleRateConversionQuality(SampleRateConversionQuality::Medium)
29
23
  ->setDataCallback(this)
30
24
  ->setSampleRate(static_cast<int>(sampleRate))
31
- ->setFramesPerDataCallback(bufferLength)
32
25
  ->openStream(mStream_);
33
26
  }
34
27
 
@@ -43,29 +36,50 @@ AndroidAudioRecorder::~AndroidAudioRecorder() {
43
36
  }
44
37
 
45
38
  void AndroidAudioRecorder::start() {
39
+ if (isRunning_.load()) {
40
+ return;
41
+ }
42
+
46
43
  if (mStream_) {
47
44
  mStream_->requestStart();
48
45
  }
46
+
47
+ isRunning_.store(true);
49
48
  }
50
49
 
51
50
  void AndroidAudioRecorder::stop() {
51
+ if (!isRunning_.load()) {
52
+ return;
53
+ }
54
+
55
+ isRunning_.store(false);
56
+
52
57
  if (mStream_) {
53
58
  mStream_->requestStop();
54
59
  }
60
+
61
+ sendRemainingData();
55
62
  }
56
63
 
57
64
  DataCallbackResult AndroidAudioRecorder::onAudioReady(
58
65
  oboe::AudioStream *oboeStream,
59
66
  void *audioData,
60
67
  int32_t numFrames) {
61
- auto buffer = static_cast<float *>(audioData);
68
+ if (isRunning_.load()) {
69
+ auto *inputChannel = static_cast<float *>(audioData);
70
+ circularBuffer_->push_back(inputChannel, numFrames);
71
+ }
62
72
 
63
- auto bus = std::make_shared<AudioBus>(bufferLength_, 1, sampleRate_);
64
- memcpy(bus->getChannel(0)->getData(), buffer, numFrames * sizeof(float));
65
- auto when = static_cast<double>(
66
- oboeStream->getTimestamp(CLOCK_MONOTONIC).value().timestamp);
73
+ while (circularBuffer_->getNumberOfAvailableFrames() >= bufferLength_) {
74
+ auto bus = std::make_shared<AudioBus>(bufferLength_, 1, sampleRate_);
75
+ auto *outputChannel = bus->getChannel(0)->getData();
67
76
 
68
- onAudioReady_(bus, numFrames, when);
77
+ circularBuffer_->pop_front(outputChannel, bufferLength_);
78
+ auto when = static_cast<double>(
79
+ oboeStream->getTimestamp(CLOCK_MONOTONIC).value().timestamp);
80
+
81
+ invokeOnAudioReadyCallback(bus, bufferLength_, when);
82
+ }
69
83
 
70
84
  return DataCallbackResult::Continue;
71
85
  }
@@ -16,9 +16,7 @@ class AndroidAudioRecorder : public AudioStreamDataCallback, public AudioRecorde
16
16
  public:
17
17
  AndroidAudioRecorder(float sampleRate,
18
18
  int bufferLength,
19
- const std::function<void(void)> &onError,
20
- const std::function<void(void)> &onStatusChange,
21
- const std::function<void(std::shared_ptr<AudioBus>, int, double)> &onAudioReady);
19
+ const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry);
22
20
 
23
21
  ~AndroidAudioRecorder() override;
24
22
 
@@ -1,11 +1,15 @@
1
1
  package com.swmansion.audioapi
2
2
 
3
3
  import com.facebook.jni.HybridData
4
+ import com.facebook.react.bridge.Promise
4
5
  import com.facebook.react.bridge.ReactApplicationContext
5
- import com.facebook.react.bridge.ReactMethod
6
+ import com.facebook.react.bridge.ReadableArray
7
+ import com.facebook.react.bridge.ReadableMap
6
8
  import com.facebook.react.common.annotations.FrameworkAPI
7
9
  import com.facebook.react.module.annotations.ReactModule
8
10
  import com.facebook.react.turbomodule.core.CallInvokerHolderImpl
11
+ import com.swmansion.audioapi.system.MediaSessionManager
12
+ import java.lang.ref.WeakReference
9
13
 
10
14
  @OptIn(FrameworkAPI::class)
11
15
  @ReactModule(name = AudioAPIModule.NAME)
@@ -16,6 +20,8 @@ class AudioAPIModule(
16
20
  const val NAME = NativeAudioAPIModuleSpec.NAME
17
21
  }
18
22
 
23
+ val reactContext: WeakReference<ReactApplicationContext> = WeakReference(reactContext)
24
+
19
25
  private val mHybridData: HybridData
20
26
 
21
27
  external fun initHybrid(
@@ -25,6 +31,11 @@ class AudioAPIModule(
25
31
 
26
32
  private external fun injectJSIBindings()
27
33
 
34
+ external fun invokeHandlerWithEventNameAndEventBody(
35
+ eventName: String,
36
+ eventBody: Map<String, Any>,
37
+ )
38
+
28
39
  init {
29
40
  try {
30
41
  System.loadLibrary("react-native-audio-api")
@@ -35,10 +46,53 @@ class AudioAPIModule(
35
46
  }
36
47
  }
37
48
 
38
- @ReactMethod(isBlockingSynchronousMethod = true)
39
49
  override fun install(): Boolean {
50
+ MediaSessionManager.initialize(WeakReference(this), reactContext)
40
51
  injectJSIBindings()
41
52
 
42
53
  return true
43
54
  }
55
+
56
+ override fun setLockScreenInfo(info: ReadableMap?) {
57
+ MediaSessionManager.setLockScreenInfo(info)
58
+ }
59
+
60
+ override fun resetLockScreenInfo() {
61
+ MediaSessionManager.resetLockScreenInfo()
62
+ }
63
+
64
+ override fun enableRemoteCommand(
65
+ name: String?,
66
+ enabled: Boolean,
67
+ ) {
68
+ MediaSessionManager.enableRemoteCommand(name!!, enabled)
69
+ }
70
+
71
+ override fun setAudioSessionOptions(
72
+ category: String?,
73
+ mode: String?,
74
+ options: ReadableArray?,
75
+ ) {
76
+ // noting to do here
77
+ }
78
+
79
+ override fun getDevicePreferredSampleRate(): Double = MediaSessionManager.getDevicePreferredSampleRate()
80
+
81
+ override fun observeAudioInterruptions(enabled: Boolean) {
82
+ MediaSessionManager.observeAudioInterruptions(enabled)
83
+ }
84
+
85
+ override fun observeVolumeChanges(enabled: Boolean) {
86
+ MediaSessionManager.observeVolumeChanges(enabled)
87
+ }
88
+
89
+ override fun requestRecordingPermissions(promise: Promise?) {
90
+ val res = MediaSessionManager.requestRecordingPermissions(currentActivity)
91
+ promise!!.resolve(res)
92
+ }
93
+
94
+ override fun checkRecordingPermissions(promise: Promise?) {
95
+ val res = MediaSessionManager.checkRecordingPermissions()
96
+ promise!!.resolve(res)
97
+ }
44
98
  }
@@ -10,7 +10,6 @@ import com.facebook.react.module.model.ReactModuleInfoProvider
10
10
  @ReactModuleList(
11
11
  nativeModules = [
12
12
  AudioAPIModule::class,
13
- AudioManagerModule::class,
14
13
  ],
15
14
  )
16
15
  class AudioAPIPackage : BaseReactPackage() {
@@ -20,7 +19,6 @@ class AudioAPIPackage : BaseReactPackage() {
20
19
  ): NativeModule? {
21
20
  when (name) {
22
21
  AudioAPIModule.NAME -> return AudioAPIModule(reactContext)
23
- AudioManagerModule.NAME -> return AudioManagerModule(reactContext)
24
22
  }
25
23
  return null
26
24
  }
@@ -40,16 +38,6 @@ class AudioAPIPackage : BaseReactPackage() {
40
38
  isTurboModule = isTurboModule,
41
39
  )
42
40
 
43
- moduleInfos[AudioManagerModule.NAME] =
44
- ReactModuleInfo(
45
- AudioManagerModule.NAME,
46
- AudioManagerModule.NAME,
47
- canOverrideExistingModule = true,
48
- needsEagerInit = false,
49
- hasConstants = true,
50
- isCxxModule = false,
51
- isTurboModule = false,
52
- )
53
41
  moduleInfos
54
42
  }
55
43
  }
@@ -4,11 +4,14 @@ import android.media.AudioFocusRequest
4
4
  import android.media.AudioManager
5
5
  import android.os.Build
6
6
  import android.util.Log
7
+ import com.swmansion.audioapi.AudioAPIModule
8
+ import java.lang.ref.WeakReference
9
+ import java.util.HashMap
7
10
 
8
11
  class AudioFocusListener(
9
- private val audioManager: AudioManager,
10
- val eventEmitter: MediaSessionEventEmitter,
11
- private val lockScreenManager: LockScreenManager,
12
+ private val audioManager: WeakReference<AudioManager>,
13
+ private val audioAPIModule: WeakReference<AudioAPIModule>,
14
+ private val lockScreenManager: WeakReference<LockScreenManager>,
12
15
  ) : AudioManager.OnAudioFocusChangeListener {
13
16
  private var playOnAudioFocus = false
14
17
  private var focusRequest: AudioFocusRequest? = null
@@ -18,17 +21,37 @@ class AudioFocusListener(
18
21
  when (focusChange) {
19
22
  AudioManager.AUDIOFOCUS_LOSS -> {
20
23
  playOnAudioFocus = false
21
- eventEmitter.onInterruption(mapOf("type" to "began", "shouldResume" to false))
24
+ val body =
25
+ HashMap<String, Any>().apply {
26
+ put("value", "began")
27
+ put("shouldResume", false)
28
+ }
29
+ audioAPIModule.get()?.invokeHandlerWithEventNameAndEventBody("interruption", body)
22
30
  }
23
31
  AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
24
- playOnAudioFocus = lockScreenManager.isPlaying
25
- eventEmitter.onInterruption(mapOf("type" to "began", "shouldResume" to playOnAudioFocus))
32
+ playOnAudioFocus = lockScreenManager.get()?.isPlaying == true
33
+ val body =
34
+ HashMap<String, Any>().apply {
35
+ put("value", "began")
36
+ put("shouldResume", playOnAudioFocus)
37
+ }
38
+ audioAPIModule.get()?.invokeHandlerWithEventNameAndEventBody("interruption", body)
26
39
  }
27
40
  AudioManager.AUDIOFOCUS_GAIN -> {
28
41
  if (playOnAudioFocus) {
29
- eventEmitter.onInterruption(mapOf("type" to "ended", "shouldResume" to true))
42
+ val body =
43
+ HashMap<String, Any>().apply {
44
+ put("value", "ended")
45
+ put("shouldResume", true)
46
+ }
47
+ audioAPIModule.get()?.invokeHandlerWithEventNameAndEventBody("interruption", body)
30
48
  } else {
31
- eventEmitter.onInterruption(mapOf("type" to "ended", "shouldResume" to false))
49
+ val body =
50
+ HashMap<String, Any>().apply {
51
+ put("value", "ended")
52
+ put("shouldResume", false)
53
+ }
54
+ audioAPIModule.get()?.invokeHandlerWithEventNameAndEventBody("interruption", body)
32
55
  }
33
56
 
34
57
  playOnAudioFocus = false
@@ -44,17 +67,17 @@ class AudioFocusListener(
44
67
  .setOnAudioFocusChangeListener(this)
45
68
  .build()
46
69
 
47
- audioManager.requestAudioFocus(focusRequest!!)
70
+ audioManager.get()?.requestAudioFocus(focusRequest!!)
48
71
  } else {
49
- audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN)
72
+ audioManager.get()?.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN)
50
73
  }
51
74
  }
52
75
 
53
76
  fun abandonAudioFocus() {
54
77
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && this.focusRequest != null) {
55
- audioManager.abandonAudioFocusRequest(focusRequest!!)
78
+ audioManager.get()?.abandonAudioFocusRequest(focusRequest!!)
56
79
  } else {
57
- audioManager.abandonAudioFocus(this)
80
+ audioManager.get()?.abandonAudioFocus(this)
58
81
  }
59
82
  }
60
83
  }
@@ -14,20 +14,20 @@ import com.facebook.react.bridge.ReadableMap
14
14
  import com.facebook.react.bridge.ReadableType
15
15
  import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper.Companion.instance
16
16
  import java.io.IOException
17
+ import java.lang.ref.WeakReference
17
18
  import java.net.URL
18
19
 
19
20
  class LockScreenManager(
20
- private val reactContext: ReactApplicationContext,
21
- private val mediaSession: MediaSessionCompat,
22
- private val mediaNotificationManager: MediaNotificationManager,
23
- val channelId: String,
21
+ private val reactContext: WeakReference<ReactApplicationContext>,
22
+ private val mediaSession: WeakReference<MediaSessionCompat>,
23
+ private val mediaNotificationManager: WeakReference<MediaNotificationManager>,
24
24
  ) {
25
25
  private var pb: PlaybackStateCompat.Builder = PlaybackStateCompat.Builder()
26
26
  private var state: PlaybackStateCompat = pb.build()
27
27
  private var controls: Long = 0
28
28
  var isPlaying: Boolean = false
29
29
 
30
- private var nb: NotificationCompat.Builder = NotificationCompat.Builder(reactContext, channelId)
30
+ private var nb: NotificationCompat.Builder = NotificationCompat.Builder(reactContext.get()!!, MediaSessionManager.CHANNEL_ID)
31
31
 
32
32
  private var artworkThread: Thread? = null
33
33
 
@@ -49,7 +49,7 @@ class LockScreenManager(
49
49
 
50
50
  updateNotificationMediaStyle()
51
51
 
52
- mediaNotificationManager.updateActions(controls)
52
+ mediaNotificationManager.get()?.updateActions(controls)
53
53
  }
54
54
 
55
55
  fun setLockScreenInfo(info: ReadableMap?) {
@@ -112,17 +112,17 @@ class LockScreenManager(
112
112
  try {
113
113
  val bitmap: Bitmap? = artwork?.let { loadArtwork(it, artworkLocal) }
114
114
 
115
- val currentMetadata: MediaMetadataCompat = mediaSession.controller.metadata
115
+ val currentMetadata = mediaSession.get()?.controller?.metadata
116
116
  val newBuilder =
117
117
  MediaMetadataCompat.Builder(
118
118
  currentMetadata,
119
119
  )
120
- mediaSession.setMetadata(
120
+ mediaSession.get()?.setMetadata(
121
121
  newBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, bitmap).build(),
122
122
  )
123
123
 
124
124
  nb.setLargeIcon(bitmap)
125
- mediaNotificationManager.show(nb, isPlaying)
125
+ mediaNotificationManager.get()?.show(nb, isPlaying)
126
126
 
127
127
  artworkThread = null
128
128
  } catch (ex: Exception) {
@@ -164,17 +164,17 @@ class LockScreenManager(
164
164
 
165
165
  updatePlaybackState(this.playbackState)
166
166
 
167
- mediaSession.setMetadata(md.build())
168
- mediaSession.setActive(true)
169
- mediaNotificationManager.show(nb, isPlaying)
167
+ mediaSession.get()?.setMetadata(md.build())
168
+ mediaSession.get()?.setActive(true)
169
+ mediaNotificationManager.get()?.show(nb, isPlaying)
170
170
  }
171
171
 
172
172
  fun resetLockScreenInfo() {
173
173
  if (artworkThread != null && artworkThread!!.isAlive) artworkThread!!.interrupt()
174
174
  artworkThread = null
175
175
 
176
- mediaNotificationManager.hide()
177
- mediaSession.setActive(false)
176
+ mediaNotificationManager.get()?.hide()
177
+ mediaSession.get()?.setActive(false)
178
178
  }
179
179
 
180
180
  fun enableRemoteCommand(
@@ -183,14 +183,15 @@ class LockScreenManager(
183
183
  ) {
184
184
  var controlValue = 0L
185
185
  when (name) {
186
- "play" -> controlValue = PlaybackStateCompat.ACTION_PLAY
187
- "pause" -> controlValue = PlaybackStateCompat.ACTION_PAUSE
188
- "stop" -> controlValue = PlaybackStateCompat.ACTION_STOP
189
- "togglePlayPause" -> controlValue = PlaybackStateCompat.ACTION_PLAY_PAUSE
190
- "nextTrack" -> controlValue = PlaybackStateCompat.ACTION_SKIP_TO_NEXT
191
- "previousTrack" -> controlValue = PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
192
- "skipForward" -> controlValue = PlaybackStateCompat.ACTION_REWIND
193
- "skipBackward" -> controlValue = PlaybackStateCompat.ACTION_REWIND
186
+ "remotePlay" -> controlValue = PlaybackStateCompat.ACTION_PLAY
187
+ "remotePause" -> controlValue = PlaybackStateCompat.ACTION_PAUSE
188
+ "remoteStop" -> controlValue = PlaybackStateCompat.ACTION_STOP
189
+ "remoteTogglePlayPause" -> controlValue = PlaybackStateCompat.ACTION_PLAY_PAUSE
190
+ "remoteNextTrack" -> controlValue = PlaybackStateCompat.ACTION_SKIP_TO_NEXT
191
+ "remotePreviousTrack" -> controlValue = PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
192
+ "remoteSkipForward" -> controlValue = PlaybackStateCompat.ACTION_FAST_FORWARD
193
+ "remoteSkipBackward" -> controlValue = PlaybackStateCompat.ACTION_REWIND
194
+ "remoteChangePlaybackPosition" -> controlValue = PlaybackStateCompat.ACTION_SEEK_TO
194
195
  }
195
196
 
196
197
  controls =
@@ -200,16 +201,16 @@ class LockScreenManager(
200
201
  controls and controlValue.inv()
201
202
  }
202
203
 
203
- mediaNotificationManager.updateActions(controls)
204
+ mediaNotificationManager.get()?.updateActions(controls)
204
205
  pb.setActions(controls)
205
206
 
206
207
  state = pb.build()
207
- mediaSession.setPlaybackState(state)
208
+ mediaSession.get()?.setPlaybackState(state)
208
209
 
209
210
  updateNotificationMediaStyle()
210
211
 
211
- if (mediaSession.isActive) {
212
- mediaNotificationManager.show(nb, isPlaying)
212
+ if (mediaSession.get()?.isActive == true) {
213
+ mediaNotificationManager.get()?.show(nb, isPlaying)
213
214
  }
214
215
  }
215
216
 
@@ -224,7 +225,7 @@ class LockScreenManager(
224
225
  if (local && !url.startsWith("http")) {
225
226
  // Gets the drawable from the RN's helper for local resources
226
227
  val helper = instance
227
- val image = helper.getResourceDrawable(reactContext, url)
228
+ val image = helper.getResourceDrawable(reactContext.get()!!, url)
228
229
 
229
230
  bitmap =
230
231
  if (image is BitmapDrawable) {
@@ -255,14 +256,14 @@ class LockScreenManager(
255
256
  pb.setState(playbackState, elapsedTime, speed)
256
257
  pb.setActions(controls)
257
258
  state = pb.build()
258
- mediaSession.setPlaybackState(state)
259
+ mediaSession.get()?.setPlaybackState(state)
259
260
  }
260
261
 
261
262
  private fun hasControl(control: Long): Boolean = (controls and control) == control
262
263
 
263
264
  private fun updateNotificationMediaStyle() {
264
265
  val style = MediaStyle()
265
- style.setMediaSession(mediaSession.sessionToken)
266
+ style.setMediaSession(mediaSession.get()?.sessionToken)
266
267
  var controlCount = 0
267
268
  if (hasControl(PlaybackStateCompat.ACTION_PLAY) ||
268
269
  hasControl(PlaybackStateCompat.ACTION_PAUSE) ||
@@ -21,9 +21,7 @@ import com.swmansion.audioapi.R
21
21
  import java.lang.ref.WeakReference
22
22
 
23
23
  class MediaNotificationManager(
24
- val reactContext: ReactApplicationContext,
25
- val notificationId: Int,
26
- val channelId: String,
24
+ private val reactContext: WeakReference<ReactApplicationContext>,
27
25
  ) {
28
26
  private var smallIcon: Int = R.drawable.play
29
27
  private var customIcon: Int = 0
@@ -80,12 +78,12 @@ class MediaNotificationManager(
80
78
 
81
79
  builder.setSmallIcon(if (customIcon != 0) customIcon else smallIcon)
82
80
 
83
- val packageName: String = reactContext.packageName
84
- val openApp: Intent? = reactContext.packageManager.getLaunchIntentForPackage(packageName)
81
+ val packageName: String? = reactContext.get()?.packageName
82
+ val openApp: Intent? = reactContext.get()?.packageManager?.getLaunchIntentForPackage(packageName!!)
85
83
  try {
86
84
  builder.setContentIntent(
87
85
  PendingIntent.getActivity(
88
- reactContext,
86
+ reactContext.get(),
89
87
  0,
90
88
  openApp,
91
89
  PendingIntent.FLAG_IMMUTABLE,
@@ -96,10 +94,10 @@ class MediaNotificationManager(
96
94
  }
97
95
 
98
96
  val remove = Intent(REMOVE_NOTIFICATION)
99
- remove.putExtra(PACKAGE_NAME, reactContext.applicationInfo.packageName)
97
+ remove.putExtra(PACKAGE_NAME, reactContext.get()?.applicationInfo?.packageName)
100
98
  builder.setDeleteIntent(
101
99
  PendingIntent.getBroadcast(
102
- reactContext,
100
+ reactContext.get(),
103
101
  0,
104
102
  remove,
105
103
  PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
@@ -115,8 +113,8 @@ class MediaNotificationManager(
115
113
  builder: NotificationCompat.Builder?,
116
114
  isPlaying: Boolean,
117
115
  ) {
118
- NotificationManagerCompat.from(reactContext).notify(
119
- notificationId,
116
+ NotificationManagerCompat.from(reactContext.get()!!).notify(
117
+ MediaSessionManager.NOTIFICATION_ID,
120
118
  prepareNotification(
121
119
  builder!!,
122
120
  isPlaying,
@@ -125,15 +123,15 @@ class MediaNotificationManager(
125
123
  }
126
124
 
127
125
  fun hide() {
128
- NotificationManagerCompat.from(reactContext).cancel(notificationId)
126
+ NotificationManagerCompat.from(reactContext.get()!!).cancel(MediaSessionManager.NOTIFICATION_ID)
129
127
 
130
128
  try {
131
129
  val myIntent =
132
130
  Intent(
133
- reactContext,
131
+ reactContext.get(),
134
132
  NotificationService::class.java,
135
133
  )
136
- reactContext.stopService(myIntent)
134
+ reactContext.get()?.stopService(myIntent)
137
135
  } catch (e: java.lang.Exception) {
138
136
  Log.w("AudioManagerModule", "Error stopping service: ${e.message}")
139
137
  }
@@ -188,9 +186,9 @@ class MediaNotificationManager(
188
186
  return oldAction
189
187
  }
190
188
 
191
- val r: Resources = reactContext.resources
192
- val packageName: String = reactContext.packageName
193
- val icon = r.getIdentifier(iconName, "drawable", packageName)
189
+ val r: Resources? = reactContext.get()?.resources
190
+ val packageName: String? = reactContext.get()?.packageName
191
+ val icon = r?.getIdentifier(iconName, "drawable", packageName)
194
192
 
195
193
  val keyCode = PlaybackStateCompat.toKeyCode(action)
196
194
  val intent = Intent(MEDIA_BUTTON)
@@ -198,13 +196,13 @@ class MediaNotificationManager(
198
196
  intent.putExtra(ContactsContract.Directory.PACKAGE_NAME, packageName)
199
197
  val i =
200
198
  PendingIntent.getBroadcast(
201
- reactContext,
199
+ reactContext.get(),
202
200
  keyCode,
203
201
  intent,
204
202
  PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
205
203
  )
206
204
 
207
- return NotificationCompat.Action(icon, title, i)
205
+ return NotificationCompat.Action(icon!!, title, i)
208
206
  }
209
207
 
210
208
  class NotificationService : Service() {
@@ -232,8 +230,8 @@ class MediaNotificationManager(
232
230
  ContextCompat.startForegroundService(this, intent)
233
231
  notification =
234
232
  MediaSessionManager.mediaNotificationManager
235
- .prepareNotification(NotificationCompat.Builder(this, MediaSessionManager.channelId), false)
236
- startForeground(MediaSessionManager.notificationId, notification)
233
+ .prepareNotification(NotificationCompat.Builder(this, MediaSessionManager.CHANNEL_ID), false)
234
+ startForeground(MediaSessionManager.NOTIFICATION_ID, notification)
237
235
  }
238
236
  }
239
237
 
@@ -242,8 +240,8 @@ class MediaNotificationManager(
242
240
  try {
243
241
  notification =
244
242
  MediaSessionManager.mediaNotificationManager
245
- .prepareNotification(NotificationCompat.Builder(this, MediaSessionManager.channelId), false)
246
- startForeground(MediaSessionManager.notificationId, notification)
243
+ .prepareNotification(NotificationCompat.Builder(this, MediaSessionManager.CHANNEL_ID), false)
244
+ startForeground(MediaSessionManager.NOTIFICATION_ID, notification)
247
245
  } catch (ex: Exception) {
248
246
  Log.w("AudioManagerModule", "Error starting service: ${ex.message}")
249
247
  }