From 584d4138fa919dc797dba6fb2da36fe13db99fd6 Mon Sep 17 00:00:00 2001 From: Maciej Makowski Date: Mon, 16 Dec 2024 13:20:03 +0100 Subject: [PATCH 1/5] refactor: refactored types --- .../common/cpp/types/BiquadFilterType.h | 4 ---- .../common/cpp/types/ChannelCountMode.h | 3 --- .../common/cpp/types/ChannelInterpretation.h | 3 --- .../common/cpp/types/ContextState.h | 3 --- .../common/cpp/types/OscillatorType.h | 4 ---- .../common/cpp/types/ParamChangeEventType.h | 11 +++++++++++ 6 files changed, 11 insertions(+), 17 deletions(-) create mode 100644 packages/react-native-audio-api/common/cpp/types/ParamChangeEventType.h diff --git a/packages/react-native-audio-api/common/cpp/types/BiquadFilterType.h b/packages/react-native-audio-api/common/cpp/types/BiquadFilterType.h index a7e763e4..f9e67a66 100644 --- a/packages/react-native-audio-api/common/cpp/types/BiquadFilterType.h +++ b/packages/react-native-audio-api/common/cpp/types/BiquadFilterType.h @@ -1,9 +1,5 @@ #pragma once -#include -#include -#include - namespace audioapi { enum class BiquadFilterType { diff --git a/packages/react-native-audio-api/common/cpp/types/ChannelCountMode.h b/packages/react-native-audio-api/common/cpp/types/ChannelCountMode.h index e2b54975..a91aa930 100644 --- a/packages/react-native-audio-api/common/cpp/types/ChannelCountMode.h +++ b/packages/react-native-audio-api/common/cpp/types/ChannelCountMode.h @@ -1,8 +1,5 @@ #pragma once -#include -#include - namespace audioapi { enum class ChannelCountMode { MAX, CLAMPED_MAX, EXPLICIT }; diff --git a/packages/react-native-audio-api/common/cpp/types/ChannelInterpretation.h b/packages/react-native-audio-api/common/cpp/types/ChannelInterpretation.h index 244d17ec..45dc1ad7 100644 --- a/packages/react-native-audio-api/common/cpp/types/ChannelInterpretation.h +++ b/packages/react-native-audio-api/common/cpp/types/ChannelInterpretation.h @@ -1,8 +1,5 @@ #pragma once -#include -#include - namespace audioapi { enum class ChannelInterpretation { SPEAKERS, DISCRETE }; diff --git a/packages/react-native-audio-api/common/cpp/types/ContextState.h b/packages/react-native-audio-api/common/cpp/types/ContextState.h index 3dd6806f..6a93b062 100644 --- a/packages/react-native-audio-api/common/cpp/types/ContextState.h +++ b/packages/react-native-audio-api/common/cpp/types/ContextState.h @@ -1,8 +1,5 @@ #pragma once -#include -#include - namespace audioapi { enum class ContextState { SUSPENDED, RUNNING, CLOSED }; diff --git a/packages/react-native-audio-api/common/cpp/types/OscillatorType.h b/packages/react-native-audio-api/common/cpp/types/OscillatorType.h index 6dccb995..c25d9e72 100644 --- a/packages/react-native-audio-api/common/cpp/types/OscillatorType.h +++ b/packages/react-native-audio-api/common/cpp/types/OscillatorType.h @@ -1,9 +1,5 @@ #pragma once -#include -#include -#include - namespace audioapi { enum class OscillatorType { SINE, SQUARE, SAWTOOTH, TRIANGLE, CUSTOM }; diff --git a/packages/react-native-audio-api/common/cpp/types/ParamChangeEventType.h b/packages/react-native-audio-api/common/cpp/types/ParamChangeEventType.h new file mode 100644 index 00000000..ffa65509 --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/types/ParamChangeEventType.h @@ -0,0 +1,11 @@ +#pragma once + +namespace audioapi { + +enum class ParamChangeEventType { + START_TIME_EVENT, + END_TIME_EVENT, + START_END_TIME_EVENT, +}; + +} // namespace audioapi From 7c73fa19dad35d3368e53f2b1cf86f0884e4ecf5 Mon Sep 17 00:00:00 2001 From: Maciej Makowski Date: Mon, 16 Dec 2024 13:20:24 +0100 Subject: [PATCH 2/5] feat: added AudioParam missing methods --- apps/fabric-example/ios/Podfile.lock | 4 +- .../cpp/HostObjects/AudioParamHostObject.h | 41 ++- .../common/cpp/core/AudioParam.cpp | 305 +++++++++++++++--- .../common/cpp/core/AudioParam.h | 23 +- .../common/cpp/core/ParamChange.cpp | 46 --- .../common/cpp/core/ParamChangeEvent.cpp | 64 ++++ .../{ParamChange.h => ParamChangeEvent.h} | 18 +- .../src/core/AudioParam.ts | 48 +++ .../react-native-audio-api/src/interfaces.ts | 12 + 9 files changed, 449 insertions(+), 112 deletions(-) delete mode 100644 packages/react-native-audio-api/common/cpp/core/ParamChange.cpp create mode 100644 packages/react-native-audio-api/common/cpp/core/ParamChangeEvent.cpp rename packages/react-native-audio-api/common/cpp/core/{ParamChange.h => ParamChangeEvent.h} (64%) diff --git a/apps/fabric-example/ios/Podfile.lock b/apps/fabric-example/ios/Podfile.lock index 6f04e1a0..7c89d400 100644 --- a/apps/fabric-example/ios/Podfile.lock +++ b/apps/fabric-example/ios/Podfile.lock @@ -1656,7 +1656,7 @@ PODS: - React-logger (= 0.76.0) - React-perflogger (= 0.76.0) - React-utils (= 0.76.0) - - RNAudioAPI (0.3.0-rc2): + - RNAudioAPI (0.3.1): - DoubleConversion - glog - hermes-engine @@ -2152,7 +2152,7 @@ SPEC CHECKSUMS: React-utils: d9624101245ebaab39c9f1bd786132da0b4f27ff ReactCodegen: dbfef1fef26f42c900bb1884fa149d49d501d64d ReactCommon: 429ca28cd813c31359c73ffac6dc24f93347d522 - RNAudioAPI: 837e19b456700c09bfd3d3c0068b82d2a11c68d0 + RNAudioAPI: e7c191e81df9b2a725b8409767130b0af869f9bc RNGestureHandler: 0e5ae8d72ef4afb855e98dcdbe60f27d938abe13 RNReanimated: 006a5d3961bf09c1e96d62ed436e02b2e43b89bb RNScreens: e389d6a6a66a4f0d3662924ecae803073ccce8ec diff --git a/packages/react-native-audio-api/common/cpp/HostObjects/AudioParamHostObject.h b/packages/react-native-audio-api/common/cpp/HostObjects/AudioParamHostObject.h index 066f3a61..d2a2b1e9 100644 --- a/packages/react-native-audio-api/common/cpp/HostObjects/AudioParamHostObject.h +++ b/packages/react-native-audio-api/common/cpp/HostObjects/AudioParamHostObject.h @@ -23,8 +23,11 @@ class AudioParamHostObject : public JsiHostObject { addFunctions( JSI_EXPORT_FUNCTION(AudioParamHostObject, setValueAtTime), JSI_EXPORT_FUNCTION(AudioParamHostObject, linearRampToValueAtTime), - JSI_EXPORT_FUNCTION( - AudioParamHostObject, exponentialRampToValueAtTime)); + JSI_EXPORT_FUNCTION(AudioParamHostObject, exponentialRampToValueAtTime), + JSI_EXPORT_FUNCTION(AudioParamHostObject, setTargetAtTime), + JSI_EXPORT_FUNCTION(AudioParamHostObject, setValueCurveAtTime), + JSI_EXPORT_FUNCTION(AudioParamHostObject, cancelScheduledValues), + JSI_EXPORT_FUNCTION(AudioParamHostObject, cancelAndHoldAtTime)); addSetters(JSI_EXPORT_PROPERTY_SETTER(AudioParamHostObject, value)); } @@ -66,6 +69,40 @@ class AudioParamHostObject : public JsiHostObject { return jsi::Value::undefined(); } + JSI_HOST_FUNCTION(setTargetAtTime) { + auto target = static_cast(args[0].getNumber()); + double startTime = args[1].getNumber(); + double timeConstant = args[2].getNumber(); + param_->setTargetAtTime(target, startTime, timeConstant); + return jsi::Value::undefined(); + } + + JSI_HOST_FUNCTION(setValueCurveAtTime) { + auto values = args[0].getObject(runtime).asArray(runtime); + auto length = static_cast(values.length(runtime)); + auto valuesData = new float[length]; + for (size_t i = 0; i < values.length(runtime); i++) { + valuesData[i] = + static_cast(values.getValueAtIndex(runtime, i).getNumber()); + } + double startTime = args[1].getNumber(); + double duration = args[2].getNumber(); + param_->setValueCurveAtTime(valuesData, length, startTime, duration); + return jsi::Value::undefined(); + } + + JSI_HOST_FUNCTION(cancelScheduledValues) { + double cancelTime = args[0].getNumber(); + param_->cancelScheduledValues(cancelTime); + return jsi::Value::undefined(); + } + + JSI_HOST_FUNCTION(cancelAndHoldAtTime) { + double cancelTime = args[0].getNumber(); + param_->cancelAndHoldAtTime(cancelTime); + return jsi::Value::undefined(); + } + JSI_PROPERTY_SETTER(value) { param_->setValue(static_cast(value.getNumber())); } diff --git a/packages/react-native-audio-api/common/cpp/core/AudioParam.cpp b/packages/react-native-audio-api/common/cpp/core/AudioParam.cpp index 43224b54..57450733 100644 --- a/packages/react-native-audio-api/common/cpp/core/AudioParam.cpp +++ b/packages/react-native-audio-api/common/cpp/core/AudioParam.cpp @@ -1,4 +1,6 @@ #include "AudioParam.h" + +#include "AudioUtils.h" #include "BaseAudioContext.h" namespace audioapi { @@ -12,12 +14,11 @@ AudioParam::AudioParam( defaultValue_(defaultValue), minValue_(minValue), maxValue_(maxValue), - context_(context), - changesQueue_() { + context_(context) { startTime_ = 0; endTime_ = 0; - startValue_ = 0; - endValue_ = 0; + startValue_ = value_; + endValue_ = value_; calculateValue_ = [this](double, double, float, float, double) { return value_; }; @@ -40,94 +41,296 @@ float AudioParam::getMaxValue() const { } void AudioParam::setValue(float value) { - value_ = checkValue(value); + value_ = std::clamp(value, minValue_, maxValue_); } float AudioParam::getValueAtTime(double time) { - if (!changesQueue_.empty()) { - if (endTime_ < time) { - auto change = *changesQueue_.begin(); - startTime_ = change.getStartTime(); - endTime_ = change.getEndTime(); - startValue_ = change.getStartValue(); - endValue_ = change.getEndValue(); - calculateValue_ = change.getCalculateValue(); - changesQueue_.erase(changesQueue_.begin()); + if (endTime_ < time) { + if (!eventsQueue_.empty()) { + auto event = eventsQueue_.begin(); + startTime_ = event->getStartTime(); + endTime_ = event->getEndTime(); + startValue_ = event->getStartValue(); + endValue_ = event->getEndValue(); + calculateValue_ = event->getCalculateValue(); + eventsQueue_.erase(eventsQueue_.begin()); } } - if (startTime_ <= time) { - value_ = - calculateValue_(startTime_, endTime_, startValue_, endValue_, time); - } + setValue(calculateValue_(startTime_, endTime_, startValue_, endValue_, time)); return value_; } -void AudioParam::setValueAtTime(float value, double time) { - value = checkValue(value); - auto calculateValue = [](double, double, float, float endValue, double) { +void AudioParam::setValueAtTime(float value, double startTime) { + // if the start time is less than the end time of last event in the queue, + // then there is no need to schedule any events. + if (startTime <= getQueueEndTime()) { + return; + } + + auto calculateValue = [](double startTime, + double, + float startValue, + float endValue, + double time) { + if (time < startTime) { + return startValue; + } + return endValue; }; - auto paramChange = ParamChange(time, time, value, value, calculateValue); - changesQueue_.insert(paramChange); + auto event = ParamChangeEvent( + startTime, + startTime, + value, + value, + calculateValue, + ParamChangeEventType::START_TIME_EVENT); + updateQueue(event); } -void AudioParam::linearRampToValueAtTime(float value, double time) { - value = checkValue(value); +void AudioParam::linearRampToValueAtTime(float value, double endTime) { + // if the end time is less than the end time of last event in the queue, then + // there is no need to schedule any events. + if (endTime <= getQueueEndTime()) { + return; + } + auto calculateValue = [](double startTime, double endTime, float startValue, float endValue, double time) { - return time >= endTime ? endValue - : startValue + - (endValue - startValue) * (time - startTime) / - (endTime - startTime); + if (time < startTime) { + return startValue; + } + + if (time < endTime) { + return static_cast( + startValue + + (endValue - startValue) * (time - startTime) / (endTime - startTime)); + } + + return endValue; }; - auto paramChange = - ParamChange(getStartTime(), time, getStartValue(), value, calculateValue); - changesQueue_.emplace(paramChange); + auto event = ParamChangeEvent( + getQueueEndTime(), + endTime, + getQueueEndValue(), + value, + calculateValue, + ParamChangeEventType::END_TIME_EVENT); + updateQueue(event); } -void AudioParam::exponentialRampToValueAtTime(float value, double time) { - value = checkValue(value); +void AudioParam::exponentialRampToValueAtTime(float value, double endTime) { + // if the end time is less than the end time of last event in the queue, then + // there is no need to schedule any events. + if (endTime <= getQueueEndTime()) { + return; + } + auto calculateValue = [](double startTime, double endTime, float startValue, float endValue, double time) { - return time >= endTime ? endValue - : startValue * - pow(endValue / startValue, - (time - startTime) / (endTime - startTime)); + if (time < startTime) { + return startValue; + } + + if (time < endTime) { + return static_cast( + startValue * + pow(endValue / startValue, + (time - startTime) / (endTime - startTime))); + } + + return endValue; }; - auto paramChange = - ParamChange(getStartTime(), time, getStartValue(), value, calculateValue); - changesQueue_.emplace(paramChange); + auto event = ParamChangeEvent( + getQueueEndTime(), + endTime, + getQueueEndValue(), + value, + calculateValue, + ParamChangeEventType::END_TIME_EVENT); + updateQueue(event); } -float AudioParam::checkValue(float value) const { - return std::clamp(value, minValue_, maxValue_); +void AudioParam::setTargetAtTime( + float target, + double startTime, + double timeConstant) { + // if the start time is less than the end time of last event in the queue, + // then there is no need to schedule any events. + if (startTime <= getQueueEndTime()) { + return; + } + + auto calculateValue = + [timeConstant, target]( + double startTime, double, float startValue, float, double time) { + if (time < startTime) { + return startValue; + } + + return static_cast( + target + + (startValue - target) * exp(-(time - startTime) / timeConstant)); + }; + + auto event = ParamChangeEvent( + startTime, + startTime, + getQueueEndValue(), + getQueueEndValue(), + calculateValue, + ParamChangeEventType::START_TIME_EVENT); + updateQueue(event); } -double AudioParam::getStartTime() { - if (changesQueue_.empty()) { - return context_->getCurrentTime(); +void AudioParam::setValueCurveAtTime( + const float *values, + int length, + double startTime, + double duration) { + // if the start time is less than the end time of last event in the queue, + // then there is no need to schedule any events. + if (startTime <= getQueueEndTime()) { + return; } - return changesQueue_.rbegin()->getEndTime(); + auto calculateValue = [&values, length]( + double startTime, + double endTime, + float startValue, + float endValue, + double time) { + if (time < startTime) { + return startValue; + } + + if (time < endTime) { + auto k = static_cast(std::floor( + (length - 1) / (endTime - startTime) * (time - startTime))); + auto factor = static_cast( + k - (time - startTime) * (length - 1) / (endTime - startTime)); + + return AudioUtils::linearInterpolate(values, k, k + 1, factor); + } + + return endValue; + }; + + auto event = ParamChangeEvent( + startTime, + startTime + duration, + getQueueEndValue(), + values[length - 1], + calculateValue, + ParamChangeEventType::START_END_TIME_EVENT); + updateQueue(event); +} + +void AudioParam::cancelScheduledValues(double cancelTime) { + bool shouldErase; + + for (auto &event : eventsQueue_) { + shouldErase = false; + if (event.getStartTime() >= cancelTime) { + shouldErase = true; + } + + if (event.getType() == ParamChangeEventType::START_END_TIME_EVENT) { + if (event.getStartTime() < cancelTime && + event.getEndTime() > cancelTime) { + shouldErase = false; + } + } + + if (shouldErase) { + eventsQueue_.erase(event); + } + } +} + +void AudioParam::cancelAndHoldAtTime(double cancelTime) { + for (auto &event : eventsQueue_) { + if (event.getStartTime() >= cancelTime) { + eventsQueue_.erase(event); + } + } + + if (eventsQueue_.empty()) { + endTime_ = cancelTime; + } + + if (!eventsQueue_.empty()) { + auto lastEvent = eventsQueue_.rbegin(); + if (lastEvent->getEndTime() > cancelTime) { + auto event = new ParamChangeEvent( + lastEvent->getStartTime(), + cancelTime, + lastEvent->getStartValue(), + lastEvent->getEndValue(), + lastEvent->getCalculateValue(), + lastEvent->getType()); + eventsQueue_.erase(*lastEvent); + eventsQueue_.insert(*event); + } + } +} + +double AudioParam::getQueueEndTime() { + if (eventsQueue_.empty()) { + return endTime_; + } + + return eventsQueue_.rbegin()->getEndTime(); +} + +float AudioParam::getQueueEndValue() { + if (eventsQueue_.empty()) { + return this->endValue_; + } + + return eventsQueue_.rbegin()->getEndValue(); } -float AudioParam::getStartValue() { - if (changesQueue_.empty()) { - return this->value_; +void AudioParam::updateQueue(ParamChangeEvent &event) { + auto prev = eventsQueue_.rbegin(); + + if (prev == eventsQueue_.rend()) { + eventsQueue_.insert(event); + return; + } + + if (prev->getType() == ParamChangeEventType::START_TIME_EVENT) { + auto prevCalculateValue = prev->getCalculateValue(); + auto prevEndValue = prevCalculateValue( + prev->getStartTime(), + prev->getEndTime(), + prev->getStartValue(), + prev->getEndValue(), + event.getStartTime()); + auto prevEvent = new ParamChangeEvent( + prev->getStartTime(), + event.getStartTime(), + prev->getStartValue(), + prevEndValue, + prev->getCalculateValue(), + ParamChangeEventType::START_TIME_EVENT); + eventsQueue_.erase(*prev); + eventsQueue_.insert(*prevEvent); } - return changesQueue_.rbegin()->getEndValue(); + event.setStartValue(prev->getEndValue()); + eventsQueue_.insert(event); } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/core/AudioParam.h b/packages/react-native-audio-api/common/cpp/core/AudioParam.h index 8604b52c..50e6fbd3 100644 --- a/packages/react-native-audio-api/common/cpp/core/AudioParam.h +++ b/packages/react-native-audio-api/common/cpp/core/AudioParam.h @@ -4,7 +4,8 @@ #include #include -#include "ParamChange.h" +#include "ParamChangeEvent.h" +#include "ParamChangeEventType.h" namespace audioapi { @@ -20,13 +21,23 @@ class AudioParam { [[nodiscard]] float getValue() const; float getValueAtTime(double time); - void setValue(float value); [[nodiscard]] float getDefaultValue() const; [[nodiscard]] float getMinValue() const; [[nodiscard]] float getMaxValue() const; + + void setValue(float value); + void setValueAtTime(float value, double startTime); void linearRampToValueAtTime(float value, double endTime); void exponentialRampToValueAtTime(float value, double endTime); + void setTargetAtTime(float target, double startTime, double timeConstant); + void setValueCurveAtTime( + const float *values, + int length, + double startTime, + double duration); + void cancelScheduledValues(double cancelTime); + void cancelAndHoldAtTime(double cancelTime); private: float value_; @@ -34,7 +45,7 @@ class AudioParam { float minValue_; float maxValue_; BaseAudioContext *context_; - std::set changesQueue_; + std::set eventsQueue_; double startTime_; double endTime_; @@ -42,9 +53,9 @@ class AudioParam { float endValue_; std::function calculateValue_; - float checkValue(float value) const; - double getStartTime(); - float getStartValue(); + double getQueueEndTime(); + float getQueueEndValue(); + void updateQueue(ParamChangeEvent &event); }; } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/core/ParamChange.cpp b/packages/react-native-audio-api/common/cpp/core/ParamChange.cpp deleted file mode 100644 index 0e26fef9..00000000 --- a/packages/react-native-audio-api/common/cpp/core/ParamChange.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "ParamChange.h" - -#include - -namespace audioapi { - -ParamChange::ParamChange( - double startTime, - double endTime, - float startValue, - float endValue, - std::function calculateValue) - : startTime_(startTime), - endTime_(endTime), - startValue_(startValue), - endValue_(endValue), - calculateValue_(std::move(calculateValue)) {} - -double ParamChange::getEndTime() const { - return endTime_; -} - -double ParamChange::getStartTime() const { - return startTime_; -} - -float ParamChange::getEndValue() const { - return endValue_; -} - -float ParamChange::getStartValue() const { - return startValue_; -} - -std::function -ParamChange::getCalculateValue() const { - return calculateValue_; -} - -bool ParamChange::operator<(const ParamChange &other) const { - if (startTime_ != other.startTime_) - return startTime_ < other.startTime_; - return endTime_ < other.endTime_; -} - -} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/core/ParamChangeEvent.cpp b/packages/react-native-audio-api/common/cpp/core/ParamChangeEvent.cpp new file mode 100644 index 00000000..a99e8af9 --- /dev/null +++ b/packages/react-native-audio-api/common/cpp/core/ParamChangeEvent.cpp @@ -0,0 +1,64 @@ +#include "ParamChangeEvent.h" + +#include + +namespace audioapi { + +ParamChangeEvent::ParamChangeEvent( + double startTime, + double endTime, + float startValue, + float endValue, + std::function calculateValue, + ParamChangeEventType type) + : startTime_(startTime), + endTime_(endTime), + startValue_(startValue), + endValue_(endValue), + calculateValue_(std::move(calculateValue)), + type_(type) {} + +double ParamChangeEvent::getEndTime() const { + return endTime_; +} + +double ParamChangeEvent::getStartTime() const { + return startTime_; +} + +float ParamChangeEvent::getEndValue() const { + return endValue_; +} + +float ParamChangeEvent::getStartValue() const { + return startValue_; +} + +std::function +ParamChangeEvent::getCalculateValue() const { + return calculateValue_; +} + +ParamChangeEventType ParamChangeEvent::getType() const { + return type_; +} + +void ParamChangeEvent::setEndTime(double endTime) { + endTime_ = endTime; +} + +void ParamChangeEvent::setStartValue(float startValue) { + startValue_ = startValue; +} + +void ParamChangeEvent::setEndValue(float endValue) { + endValue_ = endValue; +} + +bool ParamChangeEvent::operator<(const ParamChangeEvent &other) const { + if (startTime_ != other.startTime_) + return startTime_ < other.startTime_; + return endTime_ < other.endTime_; +} + +} // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/core/ParamChange.h b/packages/react-native-audio-api/common/cpp/core/ParamChangeEvent.h similarity index 64% rename from packages/react-native-audio-api/common/cpp/core/ParamChange.h rename to packages/react-native-audio-api/common/cpp/core/ParamChangeEvent.h index f431edc4..a4835aec 100644 --- a/packages/react-native-audio-api/common/cpp/core/ParamChange.h +++ b/packages/react-native-audio-api/common/cpp/core/ParamChangeEvent.h @@ -2,18 +2,19 @@ #include #include +#include "ParamChangeEventType.h" namespace audioapi { -class ParamChange { +class ParamChangeEvent { public: - explicit ParamChange( + explicit ParamChangeEvent( double startTime, double endTime, float startValue, float endValue, - std::function - calculateValue); + std::function calculateValue, + ParamChangeEventType type); [[nodiscard]] double getEndTime() const; [[nodiscard]] double getStartTime() const; @@ -21,7 +22,13 @@ class ParamChange { [[nodiscard]] float getStartValue() const; [[nodiscard]] std::function getCalculateValue() const; - bool operator<(const ParamChange &other) const; + [[nodiscard]] ParamChangeEventType getType() const; + + void setEndTime(double endTime); + void setStartValue(float startValue); + void setEndValue(float endValue); + + bool operator<(const ParamChangeEvent &other) const; private: double startTime_; @@ -29,6 +36,7 @@ class ParamChange { float startValue_; float endValue_; std::function calculateValue_; + ParamChangeEventType type_; }; } // namespace audioapi diff --git a/packages/react-native-audio-api/src/core/AudioParam.ts b/packages/react-native-audio-api/src/core/AudioParam.ts index 66bbc28d..768f0b36 100644 --- a/packages/react-native-audio-api/src/core/AudioParam.ts +++ b/packages/react-native-audio-api/src/core/AudioParam.ts @@ -52,4 +52,52 @@ export default class AudioParam { this.audioParam.exponentialRampToValueAtTime(value, endTime); } + + public setTargetAtTime( + target: number, + startTime: number, + timeConstant: number + ): void { + if (startTime < 0) { + throw new RangeError( + `Time must be a finite non-negative number: ${startTime}` + ); + } + + this.audioParam.setTargetAtTime(target, startTime, timeConstant); + } + + public setValueCurveAtTime( + values: number[], + startTime: number, + duration: number + ): void { + if (startTime < 0) { + throw new RangeError( + `Time must be a finite non-negative number: ${startTime}` + ); + } + + this.audioParam.setValueCurveAtTime(values, startTime, duration); + } + + public cancelScheduledValues(cancelTime: number): void { + if (cancelTime < 0) { + throw new RangeError( + `Time must be a finite non-negative number: ${cancelTime}` + ); + } + + this.audioParam.cancelScheduledValues(cancelTime); + } + + public cancelAndHoldAtTime(cancelTime: number): void { + if (cancelTime < 0) { + throw new RangeError( + `Time must be a finite non-negative number: ${cancelTime}` + ); + } + + this.audioParam.cancelAndHoldAtTime(cancelTime); + } } diff --git a/packages/react-native-audio-api/src/interfaces.ts b/packages/react-native-audio-api/src/interfaces.ts index 3d192966..58a7637c 100644 --- a/packages/react-native-audio-api/src/interfaces.ts +++ b/packages/react-native-audio-api/src/interfaces.ts @@ -121,6 +121,18 @@ export interface IAudioParam { setValueAtTime: (value: number, startTime: number) => void; linearRampToValueAtTime: (value: number, endTime: number) => void; exponentialRampToValueAtTime: (value: number, endTime: number) => void; + setTargetAtTime: ( + target: number, + startTime: number, + timeConstant: number + ) => void; + setValueCurveAtTime: ( + values: number[], + startTime: number, + duration: number + ) => void; + cancelScheduledValues: (cancelTime: number) => void; + cancelAndHoldAtTime: (cancelTime: number) => void; } export interface IPeriodicWave {} From 4746c3e748214ed1ed37decb5762b0be5214dbc9 Mon Sep 17 00:00:00 2001 From: Maciej Makowski Date: Mon, 16 Dec 2024 13:21:32 +0100 Subject: [PATCH 3/5] docs: update API coverage docs --- docs/web-audio-coverage.md | 34 ++++++---------------------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/docs/web-audio-coverage.md b/docs/web-audio-coverage.md index 8500448d..ced594cf 100644 --- a/docs/web-audio-coverage.md +++ b/docs/web-audio-coverage.md @@ -7,7 +7,7 @@ Some of the noticeable implementation details that are still in progress or not - Support of different number of channels (current approach in most of the audio-graph nodes assumes working with two channel audio) - Multi-input for each node and input mixing (Although specification suggests that most of the nodes can cave only one input or output, common use-cases proves otherwise). Only node that mixes multiple inputs is `DestinationNode`. -## ✅ Completed (**9** out of 33) +## ✅ Completed (**10** out of 32)
AudioBuffer @@ -36,8 +36,11 @@ Some of the noticeable implementation details that are still in progress or not
StereoPannerNode
+
+ AudioParam +
-## 🚧 In Progress (**4** out of 33) +## 🚧 In Progress (**3** out of 32)
AudioContext @@ -80,28 +83,6 @@ Some of the noticeable implementation details that are still in progress or not
-
- AudioParam - -
- -| Property 🔹/ Method 🔘 | state | -| -------------------------- | ----- | -| 🔹 value | ✅ | -| 🔹 defaultValue | ✅ | -| 🔹 minValue | ✅ | -| 🔹 maxValue | ✅ | -| 🔘 setValueAtTime | ✅ | -| 🔘 linearRampToValueAtTime | ✅ | -| 🔘 setTargetAtTime | ❌ | -| 🔘 setValueCurveAtTime | ❌ | -| 🔘 cancelScheduledValues | ❌ | -| 🔘 cancelAndHoldAtTime | ❌ | - -
- -
-
BaseAudioContext @@ -138,7 +119,7 @@ Some of the noticeable implementation details that are still in progress or not
-## ❌ Not yet available (**20** out of 33) +## ❌ Not yet available (**19** out of 32)
AudioParamMap @@ -197,6 +178,3 @@ Some of the noticeable implementation details that are still in progress or not
OfflineAudioContext
-
- AudioParamMap -
From ee4d66d074ee2031c1f0280a4d02009d30b761ec Mon Sep 17 00:00:00 2001 From: Maciej Makowski Date: Mon, 16 Dec 2024 15:56:57 +0100 Subject: [PATCH 4/5] refactor: switch priority queue to deque --- .../common/cpp/core/AudioParam.cpp | 124 ++++++------------ .../common/cpp/core/AudioParam.h | 4 +- .../common/cpp/core/ParamChangeEvent.cpp | 6 - .../common/cpp/core/ParamChangeEvent.h | 2 - .../common/cpp/types/ParamChangeEventType.h | 8 +- 5 files changed, 50 insertions(+), 94 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/core/AudioParam.cpp b/packages/react-native-audio-api/common/cpp/core/AudioParam.cpp index 57450733..78f11cc4 100644 --- a/packages/react-native-audio-api/common/cpp/core/AudioParam.cpp +++ b/packages/react-native-audio-api/common/cpp/core/AudioParam.cpp @@ -45,16 +45,14 @@ void AudioParam::setValue(float value) { } float AudioParam::getValueAtTime(double time) { - if (endTime_ < time) { - if (!eventsQueue_.empty()) { - auto event = eventsQueue_.begin(); - startTime_ = event->getStartTime(); - endTime_ = event->getEndTime(); - startValue_ = event->getStartValue(); - endValue_ = event->getEndValue(); - calculateValue_ = event->getCalculateValue(); - eventsQueue_.erase(eventsQueue_.begin()); - } + if (endTime_ < time && !eventsQueue_.empty()) { + auto event = eventsQueue_.front(); + startTime_ = event.getStartTime(); + endTime_ = event.getEndTime(); + startValue_ = event.getStartValue(); + endValue_ = event.getEndValue(); + calculateValue_ = event.getCalculateValue(); + eventsQueue_.pop_front(); } setValue(calculateValue_(startTime_, endTime_, startValue_, endValue_, time)); @@ -63,8 +61,6 @@ float AudioParam::getValueAtTime(double time) { } void AudioParam::setValueAtTime(float value, double startTime) { - // if the start time is less than the end time of last event in the queue, - // then there is no need to schedule any events. if (startTime <= getQueueEndTime()) { return; } @@ -84,16 +80,14 @@ void AudioParam::setValueAtTime(float value, double startTime) { auto event = ParamChangeEvent( startTime, startTime, - value, + getQueueEndValue(), value, calculateValue, - ParamChangeEventType::START_TIME_EVENT); + ParamChangeEventType::SET_VALUE); updateQueue(event); } void AudioParam::linearRampToValueAtTime(float value, double endTime) { - // if the end time is less than the end time of last event in the queue, then - // there is no need to schedule any events. if (endTime <= getQueueEndTime()) { return; } @@ -122,13 +116,11 @@ void AudioParam::linearRampToValueAtTime(float value, double endTime) { getQueueEndValue(), value, calculateValue, - ParamChangeEventType::END_TIME_EVENT); + ParamChangeEventType::LINEAR_RAMP); updateQueue(event); } void AudioParam::exponentialRampToValueAtTime(float value, double endTime) { - // if the end time is less than the end time of last event in the queue, then - // there is no need to schedule any events. if (endTime <= getQueueEndTime()) { return; } @@ -158,7 +150,7 @@ void AudioParam::exponentialRampToValueAtTime(float value, double endTime) { getQueueEndValue(), value, calculateValue, - ParamChangeEventType::END_TIME_EVENT); + ParamChangeEventType::EXPONENTIAL_RAMP); updateQueue(event); } @@ -166,8 +158,6 @@ void AudioParam::setTargetAtTime( float target, double startTime, double timeConstant) { - // if the start time is less than the end time of last event in the queue, - // then there is no need to schedule any events. if (startTime <= getQueueEndTime()) { return; } @@ -190,7 +180,7 @@ void AudioParam::setTargetAtTime( getQueueEndValue(), getQueueEndValue(), calculateValue, - ParamChangeEventType::START_TIME_EVENT); + ParamChangeEventType::SET_TARGET); updateQueue(event); } @@ -199,8 +189,6 @@ void AudioParam::setValueCurveAtTime( int length, double startTime, double duration) { - // if the start time is less than the end time of last event in the queue, - // then there is no need to schedule any events. if (startTime <= getQueueEndTime()) { return; } @@ -233,37 +221,30 @@ void AudioParam::setValueCurveAtTime( getQueueEndValue(), values[length - 1], calculateValue, - ParamChangeEventType::START_END_TIME_EVENT); + ParamChangeEventType::SET_VALUE_CURVE); updateQueue(event); } void AudioParam::cancelScheduledValues(double cancelTime) { - bool shouldErase; - - for (auto &event : eventsQueue_) { - shouldErase = false; - if (event.getStartTime() >= cancelTime) { - shouldErase = true; + auto it = eventsQueue_.rbegin(); + while (it->getEndTime() >= cancelTime) { + if (it->getStartTime() >= cancelTime || + it->getType() == ParamChangeEventType::SET_VALUE_CURVE) { + eventsQueue_.pop_back(); } - if (event.getType() == ParamChangeEventType::START_END_TIME_EVENT) { - if (event.getStartTime() < cancelTime && - event.getEndTime() > cancelTime) { - shouldErase = false; - } - } - - if (shouldErase) { - eventsQueue_.erase(event); - } + it++; } } void AudioParam::cancelAndHoldAtTime(double cancelTime) { - for (auto &event : eventsQueue_) { - if (event.getStartTime() >= cancelTime) { - eventsQueue_.erase(event); + auto it = eventsQueue_.rbegin(); + while (it->getEndTime() >= cancelTime) { + if (it->getStartTime() >= cancelTime) { + eventsQueue_.pop_back(); } + + it++; } if (eventsQueue_.empty()) { @@ -273,15 +254,7 @@ void AudioParam::cancelAndHoldAtTime(double cancelTime) { if (!eventsQueue_.empty()) { auto lastEvent = eventsQueue_.rbegin(); if (lastEvent->getEndTime() > cancelTime) { - auto event = new ParamChangeEvent( - lastEvent->getStartTime(), - cancelTime, - lastEvent->getStartValue(), - lastEvent->getEndValue(), - lastEvent->getCalculateValue(), - lastEvent->getType()); - eventsQueue_.erase(*lastEvent); - eventsQueue_.insert(*event); + lastEvent->setEndTime(cancelTime); } } } @@ -291,7 +264,7 @@ double AudioParam::getQueueEndTime() { return endTime_; } - return eventsQueue_.rbegin()->getEndTime(); + return eventsQueue_.back().getEndTime(); } float AudioParam::getQueueEndValue() { @@ -299,38 +272,27 @@ float AudioParam::getQueueEndValue() { return this->endValue_; } - return eventsQueue_.rbegin()->getEndValue(); + return eventsQueue_.back().getEndValue(); } void AudioParam::updateQueue(ParamChangeEvent &event) { - auto prev = eventsQueue_.rbegin(); - - if (prev == eventsQueue_.rend()) { - eventsQueue_.insert(event); - return; - } + if (!eventsQueue_.empty()) { + auto prev = eventsQueue_.back(); + + if (prev.getType() == ParamChangeEventType::SET_TARGET) { + prev.setEndTime(event.getStartTime()); + prev.setEndValue(prev.getCalculateValue()( + prev.getStartTime(), + prev.getEndTime(), + prev.getStartValue(), + prev.getEndValue(), + event.getStartTime())); + } - if (prev->getType() == ParamChangeEventType::START_TIME_EVENT) { - auto prevCalculateValue = prev->getCalculateValue(); - auto prevEndValue = prevCalculateValue( - prev->getStartTime(), - prev->getEndTime(), - prev->getStartValue(), - prev->getEndValue(), - event.getStartTime()); - auto prevEvent = new ParamChangeEvent( - prev->getStartTime(), - event.getStartTime(), - prev->getStartValue(), - prevEndValue, - prev->getCalculateValue(), - ParamChangeEventType::START_TIME_EVENT); - eventsQueue_.erase(*prev); - eventsQueue_.insert(*prevEvent); + event.setStartValue(prev.getEndValue()); } - event.setStartValue(prev->getEndValue()); - eventsQueue_.insert(event); + eventsQueue_.push_back(event); } } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/core/AudioParam.h b/packages/react-native-audio-api/common/cpp/core/AudioParam.h index 50e6fbd3..cc32eeee 100644 --- a/packages/react-native-audio-api/common/cpp/core/AudioParam.h +++ b/packages/react-native-audio-api/common/cpp/core/AudioParam.h @@ -1,7 +1,7 @@ #pragma once +#include #include -#include #include #include "ParamChangeEvent.h" @@ -45,7 +45,7 @@ class AudioParam { float minValue_; float maxValue_; BaseAudioContext *context_; - std::set eventsQueue_; + std::deque eventsQueue_; double startTime_; double endTime_; diff --git a/packages/react-native-audio-api/common/cpp/core/ParamChangeEvent.cpp b/packages/react-native-audio-api/common/cpp/core/ParamChangeEvent.cpp index a99e8af9..3062e39c 100644 --- a/packages/react-native-audio-api/common/cpp/core/ParamChangeEvent.cpp +++ b/packages/react-native-audio-api/common/cpp/core/ParamChangeEvent.cpp @@ -55,10 +55,4 @@ void ParamChangeEvent::setEndValue(float endValue) { endValue_ = endValue; } -bool ParamChangeEvent::operator<(const ParamChangeEvent &other) const { - if (startTime_ != other.startTime_) - return startTime_ < other.startTime_; - return endTime_ < other.endTime_; -} - } // namespace audioapi diff --git a/packages/react-native-audio-api/common/cpp/core/ParamChangeEvent.h b/packages/react-native-audio-api/common/cpp/core/ParamChangeEvent.h index a4835aec..76520039 100644 --- a/packages/react-native-audio-api/common/cpp/core/ParamChangeEvent.h +++ b/packages/react-native-audio-api/common/cpp/core/ParamChangeEvent.h @@ -28,8 +28,6 @@ class ParamChangeEvent { void setStartValue(float startValue); void setEndValue(float endValue); - bool operator<(const ParamChangeEvent &other) const; - private: double startTime_; double endTime_; diff --git a/packages/react-native-audio-api/common/cpp/types/ParamChangeEventType.h b/packages/react-native-audio-api/common/cpp/types/ParamChangeEventType.h index ffa65509..e5bd6b52 100644 --- a/packages/react-native-audio-api/common/cpp/types/ParamChangeEventType.h +++ b/packages/react-native-audio-api/common/cpp/types/ParamChangeEventType.h @@ -3,9 +3,11 @@ namespace audioapi { enum class ParamChangeEventType { - START_TIME_EVENT, - END_TIME_EVENT, - START_END_TIME_EVENT, + LINEAR_RAMP, + EXPONENTIAL_RAMP, + SET_VALUE, + SET_TARGET, + SET_VALUE_CURVE, }; } // namespace audioapi From a800473949936cb81dbf32511ffa44a7bf9100d2 Mon Sep 17 00:00:00 2001 From: Maciej Makowski Date: Mon, 16 Dec 2024 16:24:32 +0100 Subject: [PATCH 5/5] feat: added all AudioParam methods to index.ts --- packages/react-native-audio-api/src/index.ts | 28 ++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/packages/react-native-audio-api/src/index.ts b/packages/react-native-audio-api/src/index.ts index 0d1d3b12..f9dfe049 100644 --- a/packages/react-native-audio-api/src/index.ts +++ b/packages/react-native-audio-api/src/index.ts @@ -244,6 +244,34 @@ export class AudioParam { public exponentialRampToValueAtTime(value: number, endTime: number): void { this.param.exponentialRampToValueAtTime(value, endTime); } + + public setTargetAtTime( + target: number, + startTime: number, + timeConstant: number + ): void { + this.param.setTargetAtTime(target, startTime, timeConstant); + } + + public setValueCurveAtTime( + values: number[], + startTime: number, + duration: number + ): void { + this.param.setValueCurveAtTime( + new Float32Array(values), + startTime, + duration + ); + } + + public cancelScheduledValues(startTime: number): void { + this.param.cancelScheduledValues(startTime); + } + + public cancelAndHoldAtTime(cancelTime: number): void { + this.param.cancelAndHoldAtTime(cancelTime); + } } export class BiquadFilterNode extends AudioNode {