Skip to content

Commit 373e473

Browse files
maciejmakowski2003Maciej Makowski
andauthored
Refactor/onEnded (#583)
* refactor: refactored onEnded * ci: yarn format * fix: override onEnded in ABSN * docs: added overridden `onEnded` callback to AudioBufferQueueSourceNode docs --------- Co-authored-by: Maciej Makowski <[email protected]>
1 parent 89c8163 commit 373e473

File tree

12 files changed

+88
-48
lines changed

12 files changed

+88
-48
lines changed

packages/audiodocs/docs/sources/audio-buffer-queue-source-node.mdx

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
sidebar_position: 5
33
---
44

5-
import { Optional, Experimental } from '@site/src/components/Badges';
5+
import { Optional, Experimental, Overridden } from '@site/src/components/Badges';
66

77
# AudioBufferQueueSourceNode
88

@@ -44,12 +44,29 @@ function App() {
4444

4545
### `enqueueBuffer`
4646

47-
The above method lets you add another buffer to queue.
47+
The above method lets you add another buffer to queue. Returns `bufferId` that can be used to identify the buffer in `onEnded` event.
4848

4949
| Parameters | Type | Description |
5050
| :---: | :---: | :---- |
51-
| `buffer` | [`AudioBuffer`](/docs/sources/audio-buffer) | Buffer with next data. |
52-
| `isLastBuffer` | `boolean` | Boolean indicating if it is a last buffer. Default value is `false`. Queue will stop after buffer marked as `last` will end. |
51+
| `buffer` | [`AudioBuffer`](/sources/audio-buffer) | Buffer with next data. |
5352

54-
#### Returns `undefined`.
53+
#### Returns `string`.
5554

55+
## Events
56+
57+
### `onEnded` <Overridden />
58+
59+
Allows to set (or remove) callback that will be fired when queue source node has stopped playing with payload `event.bufferId = undefined`
60+
or when a specific buffer has ended with payload `event.bufferId = <bufferId>`.
61+
62+
You can remove callback by passing `null`.
63+
64+
```ts
65+
audioBufferSourceNode.onEnded = (event) => { //setting callback
66+
if (event.bufferId === undefined) {
67+
console.log('queue source node has been stopped');
68+
} else {
69+
console.log(`buffer with id {event.bufferId} ended`);
70+
}
71+
};
72+
```

packages/audiodocs/docs/sources/audio-scheduled-source-node.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ If you invoke this method multiple times on the same node before the designated
5353

5454
## Events
5555

56+
### `onEnded`
57+
5658
Allows to set (or remove) callback that will be fired when source node has stopped playing,
5759
either because it's reached a predetermined stop time, the full duration of the audio has been performed, or because the entire buffer has been played.
5860
You can remove callback by passing `null`.

packages/react-native-audio-api/common/cpp/audioapi/HostObjects/AudioBufferQueueSourceNodeHostObject.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,10 @@ class AudioBufferQueueSourceNodeHostObject
3636

3737
auto audioBufferHostObject =
3838
args[0].getObject(runtime).asHostObject<AudioBufferHostObject>(runtime);
39-
auto isLastBuffer = args[1].asBool();
4039

41-
audioBufferQueueSourceNode->enqueueBuffer(audioBufferHostObject->audioBuffer_, isLastBuffer);
40+
auto bufferId = audioBufferQueueSourceNode->enqueueBuffer(audioBufferHostObject->audioBuffer_);
4241

43-
return jsi::Value::undefined();
42+
return jsi::String::createFromUtf8(runtime, bufferId);
4443
}
4544
};
4645

packages/react-native-audio-api/common/cpp/audioapi/core/AudioParam.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ AudioParam::AudioParam(
1818
minValue_(minValue),
1919
maxValue_(maxValue),
2020
context_(context),
21-
audioBus_(std::make_shared<AudioBus>(
22-
RENDER_QUANTUM_SIZE,
23-
1,
24-
context->getSampleRate())) {
21+
audioBus_(
22+
std::make_shared<AudioBus>(
23+
RENDER_QUANTUM_SIZE,
24+
1,
25+
context->getSampleRate())) {
2526
startTime_ = 0;
2627
endTime_ = 0;
2728
startValue_ = value_;

packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,12 @@ void AudioBufferQueueSourceNode::pause() {
3535
isPaused_ = true;
3636
}
3737

38-
void AudioBufferQueueSourceNode::enqueueBuffer(
39-
const std::shared_ptr<AudioBuffer> &buffer,
40-
bool isLastBuffer) {
38+
std::string AudioBufferQueueSourceNode::enqueueBuffer(
39+
const std::shared_ptr<AudioBuffer> &buffer) {
4140
auto locker = Locker(getBufferLock());
42-
buffers_.emplace(buffer);
41+
buffers_.emplace(bufferId_, buffer);
4342

44-
isLastBuffer_ = isLastBuffer;
43+
return std::to_string(bufferId_++);
4544
}
4645

4746
void AudioBufferQueueSourceNode::disable() {
@@ -93,7 +92,9 @@ void AudioBufferQueueSourceNode::processWithoutInterpolation(
9392
auto readIndex = static_cast<size_t>(vReadIndex_);
9493
size_t writeIndex = startOffset;
9594

96-
auto buffer = buffers_.front();
95+
auto data = buffers_.front();
96+
auto bufferId = data.first;
97+
auto buffer = data.second;
9798

9899
size_t framesLeft = offsetLength;
99100

@@ -117,16 +118,20 @@ void AudioBufferQueueSourceNode::processWithoutInterpolation(
117118
playedBuffersDuration_ += buffer->getDuration();
118119
buffers_.pop();
119120

121+
std::unordered_map<std::string, EventValue> body = {
122+
{"bufferId", std::to_string(bufferId)}};
123+
context_->audioEventHandlerRegistry_->invokeHandlerWithEventBody(
124+
"ended", onEndedCallbackId_, body);
125+
120126
if (buffers_.empty()) {
121127
processingBus->zero(writeIndex, framesLeft);
122128
readIndex = 0;
123129

124-
if (isLastBuffer_) {
125-
playbackState_ = PlaybackState::STOP_SCHEDULED;
126-
}
127130
break;
128131
} else {
129-
buffer = buffers_.front();
132+
data = buffers_.front();
133+
bufferId = data.first;
134+
buffer = data.second;
130135

131136
readIndex = 0;
132137
}

packages/react-native-audio-api/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode {
2323
void stop(double when) override;
2424
void pause();
2525

26-
void enqueueBuffer(const std::shared_ptr<AudioBuffer> &buffer, bool isLastBuffer);
26+
std::string enqueueBuffer(const std::shared_ptr<AudioBuffer> &buffer);
2727
void disable() override;
2828

2929
protected:
@@ -32,8 +32,9 @@ class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode {
3232

3333
private:
3434
// User provided buffers
35-
std::queue<std::shared_ptr<AudioBuffer>> buffers_;
36-
bool isLastBuffer_ = false;
35+
std::queue<std::pair<size_t, std::shared_ptr<AudioBuffer>>> buffers_;
36+
size_t bufferId_ = 0;
37+
3738
bool isPaused_ = false;
3839

3940
double playedBuffersDuration_ = 0;

packages/react-native-audio-api/ios/audioapi/ios/AudioAPIModule.mm

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,9 @@ - (void)invalidate
8585
return [self.audioSessionManager getDevicePreferredSampleRate];
8686
}
8787

88-
RCT_EXPORT_METHOD(setAudioSessionActivity : (BOOL)enabled resolve : (RCTPromiseResolveBlock)
89-
resolve reject : (RCTPromiseRejectBlock)reject)
88+
RCT_EXPORT_METHOD(
89+
setAudioSessionActivity : (BOOL)enabled resolve : (RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)
90+
reject)
9091
{
9192
if ([self.audioSessionManager setActive:enabled]) {
9293
resolve(@"true");
@@ -96,8 +97,9 @@ - (void)invalidate
9697
resolve(@"false");
9798
}
9899

99-
RCT_EXPORT_METHOD(setAudioSessionOptions : (NSString *)category mode : (NSString *)mode options : (NSArray *)
100-
options allowHaptics : (BOOL)allowHaptics)
100+
RCT_EXPORT_METHOD(
101+
setAudioSessionOptions : (NSString *)category mode : (NSString *)mode options : (NSArray *)
102+
options allowHaptics : (BOOL)allowHaptics)
101103
{
102104
[self.audioSessionManager setAudioSessionOptions:category mode:mode options:options allowHaptics:allowHaptics];
103105
}
@@ -132,20 +134,21 @@ - (void)invalidate
132134
[self.notificationManager observeVolumeChanges:(BOOL)enabled];
133135
}
134136

135-
RCT_EXPORT_METHOD(requestRecordingPermissions : (nonnull RCTPromiseResolveBlock)
136-
resolve reject : (nonnull RCTPromiseRejectBlock)reject)
137+
RCT_EXPORT_METHOD(
138+
requestRecordingPermissions : (nonnull RCTPromiseResolveBlock)resolve reject : (nonnull RCTPromiseRejectBlock)
139+
reject)
137140
{
138141
[self.audioSessionManager requestRecordingPermissions:resolve reject:reject];
139142
}
140143

141-
RCT_EXPORT_METHOD(checkRecordingPermissions : (nonnull RCTPromiseResolveBlock)
142-
resolve reject : (nonnull RCTPromiseRejectBlock)reject)
144+
RCT_EXPORT_METHOD(
145+
checkRecordingPermissions : (nonnull RCTPromiseResolveBlock)resolve reject : (nonnull RCTPromiseRejectBlock)reject)
143146
{
144147
[self.audioSessionManager checkRecordingPermissions:resolve reject:reject];
145148
}
146149

147-
RCT_EXPORT_METHOD(getDevicesInfo : (nonnull RCTPromiseResolveBlock)resolve reject : (nonnull RCTPromiseRejectBlock)
148-
reject)
150+
RCT_EXPORT_METHOD(
151+
getDevicesInfo : (nonnull RCTPromiseResolveBlock)resolve reject : (nonnull RCTPromiseRejectBlock)reject)
149152
{
150153
[self.audioSessionManager getDevicesInfo:resolve reject:reject];
151154
}

packages/react-native-audio-api/src/core/AudioBufferQueueSourceNode.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,9 @@ import AudioBuffer from './AudioBuffer';
44
import { RangeError } from '../errors';
55

66
export default class AudioBufferQueueSourceNode extends AudioBufferBaseSourceNode {
7-
public enqueueBuffer(
8-
buffer: AudioBuffer,
9-
isLastBuffer: boolean = false
10-
): void {
11-
(this.node as IAudioBufferQueueSourceNode).enqueueBuffer(
12-
buffer.buffer,
13-
isLastBuffer
7+
public enqueueBuffer(buffer: AudioBuffer): string {
8+
return (this.node as IAudioBufferQueueSourceNode).enqueueBuffer(
9+
buffer.buffer
1410
);
1511
}
1612

packages/react-native-audio-api/src/core/AudioBufferSourceNode.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { IAudioBufferSourceNode } from '../interfaces';
22
import AudioBufferBaseSourceNode from './AudioBufferBaseSourceNode';
33
import AudioBuffer from './AudioBuffer';
44
import { InvalidStateError, RangeError } from '../errors';
5+
import { EventEmptyType } from '../events/types';
56

67
export default class AudioBufferSourceNode extends AudioBufferBaseSourceNode {
78
public get buffer(): AudioBuffer | null {
@@ -79,4 +80,14 @@ export default class AudioBufferSourceNode extends AudioBufferBaseSourceNode {
7980
this.hasBeenStarted = true;
8081
(this.node as IAudioBufferSourceNode).start(when, offset, duration);
8182
}
83+
84+
public override get onEnded(): ((event: EventEmptyType) => void) | undefined {
85+
return super.onEnded as ((event: EventEmptyType) => void) | undefined;
86+
}
87+
88+
public override set onEnded(
89+
callback: ((event: EventEmptyType) => void) | null
90+
) {
91+
super.onEnded = callback;
92+
}
8293
}

packages/react-native-audio-api/src/core/AudioScheduledSourceNode.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { IAudioScheduledSourceNode } from '../interfaces';
22
import AudioNode from './AudioNode';
33
import { InvalidStateError, RangeError } from '../errors';
4-
import { EventEmptyType } from '../events/types';
4+
import { OnEndedEventType } from '../events/types';
55
import { AudioEventEmitter, AudioEventSubscription } from '../events';
66

77
export default class AudioScheduledSourceNode extends AudioNode {
@@ -11,7 +11,7 @@ export default class AudioScheduledSourceNode extends AudioNode {
1111
);
1212

1313
private onendedSubscription?: AudioEventSubscription;
14-
private onEndedCallback?: (event: EventEmptyType) => void;
14+
private onEndedCallback?: (event: OnEndedEventType) => void;
1515

1616
public start(when: number = 0): void {
1717
if (when < 0) {
@@ -44,11 +44,11 @@ export default class AudioScheduledSourceNode extends AudioNode {
4444
(this.node as IAudioScheduledSourceNode).stop(when);
4545
}
4646

47-
public get onEnded(): ((event: EventEmptyType) => void) | undefined {
47+
public get onEnded(): ((event: OnEndedEventType) => void) | undefined {
4848
return this.onEndedCallback;
4949
}
5050

51-
public set onEnded(callback: ((event: EventEmptyType) => void) | null) {
51+
public set onEnded(callback: ((event: OnEndedEventType) => void) | null) {
5252
if (!callback) {
5353
(this.node as IAudioScheduledSourceNode).onEnded = '0';
5454
this.onendedSubscription?.remove();

0 commit comments

Comments
 (0)