Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ namespace audioapi {
AudioBufferBaseSourceNodeHostObject::AudioBufferBaseSourceNodeHostObject(
const std::shared_ptr<AudioBufferBaseSourceNode> &node,
const BaseAudioBufferSourceOptions &options)
: AudioScheduledSourceNodeHostObject(node, options), pitchCorrection_(options.pitchCorrection) {
: AudioScheduledSourceNodeHostObject(node, options),
onPositionChangedInterval_(options.onPositionChangedInterval),
pitchCorrection_(options.pitchCorrection) {
auto sourceNode = std::static_pointer_cast<AudioBufferBaseSourceNode>(node_);
detuneParam_ = std::make_shared<AudioParamHostObject>(sourceNode->getDetuneParam());
playbackRateParam_ = std::make_shared<AudioParamHostObject>(sourceNode->getPlaybackRateParam());
onPositionChangedInterval_ = sourceNode->getOnPositionChangedInterval();

addGetters(
JSI_EXPORT_PROPERTY_GETTER(AudioBufferBaseSourceNodeHostObject, detune),
Expand Down Expand Up @@ -61,7 +62,6 @@ JSI_PROPERTY_SETTER_IMPL(AudioBufferBaseSourceNodeHostObject, onPositionChangedI
auto sourceNode = std::static_pointer_cast<AudioBufferBaseSourceNode>(node_);
auto interval = static_cast<int>(value.getNumber());

sourceNode->setOnPositionChangedInterval(static_cast<int>(value.getNumber()));
auto event = [sourceNode, interval](BaseAudioContext &) {
sourceNode->setOnPositionChangedInterval(interval);
};
Expand Down Expand Up @@ -99,8 +99,11 @@ void AudioBufferBaseSourceNodeHostObject::initStretch(int channelCount, float sa
outputLatency_ = std::max(
dsp::sampleFrameToTime(stretch->outputLatency(), node_->getContextSampleRate()), 0.0);

auto event = [sourceNode, stretch](BaseAudioContext &) {
sourceNode->initStretch(stretch);
auto playbackRateBuffer =
std::make_shared<DSPAudioBuffer>(3 * RENDER_QUANTUM_SIZE, channelCount, sampleRate);

auto event = [sourceNode, stretch, playbackRateBuffer](BaseAudioContext &) {
sourceNode->initStretch(stretch, playbackRateBuffer);
};
sourceNode->scheduleAudioEvent(std::move(event));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,12 +169,10 @@ void AudioBufferSourceNodeHostObject::setBuffer(const std::shared_ptr<AudioBuffe
auto audioBufferSourceNode = std::static_pointer_cast<AudioBufferSourceNode>(node_);

std::shared_ptr<AudioBuffer> copiedBuffer;
std::shared_ptr<DSPAudioBuffer> playbackRateBuffer;
std::shared_ptr<DSPAudioBuffer> audioBuffer;

if (buffer == nullptr) {
copiedBuffer = nullptr;
playbackRateBuffer = nullptr;
audioBuffer = std::make_shared<DSPAudioBuffer>(
RENDER_QUANTUM_SIZE, 1, audioBufferSourceNode->getContextSampleRate());
} else {
Expand All @@ -191,20 +189,15 @@ void AudioBufferSourceNodeHostObject::setBuffer(const std::shared_ptr<AudioBuffe
copiedBuffer = std::make_shared<AudioBuffer>(*buffer);
}

playbackRateBuffer = std::make_shared<DSPAudioBuffer>(
3 * RENDER_QUANTUM_SIZE,
copiedBuffer->getNumberOfChannels(),
audioBufferSourceNode->getContextSampleRate());
audioBuffer = std::make_shared<DSPAudioBuffer>(
RENDER_QUANTUM_SIZE,
copiedBuffer->getNumberOfChannels(),
audioBufferSourceNode->getContextSampleRate());
}

auto event =
[audioBufferSourceNode, copiedBuffer, playbackRateBuffer, audioBuffer](BaseAudioContext &) {
audioBufferSourceNode->setBuffer(copiedBuffer, playbackRateBuffer, audioBuffer);
};
auto event = [audioBufferSourceNode, copiedBuffer, audioBuffer](BaseAudioContext &) {
audioBufferSourceNode->setBuffer(copiedBuffer, audioBuffer);
};
audioBufferSourceNode->scheduleAudioEvent(std::move(event));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,8 @@ AudioBufferBaseSourceNode::AudioBufferBaseSourceNode(
const std::shared_ptr<BaseAudioContext> &context,
const BaseAudioBufferSourceOptions &options)
: AudioScheduledSourceNode(context, options),
vReadIndex_(0.0),
pitchCorrection_(options.pitchCorrection),
playbackRateBuffer_( // TODO refactor init
std::make_shared<DSPAudioBuffer>(
RENDER_QUANTUM_SIZE * 3,
channelCount_,
context->getSampleRate())),
detuneParam_(
std::make_shared<AudioParam>(
options.detune,
Expand All @@ -33,13 +29,15 @@ AudioBufferBaseSourceNode::AudioBufferBaseSourceNode(
options.playbackRate,
MOST_NEGATIVE_SINGLE_FLOAT,
MOST_POSITIVE_SINGLE_FLOAT,
context)),
vReadIndex_(0.0),
onPositionChangedInterval_(static_cast<int>(context->getSampleRate() * 0.1)) {}
context)) {
setOnPositionChangedInterval(options.onPositionChangedInterval);
}

void AudioBufferBaseSourceNode::initStretch(
const std::shared_ptr<signalsmith::stretch::SignalsmithStretch<float>> &stretch) {
const std::shared_ptr<signalsmith::stretch::SignalsmithStretch<float>> &stretch,
const std::shared_ptr<DSPAudioBuffer> &playbackRateBuffer) {
stretch_ = stretch;
playbackRateBuffer_ = playbackRateBuffer;
}

std::shared_ptr<AudioParam> AudioBufferBaseSourceNode::getDetuneParam() const {
Expand All @@ -55,29 +53,45 @@ void AudioBufferBaseSourceNode::setOnPositionChangedCallbackId(uint64_t callback
}

void AudioBufferBaseSourceNode::setOnPositionChangedInterval(int interval) {
onPositionChangedInterval_ =
onPositionChangedIntervalInFrames_ =
static_cast<int>(getContextSampleRate() * static_cast<float>(interval) / 1000);
}

int AudioBufferBaseSourceNode::getOnPositionChangedInterval() const {
return onPositionChangedInterval_;
}

void AudioBufferBaseSourceNode::unregisterOnPositionChangedCallback(uint64_t callbackId) {
audioEventHandlerRegistry_->unregisterHandler(AudioEvent::POSITION_CHANGED, callbackId);
}

std::shared_ptr<DSPAudioBuffer> AudioBufferBaseSourceNode::processNode(
const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
int framesToProcess) {
if (isEmpty()) {
processingBuffer->zero();
return processingBuffer;
}

if (!pitchCorrection_) {
processWithoutPitchCorrection(processingBuffer, framesToProcess);
} else {
processWithPitchCorrection(processingBuffer, framesToProcess);
}

handleStopScheduled();

return processingBuffer;
}

void AudioBufferBaseSourceNode::sendOnPositionChangedEvent() {
if (onPositionChangedCallbackId_ != 0 && onPositionChangedTime_ > onPositionChangedInterval_) {
if (onPositionChangedCallbackId_ != 0 &&
onPositionChangedTimeInFrames_ > onPositionChangedIntervalInFrames_) {
std::unordered_map<std::string, EventValue> body = {{"value", getCurrentPosition()}};

audioEventHandlerRegistry_->invokeHandlerWithEventBody(
AudioEvent::POSITION_CHANGED, onPositionChangedCallbackId_, body);

onPositionChangedTime_ = 0;
onPositionChangedTimeInFrames_ = 0;
}

onPositionChangedTime_ += RENDER_QUANTUM_SIZE;
onPositionChangedTimeInFrames_ += RENDER_QUANTUM_SIZE;
}

void AudioBufferBaseSourceNode::processWithPitchCorrection(
Expand All @@ -87,7 +101,7 @@ void AudioBufferBaseSourceNode::processWithPitchCorrection(
size_t offsetLength = 0;

std::shared_ptr<BaseAudioContext> context = context_.lock();
if (context == nullptr) {
if (context == nullptr || playbackRateBuffer_ == nullptr) {
processingBuffer->zero();
return;
}
Expand Down Expand Up @@ -140,8 +154,10 @@ void AudioBufferBaseSourceNode::processWithoutPitchCorrection(
processingBuffer->zero();
return;
}

auto computedPlaybackRate =
getComputedPlaybackRateValue(framesToProcess, context->getCurrentTime());

updatePlaybackInfo(
processingBuffer,
framesToProcess,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ class AudioBufferBaseSourceNode : public AudioScheduledSourceNode {
const std::shared_ptr<BaseAudioContext> &context,
const BaseAudioBufferSourceOptions &options);

/// @note JS Thread only
void initStretch(const std::shared_ptr<signalsmith::stretch::SignalsmithStretch<float>> &stretch);
/// @note Audio Thread only
void initStretch(
const std::shared_ptr<signalsmith::stretch::SignalsmithStretch<float>> &stretch,
const std::shared_ptr<DSPAudioBuffer> &playbackRateBuffer);

[[nodiscard]] std::shared_ptr<AudioParam> getDetuneParam() const;
[[nodiscard]] std::shared_ptr<AudioParam> getPlaybackRateParam() const;
Expand All @@ -29,31 +31,46 @@ class AudioBufferBaseSourceNode : public AudioScheduledSourceNode {
/// @note Audio Thread only
void setOnPositionChangedInterval(int interval);

/// TODO remove and refactor
[[nodiscard]] int getOnPositionChangedInterval() const;

void unregisterOnPositionChangedCallback(uint64_t callbackId);

protected:
// pitch correction
const bool pitchCorrection_;
// internal helper
double vReadIndex_;

std::shared_ptr<DSPAudioBuffer> processNode(
const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
int framesToProcess) final;

virtual double getCurrentPosition() const = 0;

virtual bool isEmpty() const = 0;

virtual void processWithoutInterpolation(
const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
size_t startOffset,
size_t offsetLength,
float playbackRate) = 0;

virtual void processWithInterpolation(
const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
size_t startOffset,
size_t offsetLength,
float playbackRate) = 0;

// pitch correction
private:
// pitch correction parameters
// late init to avoid unnecessary allocation when pitch correction is not used.
const bool pitchCorrection_;
std::shared_ptr<signalsmith::stretch::SignalsmithStretch<float>> stretch_;
std::shared_ptr<DSPAudioBuffer> playbackRateBuffer_;

// k-rate params
const std::shared_ptr<AudioParam> detuneParam_;
const std::shared_ptr<AudioParam> playbackRateParam_;

// internal helper
double vReadIndex_;

uint64_t onPositionChangedCallbackId_ = 0; // 0 means no callback
int onPositionChangedInterval_;
int onPositionChangedTime_ = 0;

virtual double getCurrentPosition() const = 0;
int onPositionChangedIntervalInFrames_;
int onPositionChangedTimeInFrames_ = 0;

void sendOnPositionChangedEvent();

Expand All @@ -65,18 +82,6 @@ class AudioBufferBaseSourceNode : public AudioScheduledSourceNode {
int framesToProcess);

float getComputedPlaybackRateValue(int framesToProcess, double time);

virtual void processWithoutInterpolation(
const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
size_t startOffset,
size_t offsetLength,
float playbackRate) = 0;

virtual void processWithInterpolation(
const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
size_t startOffset,
size_t offsetLength,
float playbackRate) = 0;
};

} // namespace audioapi
Original file line number Diff line number Diff line change
Expand Up @@ -132,26 +132,6 @@ void AudioBufferQueueSourceNode::unregisterOnBufferEndedCallback(uint64_t callba
audioEventHandlerRegistry_->unregisterHandler(AudioEvent::BUFFER_ENDED, callbackId);
}

std::shared_ptr<DSPAudioBuffer> AudioBufferQueueSourceNode::processNode(
const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
int framesToProcess) {
// no audio data to fill, zero the output and return.
if (buffers_.empty()) {
processingBuffer->zero();
return processingBuffer;
}

if (!pitchCorrection_) {
processWithoutPitchCorrection(processingBuffer, framesToProcess);
} else {
processWithPitchCorrection(processingBuffer, framesToProcess);
}

handleStopScheduled();

return processingBuffer;
}

double AudioBufferQueueSourceNode::getCurrentPosition() const {
return dsp::sampleFrameToTime(static_cast<int>(vReadIndex_), getContextSampleRate()) +
playedBuffersDuration_;
Expand All @@ -171,6 +151,10 @@ void AudioBufferQueueSourceNode::sendOnBufferEndedEvent(size_t bufferId, bool is
* Helper functions
*/

bool AudioBufferQueueSourceNode::isEmpty() const {
return buffers_.empty();
}

void AudioBufferQueueSourceNode::processWithoutInterpolation(
const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
size_t startOffset,
Expand Down Expand Up @@ -302,8 +286,8 @@ void AudioBufferQueueSourceNode::processWithInterpolation(
break;
}

context->getGraphManager()->addAudioBufferForDestruction(std::move(buffer));
vReadIndex_ = vReadIndex_ - buffer->getSize();
context->getGraphManager()->addAudioBufferForDestruction(std::move(buffer));
data = buffers_.front();
bufferId = data.first;
buffer = data.second;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,24 @@ class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode {
void unregisterOnBufferEndedCallback(uint64_t callbackId);

protected:
std::shared_ptr<DSPAudioBuffer> processNode(
const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
int framesToProcess) override;

double getCurrentPosition() const override;

void sendOnBufferEndedEvent(size_t bufferId, bool isLastBufferInQueue);

bool isEmpty() const final;

void processWithoutInterpolation(
const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
size_t startOffset,
size_t offsetLength,
float playbackRate) final;

void processWithInterpolation(
const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
size_t startOffset,
size_t offsetLength,
float playbackRate) final;

private:
// User provided buffers
std::list<std::pair<size_t, std::shared_ptr<AudioBuffer>>> buffers_{};
Expand All @@ -67,18 +77,6 @@ class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode {
double playedBuffersDuration_ = 0;

uint64_t onBufferEndedCallbackId_ = 0; // 0 means no callback

void processWithoutInterpolation(
const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
size_t startOffset,
size_t offsetLength,
float playbackRate) override;

void processWithInterpolation(
const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
size_t startOffset,
size_t offsetLength,
float playbackRate) override;
};

} // namespace audioapi
Loading
Loading