react-native-audio-api 0.6.0-rc.1 → 0.6.0-rc.3
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.
- package/README.md +35 -22
- package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +73 -0
- package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.h +37 -0
- package/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +6 -10
- package/android/src/main/cpp/audioapi/android/core/AudioPlayer.h +2 -3
- package/android/src/main/java/com/swmansion/audioapi/AudioManagerModule.kt +10 -8
- package/android/src/main/java/com/swmansion/audioapi/system/MediaNotificationManager.kt +7 -7
- package/android/src/main/java/com/swmansion/audioapi/system/MediaSessionEventEmitter.kt +4 -0
- package/android/src/main/java/com/swmansion/audioapi/system/MediaSessionManager.kt +31 -13
- package/android/src/main/java/com/swmansion/audioapi/system/VolumeChangeListener.kt +27 -0
- package/common/cpp/audioapi/AudioAPIModuleInstaller.h +29 -5
- package/common/cpp/audioapi/HostObjects/AnalyserNodeHostObject.h +1 -0
- package/common/cpp/audioapi/HostObjects/AudioRecorderHostObject.h +149 -0
- package/common/cpp/audioapi/core/AudioContext.cpp +4 -3
- package/common/cpp/audioapi/core/BaseAudioContext.cpp +6 -6
- package/common/cpp/audioapi/core/inputs/AudioRecorder.h +38 -0
- package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h +5 -0
- package/common/cpp/audioapi/core/sources/OscillatorNode.cpp +1 -1
- package/common/cpp/audioapi/core/utils/AudioNodeManager.cpp +45 -11
- package/common/cpp/audioapi/core/utils/AudioNodeManager.h +6 -2
- package/ios/audioapi/ios/AudioManagerModule.mm +9 -10
- package/ios/audioapi/ios/core/IOSAudioPlayer.h +11 -12
- package/ios/audioapi/ios/core/IOSAudioPlayer.mm +22 -16
- package/ios/audioapi/ios/core/IOSAudioRecorder.h +36 -0
- package/ios/audioapi/ios/core/IOSAudioRecorder.mm +64 -0
- package/ios/audioapi/ios/core/{AudioPlayer.h → NativeAudioPlayer.h} +1 -8
- package/ios/audioapi/ios/core/{AudioPlayer.m → NativeAudioPlayer.m} +4 -33
- package/ios/audioapi/ios/core/NativeAudioRecorder.h +31 -0
- package/ios/audioapi/ios/core/NativeAudioRecorder.m +107 -0
- package/ios/audioapi/ios/system/AudioEngine.h +7 -1
- package/ios/audioapi/ios/system/AudioEngine.mm +64 -20
- package/ios/audioapi/ios/system/AudioSessionManager.h +3 -1
- package/ios/audioapi/ios/system/AudioSessionManager.mm +37 -25
- package/ios/audioapi/ios/system/NotificationManager.h +7 -1
- package/ios/audioapi/ios/system/NotificationManager.mm +30 -0
- package/lib/commonjs/api.js +15 -1
- package/lib/commonjs/api.js.map +1 -1
- package/lib/commonjs/core/AudioRecorder.js +51 -0
- package/lib/commonjs/core/AudioRecorder.js.map +1 -0
- package/lib/commonjs/errors/NotSupportedError.js.map +1 -1
- package/lib/commonjs/hooks/useSytemVolume.js +24 -0
- package/lib/commonjs/hooks/useSytemVolume.js.map +1 -0
- package/lib/commonjs/plugin/withAudioAPI.js +1 -1
- package/lib/commonjs/plugin/withAudioAPI.js.map +1 -1
- package/lib/commonjs/specs/NativeAudioManagerModule.js +5 -2
- package/lib/commonjs/specs/NativeAudioManagerModule.js.map +1 -1
- package/lib/commonjs/system/AudioManager.js +23 -50
- package/lib/commonjs/system/AudioManager.js.map +1 -1
- package/lib/module/api.js +3 -1
- package/lib/module/api.js.map +1 -1
- package/lib/module/core/AudioRecorder.js +45 -0
- package/lib/module/core/AudioRecorder.js.map +1 -0
- package/lib/module/errors/NotSupportedError.js.map +1 -1
- package/lib/module/hooks/useSytemVolume.js +19 -0
- package/lib/module/hooks/useSytemVolume.js.map +1 -0
- package/lib/module/plugin/withAudioAPI.js +1 -1
- package/lib/module/plugin/withAudioAPI.js.map +1 -1
- package/lib/module/specs/NativeAudioManagerModule.js +5 -2
- package/lib/module/specs/NativeAudioManagerModule.js.map +1 -1
- package/lib/module/system/AudioManager.js +23 -50
- package/lib/module/system/AudioManager.js.map +1 -1
- package/lib/typescript/api.d.ts +5 -1
- package/lib/typescript/api.d.ts.map +1 -1
- package/lib/typescript/core/AudioRecorder.d.ts +22 -0
- package/lib/typescript/core/AudioRecorder.d.ts.map +1 -0
- package/lib/typescript/errors/NotSupportedError.d.ts.map +1 -1
- package/lib/typescript/hooks/useSytemVolume.d.ts +2 -0
- package/lib/typescript/hooks/useSytemVolume.d.ts.map +1 -0
- package/lib/typescript/interfaces.d.ts +11 -5
- package/lib/typescript/interfaces.d.ts.map +1 -1
- package/lib/typescript/plugin/withAudioAPI.d.ts.map +1 -1
- package/lib/typescript/specs/NativeAudioManagerModule.d.ts +2 -1
- package/lib/typescript/specs/NativeAudioManagerModule.d.ts.map +1 -1
- package/lib/typescript/system/AudioManager.d.ts +4 -3
- package/lib/typescript/system/AudioManager.d.ts.map +1 -1
- package/lib/typescript/system/types.d.ts +26 -7
- package/lib/typescript/system/types.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +5 -0
- package/lib/typescript/types.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/api.ts +13 -2
- package/src/core/AudioRecorder.ts +81 -0
- package/src/hooks/useSytemVolume.ts +19 -0
- package/src/interfaces.ts +25 -11
- package/src/plugin/withAudioAPI.ts +2 -1
- package/src/specs/NativeAudioManagerModule.ts +5 -8
- package/src/system/AudioManager.ts +30 -107
- package/src/system/types.ts +31 -21
- package/src/types.ts +13 -0
- /package/src/errors/{NotSupportedError.tsx → NotSupportedError.ts} +0 -0
|
@@ -18,9 +18,10 @@ AudioContext::AudioContext(float sampleRate) : BaseAudioContext() {
|
|
|
18
18
|
std::make_shared<IOSAudioPlayer>(this->renderAudio(), sampleRate);
|
|
19
19
|
#endif
|
|
20
20
|
|
|
21
|
-
sampleRate_ =
|
|
22
|
-
audioDecoder_ = std::make_shared<AudioDecoder>(
|
|
21
|
+
sampleRate_ = sampleRate;
|
|
22
|
+
audioDecoder_ = std::make_shared<AudioDecoder>(sampleRate);
|
|
23
23
|
|
|
24
|
+
state_ = ContextState::RUNNING;
|
|
24
25
|
audioPlayer_->start();
|
|
25
26
|
}
|
|
26
27
|
|
|
@@ -53,7 +54,7 @@ bool AudioContext::suspend() {
|
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
state_ = ContextState::SUSPENDED;
|
|
56
|
-
audioPlayer_->
|
|
57
|
+
audioPlayer_->pause();
|
|
57
58
|
return true;
|
|
58
59
|
}
|
|
59
60
|
|
|
@@ -43,25 +43,25 @@ std::shared_ptr<AudioDestinationNode> BaseAudioContext::getDestination() {
|
|
|
43
43
|
|
|
44
44
|
std::shared_ptr<OscillatorNode> BaseAudioContext::createOscillator() {
|
|
45
45
|
auto oscillator = std::make_shared<OscillatorNode>(this);
|
|
46
|
-
nodeManager_->
|
|
46
|
+
nodeManager_->addSourceNode(oscillator);
|
|
47
47
|
return oscillator;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
std::shared_ptr<GainNode> BaseAudioContext::createGain() {
|
|
51
51
|
auto gain = std::make_shared<GainNode>(this);
|
|
52
|
-
nodeManager_->
|
|
52
|
+
nodeManager_->addProcessingNode(gain);
|
|
53
53
|
return gain;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
std::shared_ptr<StereoPannerNode> BaseAudioContext::createStereoPanner() {
|
|
57
57
|
auto stereoPanner = std::make_shared<StereoPannerNode>(this);
|
|
58
|
-
nodeManager_->
|
|
58
|
+
nodeManager_->addProcessingNode(stereoPanner);
|
|
59
59
|
return stereoPanner;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
std::shared_ptr<BiquadFilterNode> BaseAudioContext::createBiquadFilter() {
|
|
63
63
|
auto biquadFilter = std::make_shared<BiquadFilterNode>(this);
|
|
64
|
-
nodeManager_->
|
|
64
|
+
nodeManager_->addProcessingNode(biquadFilter);
|
|
65
65
|
return biquadFilter;
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -69,7 +69,7 @@ std::shared_ptr<AudioBufferSourceNode> BaseAudioContext::createBufferSource(
|
|
|
69
69
|
bool pitchCorrection) {
|
|
70
70
|
auto bufferSource =
|
|
71
71
|
std::make_shared<AudioBufferSourceNode>(this, pitchCorrection);
|
|
72
|
-
nodeManager_->
|
|
72
|
+
nodeManager_->addSourceNode(bufferSource);
|
|
73
73
|
return bufferSource;
|
|
74
74
|
}
|
|
75
75
|
|
|
@@ -90,7 +90,7 @@ std::shared_ptr<PeriodicWave> BaseAudioContext::createPeriodicWave(
|
|
|
90
90
|
|
|
91
91
|
std::shared_ptr<AnalyserNode> BaseAudioContext::createAnalyser() {
|
|
92
92
|
auto analyser = std::make_shared<AnalyserNode>(this);
|
|
93
|
-
nodeManager_->
|
|
93
|
+
nodeManager_->addProcessingNode(analyser);
|
|
94
94
|
return analyser;
|
|
95
95
|
}
|
|
96
96
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <memory>
|
|
4
|
+
#include <functional>
|
|
5
|
+
|
|
6
|
+
namespace audioapi {
|
|
7
|
+
class AudioBus;
|
|
8
|
+
|
|
9
|
+
class AudioRecorder {
|
|
10
|
+
public:
|
|
11
|
+
explicit AudioRecorder(
|
|
12
|
+
float sampleRate,
|
|
13
|
+
int bufferLength,
|
|
14
|
+
const std::function<void(void)> &onError,
|
|
15
|
+
const std::function<void(void)> &onStatusChange,
|
|
16
|
+
const std::function<void(std::shared_ptr<AudioBus>, int, double)> &onAudioReady
|
|
17
|
+
)
|
|
18
|
+
: sampleRate_(sampleRate),
|
|
19
|
+
bufferLength_(bufferLength),
|
|
20
|
+
onError_(onError),
|
|
21
|
+
onStatusChange_(onStatusChange),
|
|
22
|
+
onAudioReady_(onAudioReady) {}
|
|
23
|
+
|
|
24
|
+
virtual ~AudioRecorder() = default;
|
|
25
|
+
|
|
26
|
+
virtual void start() = 0;
|
|
27
|
+
virtual void stop() = 0;
|
|
28
|
+
|
|
29
|
+
protected:
|
|
30
|
+
float sampleRate_;
|
|
31
|
+
int bufferLength_;
|
|
32
|
+
|
|
33
|
+
std::function<void(void)> onError_;
|
|
34
|
+
std::function<void(void)> onStatusChange_;
|
|
35
|
+
std::function<void(std::shared_ptr<AudioBus>, int, double)> onAudioReady_;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
} // namespace audioapi
|
|
@@ -18,6 +18,11 @@ namespace audioapi {
|
|
|
18
18
|
|
|
19
19
|
class AudioScheduledSourceNode : public AudioNode {
|
|
20
20
|
public:
|
|
21
|
+
// UNSCHEDULED: The node is not scheduled to play.
|
|
22
|
+
// SCHEDULED: The node is scheduled to play at a specific time.
|
|
23
|
+
// PLAYING: The node is currently playing.
|
|
24
|
+
// FINISHED: The node has finished playing.
|
|
25
|
+
// STOP_SCHEDULED: The node is scheduled to stop at a specific time, but is still playing.
|
|
21
26
|
enum class PlaybackState { UNSCHEDULED, SCHEDULED, PLAYING, FINISHED, STOP_SCHEDULED };
|
|
22
27
|
explicit AudioScheduledSourceNode(BaseAudioContext *context);
|
|
23
28
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#include <audioapi/core/AudioNode.h>
|
|
2
|
+
#include <audioapi/core/sources/AudioScheduledSourceNode.h>
|
|
2
3
|
#include <audioapi/core/utils/AudioNodeManager.h>
|
|
3
4
|
#include <audioapi/core/utils/Locker.h>
|
|
4
5
|
|
|
@@ -28,9 +29,16 @@ std::mutex &AudioNodeManager::getGraphLock() {
|
|
|
28
29
|
return graphLock_;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
void AudioNodeManager::
|
|
32
|
+
void AudioNodeManager::addProcessingNode(
|
|
33
|
+
const std::shared_ptr<AudioNode> &node) {
|
|
32
34
|
Locker lock(getGraphLock());
|
|
33
|
-
|
|
35
|
+
processingNodes_.insert(node);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
void AudioNodeManager::addSourceNode(
|
|
39
|
+
const std::shared_ptr<AudioScheduledSourceNode> &node) {
|
|
40
|
+
Locker lock(getGraphLock());
|
|
41
|
+
sourceNodes_.insert(node);
|
|
34
42
|
}
|
|
35
43
|
|
|
36
44
|
void AudioNodeManager::settlePendingConnections() {
|
|
@@ -54,17 +62,35 @@ void AudioNodeManager::settlePendingConnections() {
|
|
|
54
62
|
audioNodesToConnect_.clear();
|
|
55
63
|
}
|
|
56
64
|
|
|
65
|
+
void AudioNodeManager::cleanupNode(const std::shared_ptr<AudioNode> &node) {
|
|
66
|
+
nodeDeconstructor_.addNodeForDeconstruction(node);
|
|
67
|
+
node.get()->cleanup();
|
|
68
|
+
}
|
|
69
|
+
|
|
57
70
|
void AudioNodeManager::prepareNodesForDestruction() {
|
|
58
71
|
nodeDeconstructor_.tryCallWithLock([this]() {
|
|
59
|
-
auto
|
|
72
|
+
auto sNodesIt = sourceNodes_.begin();
|
|
73
|
+
|
|
74
|
+
while (sNodesIt != sourceNodes_.end()) {
|
|
75
|
+
// we don't want to destroy nodes that are still playing or will be
|
|
76
|
+
// playing
|
|
77
|
+
if (sNodesIt->use_count() == 1 &&
|
|
78
|
+
(sNodesIt->get()->isUnscheduled() || sNodesIt->get()->isFinished())) {
|
|
79
|
+
cleanupNode(*sNodesIt);
|
|
80
|
+
sNodesIt = sourceNodes_.erase(sNodesIt);
|
|
81
|
+
} else {
|
|
82
|
+
++sNodesIt;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
60
85
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
86
|
+
auto pNodesIt = processingNodes_.begin();
|
|
87
|
+
|
|
88
|
+
while (pNodesIt != processingNodes_.end()) {
|
|
89
|
+
if (pNodesIt->use_count() == 1) {
|
|
90
|
+
cleanupNode(*pNodesIt);
|
|
91
|
+
pNodesIt = processingNodes_.erase(pNodesIt);
|
|
66
92
|
} else {
|
|
67
|
-
++
|
|
93
|
+
++pNodesIt;
|
|
68
94
|
}
|
|
69
95
|
}
|
|
70
96
|
});
|
|
@@ -74,11 +100,19 @@ void AudioNodeManager::prepareNodesForDestruction() {
|
|
|
74
100
|
void AudioNodeManager::cleanup() {
|
|
75
101
|
Locker lock(getGraphLock());
|
|
76
102
|
|
|
77
|
-
for (auto it =
|
|
103
|
+
for (auto it = sourceNodes_.begin(), end = sourceNodes_.end(); it != end;
|
|
104
|
+
++it) {
|
|
105
|
+
it->get()->cleanup();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
for (auto it = processingNodes_.begin(), end = processingNodes_.end();
|
|
109
|
+
it != end;
|
|
110
|
+
++it) {
|
|
78
111
|
it->get()->cleanup();
|
|
79
112
|
}
|
|
80
113
|
|
|
81
|
-
|
|
114
|
+
sourceNodes_.clear();
|
|
115
|
+
processingNodes_.clear();
|
|
82
116
|
}
|
|
83
117
|
|
|
84
118
|
} // namespace audioapi
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
namespace audioapi {
|
|
12
12
|
|
|
13
13
|
class AudioNode;
|
|
14
|
+
class AudioScheduledSourceNode;
|
|
14
15
|
|
|
15
16
|
class AudioNodeManager {
|
|
16
17
|
public:
|
|
@@ -27,7 +28,8 @@ class AudioNodeManager {
|
|
|
27
28
|
const std::shared_ptr<AudioNode> &to,
|
|
28
29
|
ConnectionType type);
|
|
29
30
|
|
|
30
|
-
void
|
|
31
|
+
void addProcessingNode(const std::shared_ptr<AudioNode> &node);
|
|
32
|
+
void addSourceNode(const std::shared_ptr<AudioScheduledSourceNode> &node);
|
|
31
33
|
|
|
32
34
|
void cleanup();
|
|
33
35
|
|
|
@@ -36,7 +38,8 @@ class AudioNodeManager {
|
|
|
36
38
|
AudioNodeDestructor nodeDeconstructor_;
|
|
37
39
|
|
|
38
40
|
// all nodes created in the context
|
|
39
|
-
std::unordered_set<std::shared_ptr<
|
|
41
|
+
std::unordered_set<std::shared_ptr<AudioScheduledSourceNode>> sourceNodes_;
|
|
42
|
+
std::unordered_set<std::shared_ptr<AudioNode>> processingNodes_;
|
|
40
43
|
|
|
41
44
|
// connections to be settled
|
|
42
45
|
std::vector<std::tuple<
|
|
@@ -46,6 +49,7 @@ class AudioNodeManager {
|
|
|
46
49
|
audioNodesToConnect_;
|
|
47
50
|
|
|
48
51
|
void settlePendingConnections();
|
|
52
|
+
void cleanupNode(const std::shared_ptr<AudioNode> &node);
|
|
49
53
|
void prepareNodesForDestruction();
|
|
50
54
|
};
|
|
51
55
|
|
|
@@ -49,16 +49,9 @@ RCT_EXPORT_METHOD(enableRemoteCommand : (NSString *)name enabled : (BOOL)enabled
|
|
|
49
49
|
[self.lockScreenManager enableRemoteCommand:name enabled:enabled];
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
RCT_EXPORT_METHOD(setAudioSessionOptions : (NSString *)category mode : (NSString *)mode options : (NSArray *)
|
|
53
|
-
options active : (BOOL)active)
|
|
52
|
+
RCT_EXPORT_METHOD(setAudioSessionOptions : (NSString *)category mode : (NSString *)mode options : (NSArray *)options)
|
|
54
53
|
{
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (active) {
|
|
58
|
-
[self.audioSessionManager setAudioSessionOptions:category mode:mode options:options];
|
|
59
|
-
} else {
|
|
60
|
-
[self.audioSessionManager setActive:false error:&error];
|
|
61
|
-
}
|
|
54
|
+
[self.audioSessionManager setAudioSessionOptions:category mode:mode options:options];
|
|
62
55
|
}
|
|
63
56
|
|
|
64
57
|
RCT_EXPORT_METHOD(observeAudioInterruptions : (BOOL)enabled)
|
|
@@ -66,6 +59,11 @@ RCT_EXPORT_METHOD(observeAudioInterruptions : (BOOL)enabled)
|
|
|
66
59
|
[self.notificationManager observeAudioInterruptions:enabled];
|
|
67
60
|
}
|
|
68
61
|
|
|
62
|
+
RCT_EXPORT_METHOD(observeVolumeChanges : (BOOL)enabled)
|
|
63
|
+
{
|
|
64
|
+
[self.notificationManager observeVolumeChanges:(BOOL)enabled];
|
|
65
|
+
}
|
|
66
|
+
|
|
69
67
|
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getDevicePreferredSampleRate)
|
|
70
68
|
{
|
|
71
69
|
return [self.audioSessionManager getDevicePreferredSampleRate];
|
|
@@ -87,7 +85,8 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getDevicePreferredSampleRate)
|
|
|
87
85
|
@"onRemoteSeekBackward",
|
|
88
86
|
@"onRemoteChangePlaybackPosition",
|
|
89
87
|
@"onInterruption",
|
|
90
|
-
@"onRouteChange"
|
|
88
|
+
@"onRouteChange",
|
|
89
|
+
@"onVolumeChange"
|
|
91
90
|
];
|
|
92
91
|
}
|
|
93
92
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
3
|
#ifdef __OBJC__ // when compiled as Objective-C++
|
|
4
|
-
#import <
|
|
4
|
+
#import <NativeAudioPlayer.h>
|
|
5
5
|
#else // when compiled as C++
|
|
6
|
-
typedef struct objc_object
|
|
6
|
+
typedef struct objc_object NativeAudioPlayer;
|
|
7
7
|
#endif // __OBJC__
|
|
8
8
|
|
|
9
9
|
#include <functional>
|
|
@@ -14,23 +14,22 @@ class AudioBus;
|
|
|
14
14
|
class AudioContext;
|
|
15
15
|
|
|
16
16
|
class IOSAudioPlayer {
|
|
17
|
-
protected:
|
|
18
|
-
std::shared_ptr<AudioBus> audioBus_;
|
|
19
|
-
AudioPlayer *audioPlayer_;
|
|
20
|
-
std::function<void(std::shared_ptr<AudioBus>, int)> renderAudio_;
|
|
21
|
-
int channelCount_;
|
|
22
|
-
|
|
23
17
|
public:
|
|
24
18
|
IOSAudioPlayer(
|
|
25
19
|
const std::function<void(std::shared_ptr<AudioBus>, int)> &renderAudio,
|
|
26
20
|
float sampleRate);
|
|
27
21
|
~IOSAudioPlayer();
|
|
28
22
|
|
|
29
|
-
float getSampleRate() const;
|
|
30
|
-
|
|
31
23
|
void start();
|
|
32
|
-
void stop();
|
|
33
24
|
void resume();
|
|
34
|
-
void
|
|
25
|
+
void stop();
|
|
26
|
+
void pause();
|
|
27
|
+
|
|
28
|
+
protected:
|
|
29
|
+
std::shared_ptr<AudioBus> audioBus_;
|
|
30
|
+
NativeAudioPlayer *audioPlayer_;
|
|
31
|
+
std::function<void(std::shared_ptr<AudioBus>, int)> renderAudio_;
|
|
32
|
+
int channelCount_;
|
|
33
|
+
std::atomic<bool> isRunning_;
|
|
35
34
|
};
|
|
36
35
|
} // namespace audioapi
|
|
@@ -9,14 +9,19 @@
|
|
|
9
9
|
namespace audioapi {
|
|
10
10
|
|
|
11
11
|
IOSAudioPlayer::IOSAudioPlayer(const std::function<void(std::shared_ptr<AudioBus>, int)> &renderAudio, float sampleRate)
|
|
12
|
-
: channelCount_(2), renderAudio_(renderAudio), audioBus_(0)
|
|
12
|
+
: channelCount_(2), renderAudio_(renderAudio), audioBus_(0), isRunning_(false)
|
|
13
13
|
{
|
|
14
14
|
RenderAudioBlock renderAudioBlock = ^(AudioBufferList *outputData, int numFrames) {
|
|
15
15
|
int processedFrames = 0;
|
|
16
16
|
|
|
17
17
|
while (processedFrames < numFrames) {
|
|
18
18
|
int framesToProcess = std::min(numFrames - processedFrames, RENDER_QUANTUM_SIZE);
|
|
19
|
-
|
|
19
|
+
|
|
20
|
+
if (isRunning_.load()) {
|
|
21
|
+
renderAudio_(audioBus_, framesToProcess);
|
|
22
|
+
} else {
|
|
23
|
+
audioBus_->zero();
|
|
24
|
+
}
|
|
20
25
|
|
|
21
26
|
for (int channel = 0; channel < channelCount_; channel += 1) {
|
|
22
27
|
float *outputChannel = (float *)outputData->mBuffers[channel].mData;
|
|
@@ -29,11 +34,11 @@ IOSAudioPlayer::IOSAudioPlayer(const std::function<void(std::shared_ptr<AudioBus
|
|
|
29
34
|
}
|
|
30
35
|
};
|
|
31
36
|
|
|
32
|
-
audioPlayer_ = [[
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
audioPlayer_ = [[NativeAudioPlayer alloc] initWithRenderAudio:renderAudioBlock
|
|
38
|
+
sampleRate:sampleRate
|
|
39
|
+
channelCount:channelCount_];
|
|
35
40
|
|
|
36
|
-
audioBus_ = std::make_shared<AudioBus>(RENDER_QUANTUM_SIZE, channelCount_,
|
|
41
|
+
audioBus_ = std::make_shared<AudioBus>(RENDER_QUANTUM_SIZE, channelCount_, sampleRate);
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
IOSAudioPlayer::~IOSAudioPlayer()
|
|
@@ -48,27 +53,28 @@ IOSAudioPlayer::~IOSAudioPlayer()
|
|
|
48
53
|
|
|
49
54
|
void IOSAudioPlayer::start()
|
|
50
55
|
{
|
|
51
|
-
|
|
52
|
-
|
|
56
|
+
if (isRunning_.load()) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
53
59
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return [audioPlayer_ stop];
|
|
60
|
+
[audioPlayer_ start];
|
|
61
|
+
isRunning_.store(true);
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
void IOSAudioPlayer::resume()
|
|
60
65
|
{
|
|
61
|
-
|
|
66
|
+
isRunning_.store(true);
|
|
62
67
|
}
|
|
63
68
|
|
|
64
|
-
void IOSAudioPlayer::
|
|
69
|
+
void IOSAudioPlayer::stop()
|
|
65
70
|
{
|
|
66
|
-
|
|
71
|
+
isRunning_.store(false);
|
|
72
|
+
[audioPlayer_ stop];
|
|
67
73
|
}
|
|
68
74
|
|
|
69
|
-
|
|
75
|
+
void IOSAudioPlayer::pause()
|
|
70
76
|
{
|
|
71
|
-
|
|
77
|
+
isRunning_.store(false);
|
|
72
78
|
}
|
|
73
79
|
|
|
74
80
|
} // namespace audioapi
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#ifdef __OBJC__ // when compiled as Objective-C++
|
|
4
|
+
#import <NativeAudioRecorder.h>
|
|
5
|
+
#else // when compiled as C++
|
|
6
|
+
typedef struct objc_object NativeAudioRecorder;
|
|
7
|
+
#endif // __OBJC__
|
|
8
|
+
|
|
9
|
+
#include <audioapi/core/inputs/AudioRecorder.h>
|
|
10
|
+
#include <functional>
|
|
11
|
+
|
|
12
|
+
namespace audioapi {
|
|
13
|
+
|
|
14
|
+
class AudioBus;
|
|
15
|
+
|
|
16
|
+
class IOSAudioRecorder : public AudioRecorder {
|
|
17
|
+
public:
|
|
18
|
+
IOSAudioRecorder(
|
|
19
|
+
float sampleRate,
|
|
20
|
+
int bufferLength,
|
|
21
|
+
const std::function<void(void)> &onError,
|
|
22
|
+
const std::function<void(void)> &onStatusChange,
|
|
23
|
+
const std::function<void(std::shared_ptr<AudioBus>, int, double)>
|
|
24
|
+
&onAudioReady);
|
|
25
|
+
|
|
26
|
+
~IOSAudioRecorder() override;
|
|
27
|
+
|
|
28
|
+
void start() override;
|
|
29
|
+
void stop() override;
|
|
30
|
+
|
|
31
|
+
private:
|
|
32
|
+
NativeAudioRecorder *audioRecorder_;
|
|
33
|
+
std::atomic<bool> isRunning_;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
} // namespace audioapi
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#import <AVFoundation/AVFoundation.h>
|
|
2
|
+
|
|
3
|
+
#include <audioapi/core/Constants.h>
|
|
4
|
+
#include <audioapi/dsp/VectorMath.h>
|
|
5
|
+
#include <audioapi/ios/core/IOSAudioRecorder.h>
|
|
6
|
+
#include <audioapi/utils/AudioArray.h>
|
|
7
|
+
#include <audioapi/utils/AudioBus.h>
|
|
8
|
+
|
|
9
|
+
namespace audioapi {
|
|
10
|
+
|
|
11
|
+
IOSAudioRecorder::IOSAudioRecorder(
|
|
12
|
+
float sampleRate,
|
|
13
|
+
int bufferLength,
|
|
14
|
+
const std::function<void(void)> &onError,
|
|
15
|
+
const std::function<void(void)> &onStatusChange,
|
|
16
|
+
const std::function<void(std::shared_ptr<AudioBus>, int, double)> &onAudioReady)
|
|
17
|
+
: AudioRecorder(sampleRate, bufferLength, onError, onStatusChange, onAudioReady)
|
|
18
|
+
{
|
|
19
|
+
AudioReceiverBlock audioReceiverBlock = ^(const AudioBufferList *inputBuffer, int numFrames, AVAudioTime *when) {
|
|
20
|
+
if (isRunning_.load()) {
|
|
21
|
+
auto bus = std::make_shared<AudioBus>(numFrames, 1, sampleRate);
|
|
22
|
+
|
|
23
|
+
auto *inputChannel = (float *)inputBuffer->mBuffers[0].mData;
|
|
24
|
+
auto *outputChannel = bus->getChannel(0)->getData();
|
|
25
|
+
|
|
26
|
+
memcpy(outputChannel, inputChannel, numFrames * sizeof(float));
|
|
27
|
+
onAudioReady_(bus, numFrames, [when sampleTime] / [when sampleRate]);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
audioRecorder_ = [[NativeAudioRecorder alloc] initWithReceiverBlock:audioReceiverBlock
|
|
32
|
+
bufferLength:bufferLength
|
|
33
|
+
sampleRate:sampleRate];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
IOSAudioRecorder::~IOSAudioRecorder()
|
|
37
|
+
{
|
|
38
|
+
AudioRecorder::~AudioRecorder();
|
|
39
|
+
|
|
40
|
+
stop();
|
|
41
|
+
[audioRecorder_ cleanup];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
void IOSAudioRecorder::start()
|
|
45
|
+
{
|
|
46
|
+
if (isRunning_.load()) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
[audioRecorder_ start];
|
|
51
|
+
isRunning_.store(true);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
void IOSAudioRecorder::stop()
|
|
55
|
+
{
|
|
56
|
+
if (!isRunning_.load()) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
isRunning_.store(false);
|
|
61
|
+
[audioRecorder_ stop];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
} // namespace audioapi
|
|
@@ -5,14 +5,13 @@
|
|
|
5
5
|
|
|
6
6
|
typedef void (^RenderAudioBlock)(AudioBufferList *outputBuffer, int numFrames);
|
|
7
7
|
|
|
8
|
-
@interface
|
|
8
|
+
@interface NativeAudioPlayer : NSObject
|
|
9
9
|
|
|
10
10
|
@property (nonatomic, strong) AVAudioFormat *format;
|
|
11
11
|
@property (nonatomic, strong) AVAudioSourceNode *sourceNode;
|
|
12
12
|
@property (nonatomic, copy) RenderAudioBlock renderAudio;
|
|
13
13
|
@property (nonatomic, assign) float sampleRate;
|
|
14
14
|
@property (nonatomic, assign) int channelCount;
|
|
15
|
-
@property (nonatomic, assign) bool isRunning;
|
|
16
15
|
@property (nonatomic, strong) NSString *sourceNodeId;
|
|
17
16
|
@property (nonatomic, strong) AVAudioSourceNodeRenderBlock renderBlock;
|
|
18
17
|
|
|
@@ -20,16 +19,10 @@ typedef void (^RenderAudioBlock)(AudioBufferList *outputBuffer, int numFrames);
|
|
|
20
19
|
sampleRate:(float)sampleRate
|
|
21
20
|
channelCount:(int)channelCount;
|
|
22
21
|
|
|
23
|
-
- (float)getSampleRate;
|
|
24
|
-
|
|
25
22
|
- (void)start;
|
|
26
23
|
|
|
27
24
|
- (void)stop;
|
|
28
25
|
|
|
29
|
-
- (void)resume;
|
|
30
|
-
|
|
31
|
-
- (void)suspend;
|
|
32
|
-
|
|
33
26
|
- (void)cleanup;
|
|
34
27
|
|
|
35
28
|
@end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
#import <audioapi/ios/core/
|
|
1
|
+
#import <audioapi/ios/core/NativeAudioPlayer.h>
|
|
2
2
|
#import <audioapi/ios/system/AudioEngine.h>
|
|
3
3
|
|
|
4
|
-
@implementation
|
|
4
|
+
@implementation NativeAudioPlayer
|
|
5
5
|
|
|
6
6
|
- (instancetype)initWithRenderAudio:(RenderAudioBlock)renderAudio
|
|
7
7
|
sampleRate:(float)sampleRate
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
weakSelf.renderAudio(outputData, frameCount);
|
|
24
|
+
|
|
24
25
|
return kAudioServicesNoError;
|
|
25
26
|
};
|
|
26
27
|
|
|
@@ -31,53 +32,23 @@
|
|
|
31
32
|
return self;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
- (float)getSampleRate
|
|
35
|
-
{
|
|
36
|
-
return self.sampleRate;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
35
|
- (void)start
|
|
40
36
|
{
|
|
41
37
|
NSLog(@"[AudioPlayer] start");
|
|
38
|
+
|
|
42
39
|
AudioEngine *audioEngine = [AudioEngine sharedInstance];
|
|
43
|
-
self.isRunning = true;
|
|
44
40
|
self.sourceNodeId = [audioEngine attachSourceNode:self.sourceNode format:self.format];
|
|
45
41
|
}
|
|
46
42
|
|
|
47
43
|
- (void)stop
|
|
48
44
|
{
|
|
49
45
|
NSLog(@"[AudioPlayer] stop");
|
|
50
|
-
if (!self.isRunning) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
46
|
|
|
54
47
|
AudioEngine *audioEngine = [AudioEngine sharedInstance];
|
|
55
|
-
self.isRunning = false;
|
|
56
48
|
[audioEngine detachSourceNodeWithId:self.sourceNodeId];
|
|
57
49
|
self.sourceNodeId = nil;
|
|
58
50
|
}
|
|
59
51
|
|
|
60
|
-
- (void)suspend
|
|
61
|
-
{
|
|
62
|
-
NSLog(@"[AudioPlayer] suspend");
|
|
63
|
-
if (!self.isRunning) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
AudioEngine *audioEngine = [AudioEngine sharedInstance];
|
|
68
|
-
self.isRunning = false;
|
|
69
|
-
[audioEngine detachSourceNodeWithId:self.sourceNodeId];
|
|
70
|
-
self.sourceNodeId = nil;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
- (void)resume
|
|
74
|
-
{
|
|
75
|
-
NSLog(@"[AudioPlayer] resume");
|
|
76
|
-
AudioEngine *audioEngine = [AudioEngine sharedInstance];
|
|
77
|
-
self.isRunning = true;
|
|
78
|
-
self.sourceNodeId = [audioEngine attachSourceNode:self.sourceNode format:self.format];
|
|
79
|
-
}
|
|
80
|
-
|
|
81
52
|
- (void)cleanup
|
|
82
53
|
{
|
|
83
54
|
self.renderAudio = nil;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#import <AVFoundation/AVFoundation.h>
|
|
4
|
+
#import <Foundation/Foundation.h>
|
|
5
|
+
|
|
6
|
+
typedef void (^AudioReceiverBlock)(const AudioBufferList *inputBuffer, int numFrames, AVAudioTime *when);
|
|
7
|
+
|
|
8
|
+
@interface NativeAudioRecorder : NSObject
|
|
9
|
+
|
|
10
|
+
@property (nonatomic, assign) int bufferLength;
|
|
11
|
+
@property (nonatomic, assign) float sampleRate;
|
|
12
|
+
|
|
13
|
+
@property (nonatomic, strong) AVAudioSinkNode *sinkNode;
|
|
14
|
+
@property (nonatomic, copy) AVAudioSinkNodeReceiverBlock receiverSinkBlock;
|
|
15
|
+
@property (nonatomic, copy) AudioReceiverBlock receiverBlock;
|
|
16
|
+
|
|
17
|
+
@property (nonatomic, strong) AVAudioConverter *audioConverter;
|
|
18
|
+
@property (nonatomic, strong) AVAudioFormat *inputFormat;
|
|
19
|
+
@property (nonatomic, strong) AVAudioFormat *outputFormat;
|
|
20
|
+
|
|
21
|
+
- (instancetype)initWithReceiverBlock:(AudioReceiverBlock)receiverBlock
|
|
22
|
+
bufferLength:(int)bufferLength
|
|
23
|
+
sampleRate:(float)sampleRate;
|
|
24
|
+
|
|
25
|
+
- (void)start;
|
|
26
|
+
|
|
27
|
+
- (void)stop;
|
|
28
|
+
|
|
29
|
+
- (void)cleanup;
|
|
30
|
+
|
|
31
|
+
@end
|