Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions packages/audiodocs/docs/sources/audio-buffer-queue-source-node.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,52 @@ Schedules the `AudioBufferQueueSourceNode` to start playback of enqueued [`Audio
| `when` <Optional /> | `number` | The time, in seconds, at which playback is scheduled to start. If `when` is less than [`AudioContext.currentTime`](/docs/core/base-audio-context#properties) or set to `0.0`, the node starts playing immediately. <br /> Default: `0.0`. |
| `offset` <Optional /> | `number` | The position, in seconds, within the first enqueued audio buffer where playback begins. <br /> The default value is `0.0`, which starts playback from the beginning of the first enqueued buffer. If the offset exceeds the buffer’s [`duration`](/docs/sources/audio-buffer#properties), it’s automatically clamped to the valid range. |

#### Errors:

| Error type | Description |
| :---: | :---- |
| `RangeError` | `when` or `offset` is negative number. |
| `InvalidStateError` | If the node has already been started once. |

#### Returns `undefined`.

#### Errors:

| Error type | Description |
| :---: | :---- |
| `RangeError` | `when` is negative number. |
| `InvalidStateError` | If the node has not been started yet or has already been stopped |

#### Returns `undefined`.

### `pause`

Stops audio immediately. Unlike [`stop()`](/docs/sources/audio-scheduled-source-node#stop), which fully stops playback and clears the queued buffers,
pause() halts the audio while keeping the current playback position, allowing you to resume from the same point later.

#### Errors:

| Error type | Description |
| :---: | :---- |
| `InvalidStateError` | If the node has not been started yet, has already been stopped, or is already paused. |

#### Returns `undefined`.

### `resume`

Resumes audio playback with a specified delay. If no time is given, it resumes immediately.

| Parameter | Type | Description |
| :---: | :---: | :---- |
| `when` <Optional /> | `number` | The time, in seconds, at which playback is scheduled to resume. If `when` is less than [`AudioContext.currentTime`](/docs/core/base-audio-context#properties) or set to `0.0`, the node resumes playing immediately. <br /> Default: `0.0`. |

#### Errors:

| Error type | Description |
| :---: | :---- |
| `RangeError` | `when` is negative number. |
| `InvalidStateError` | If the node has not been started yet, has already been stopped, or is not currently paused. |

#### Returns `undefined`.

## Events
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ReadOnly } from '@site/src/components/Badges';

The `MediaElementAudioSourceNode` is an [`AudioNode`](/docs/core/audio-node) that routes media playback into the audio graph.
On the web it mirrors [`AudioContext.createMediaElementSource()`](https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/createMediaElementSource) and accepts an `HTMLMediaElement`.
On the native it wraps a file-backed source created by the experimental [`<Audio>`](/docs/sources/audio-tag) tag.
On the native it wraps a file-backed source created by the [`<Audio>`](/docs/sources/audio-tag) tag.

## Constructor

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include <audioapi/dsp/WsolaTimeStretcher.h>
#include <audioapi/types/NodeOptions.h>

#include <algorithm>
#include <memory>
#include <string>
#include <utility>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ AudioBufferQueueSourceNodeHostObject::AudioBufferQueueSourceNodeHostObject(
JSI_EXPORT_FUNCTION(AudioBufferQueueSourceNodeHostObject, enqueueBuffer),
JSI_EXPORT_FUNCTION(AudioBufferQueueSourceNodeHostObject, dequeueBuffer),
JSI_EXPORT_FUNCTION(AudioBufferQueueSourceNodeHostObject, clearBuffers),
JSI_EXPORT_FUNCTION(AudioBufferQueueSourceNodeHostObject, pause));
JSI_EXPORT_FUNCTION(AudioBufferQueueSourceNodeHostObject, pause),
JSI_EXPORT_FUNCTION(AudioBufferQueueSourceNodeHostObject, resume));
}

AudioBufferQueueSourceNodeHostObject::~AudioBufferQueueSourceNodeHostObject() {
Expand Down Expand Up @@ -63,6 +64,16 @@ JSI_HOST_FUNCTION_IMPL(AudioBufferQueueSourceNodeHostObject, pause) {
return jsi::Value::undefined();
}

JSI_HOST_FUNCTION_IMPL(AudioBufferQueueSourceNodeHostObject, resume) {
auto handle = node_->handle;
auto event =
[handle, node = bufferQueueSourceNode_, when = args[0].getNumber()](BaseAudioContext &) {
Comment thread
closetcaiman marked this conversation as resolved.
node->resume(when);
};
bufferQueueSourceNode_->scheduleAudioEvent(std::move(event));
return jsi::Value::undefined();
}

JSI_HOST_FUNCTION_IMPL(AudioBufferQueueSourceNodeHostObject, enqueueBuffer) {
auto audioBufferHostObject =
args[0].getObject(runtime).asHostObject<AudioBufferHostObject>(runtime);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <audioapi/HostObjects/AudioParamHostObject.h>
#include <audioapi/HostObjects/sources/AudioBufferBaseSourceNodeHostObject.h>

#include <audioapi/jsi/HostObject.h>
#include <audioapi/utils/Macros.h>
#include <memory>

namespace audioapi {
Expand All @@ -19,11 +21,13 @@ class AudioBufferQueueSourceNodeHostObject : public AudioBufferBaseSourceNodeHos
const BaseAudioBufferSourceOptions &options);

~AudioBufferQueueSourceNodeHostObject() override;
DELETE_COPY_AND_MOVE(AudioBufferQueueSourceNodeHostObject);

JSI_PROPERTY_SETTER_DECL(onBufferEnded);

JSI_HOST_FUNCTION_DECL(start);
JSI_HOST_FUNCTION_DECL(start) override;
JSI_HOST_FUNCTION_DECL(pause);
JSI_HOST_FUNCTION_DECL(resume);
JSI_HOST_FUNCTION_DECL(enqueueBuffer);
JSI_HOST_FUNCTION_DECL(dequeueBuffer);
JSI_HOST_FUNCTION_DECL(clearBuffers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <audioapi/HostObjects/AudioNodeHostObject.h>
#include <audioapi/types/NodeOptions.h>

#include <audioapi/utils/Macros.h>
#include <memory>

namespace audioapi {
Expand All @@ -18,10 +19,11 @@ class AudioScheduledSourceNodeHostObject : public AudioNodeHostObject {
const AudioScheduledSourceNodeOptions &options = AudioScheduledSourceNodeOptions());

~AudioScheduledSourceNodeHostObject() override;
DELETE_COPY_AND_MOVE(AudioScheduledSourceNodeHostObject);

JSI_PROPERTY_SETTER_DECL(onEnded);

JSI_HOST_FUNCTION_DECL(start);
virtual JSI_HOST_FUNCTION_DECL(start);
JSI_HOST_FUNCTION_DECL(stop);

protected:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ void AudioBufferQueueSourceNode::start(double when, double offset) {
vReadIndex_ = static_cast<double>(buffers_.front().second->getSampleRate() * offset);
}

void AudioBufferQueueSourceNode::resume(double when) {
start(when);
}

void AudioBufferQueueSourceNode::pause() {
AudioScheduledSourceNode::stop(0.0);
isPaused_ = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode {
/// @note Audio Thread only
void pause();

void resume(double when);

/// @note Audio Thread only
void enqueueBuffer(
const std::shared_ptr<AudioBuffer> &buffer,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { IAudioBufferQueueSourceNode } from '../jsi-interfaces';
import AudioBufferBaseSourceNode from './AudioBufferBaseSourceNode';
import AudioBuffer from './AudioBuffer';
import { RangeError } from '../errors';
import { InvalidStateError, RangeError } from '../errors';
import type BaseAudioContext from './BaseAudioContext';
import { AudioBufferQueueSourceOptions } from '../types';
import {
AudioBufferQueueSourceOptions,
AudioBufferQueueSourceState,
} from '../types';
import { OnBufferEndEventType } from '../events/types';

export default class AudioBufferQueueSourceNode extends AudioBufferBaseSourceNode {
private onBufferEndedCallback?: (event: OnBufferEndEventType) => void;
private state: AudioBufferQueueSourceState = AudioBufferQueueSourceState.IDLE;

constructor(
context: BaseAudioContext,
Expand Down Expand Up @@ -37,7 +41,7 @@ export default class AudioBufferQueueSourceNode extends AudioBufferBaseSourceNod
(this.node as IAudioBufferQueueSourceNode).clearBuffers();
}

public override start(when: number = 0, offset: number = -1): void {
public override start(when: number = 0, offset: number = 0): void {
if (when < 0) {
throw new RangeError(
`when must be a finite non-negative number: ${when}`
Expand All @@ -50,6 +54,11 @@ export default class AudioBufferQueueSourceNode extends AudioBufferBaseSourceNod
);
}

if (this.state !== AudioBufferQueueSourceState.IDLE) {
throw new InvalidStateError('Cannot call start more than once');
}
this.state = AudioBufferQueueSourceState.PLAYING;

(this.node as IAudioBufferQueueSourceNode).start(when, offset);
}

Expand All @@ -59,6 +68,7 @@ export default class AudioBufferQueueSourceNode extends AudioBufferBaseSourceNod
`when must be a finite non-negative number: ${when}`
);
}
this.state = AudioBufferQueueSourceState.STOPPED;

(this.node as IAudioBufferQueueSourceNode).stop(when);
}
Expand Down Expand Up @@ -89,6 +99,30 @@ export default class AudioBufferQueueSourceNode extends AudioBufferBaseSourceNod
}

public pause(): void {
if (this.state !== AudioBufferQueueSourceState.PLAYING) {
throw new InvalidStateError(
'Cannot call pause when the node is not playing'
);
}
this.state = AudioBufferQueueSourceState.PAUSED;

(this.node as IAudioBufferQueueSourceNode).pause();
}

public resume(when: number = 0): void {
if (when < 0) {
throw new RangeError(
`when must be a finite non-negative number: ${when}`
);
}

if (this.state !== AudioBufferQueueSourceState.PAUSED) {
throw new InvalidStateError(
'Cannot call resume without calling pause first'
);
}
this.state = AudioBufferQueueSourceState.PLAYING;

(this.node as IAudioBufferQueueSourceNode).resume(when);
}
}
1 change: 1 addition & 0 deletions packages/react-native-audio-api/src/jsi-interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ export interface IAudioBufferQueueSourceNode extends IAudioBufferBaseSourceNode
enqueueBuffer: (audioBuffer: IAudioBuffer) => string;
start: (when?: number, offset?: number) => void;
pause: () => void;
resume: (when?: number) => void;

// passing subscriptionId(uint_64 in cpp, string in js) to the cpp
onBufferEnded: string;
Expand Down
7 changes: 7 additions & 0 deletions packages/react-native-audio-api/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,13 @@ interface BaseAudioBufferSourceOptions extends AudioNodeOptions {

export type AudioBufferQueueSourceOptions = BaseAudioBufferSourceOptions;

export enum AudioBufferQueueSourceState {
IDLE,
PLAYING,
PAUSED,
STOPPED,
}

export interface AudioBufferSourceOptions extends BaseAudioBufferSourceOptions {
buffer?: AudioBufferLike;
loop?: boolean;
Expand Down
Loading