feat: 3 IA audio effects — Stutter, RingMod, Chorus
- IA Stutter: rhythmic buffer repeat/glitch (rate, depth, chance, mix) - IA RingMod: ring modulation for metallic/robotic textures (freq, depth, mix) - IA Chorus: chorus/flanger with modulated delay line (rate, depth, feedback, mix) Each effect: schema + forge + box + adapter + processor + editor + manual Registered in: BoxAdapters, DeviceProcessorFactory, DeviceEditorFactory Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
# IA Chorus
|
||||
|
||||
Chorus and flanger effect for thickening and movement.
|
||||
|
||||
---
|
||||
|
||||
## 0. Overview
|
||||
|
||||
_IA Chorus_ uses a modulated delay line to create chorus, flanger, and doubling effects. An LFO modulates the delay time, producing the characteristic shimmering sound.
|
||||
|
||||
---
|
||||
|
||||
## 1. Controls
|
||||
|
||||
### 1.1 Rate
|
||||
|
||||
LFO speed in Hz. Slow (0.1-1Hz) for chorus, fast (1-10Hz) for flanger/vibrato.
|
||||
|
||||
### 1.2 Depth
|
||||
|
||||
LFO modulation depth. Controls how much the delay time varies.
|
||||
|
||||
### 1.3 Feedback
|
||||
|
||||
Amount of output fed back into the delay. Higher values create flanger resonance.
|
||||
|
||||
### 1.4 Mix
|
||||
|
||||
Dry/wet balance.
|
||||
|
||||
---
|
||||
|
||||
_IA audio effect — openDIAW.be / KXKM_
|
||||
@@ -0,0 +1,29 @@
|
||||
# IA RingMod
|
||||
|
||||
Ring modulation effect for metallic and robotic textures.
|
||||
|
||||
---
|
||||
|
||||
## 0. Overview
|
||||
|
||||
_IA RingMod_ multiplies the input signal by a sine wave oscillator, producing sum and difference frequencies. Creates robotic, metallic, bell-like, or alien timbres depending on the modulation frequency.
|
||||
|
||||
---
|
||||
|
||||
## 1. Controls
|
||||
|
||||
### 1.1 Frequency
|
||||
|
||||
Modulation oscillator frequency in Hz. Low values (20-100Hz) create tremolo. Higher values (200-2000Hz) create metallic tones.
|
||||
|
||||
### 1.2 Depth
|
||||
|
||||
Amount of ring modulation applied.
|
||||
|
||||
### 1.3 Mix
|
||||
|
||||
Dry/wet balance.
|
||||
|
||||
---
|
||||
|
||||
_IA audio effect — openDIAW.be / KXKM_
|
||||
@@ -0,0 +1,33 @@
|
||||
# IA Stutter
|
||||
|
||||
Rhythmic buffer repeat and glitch effect.
|
||||
|
||||
---
|
||||
|
||||
## 0. Overview
|
||||
|
||||
_IA Stutter_ captures audio into a buffer and repeats it at a configurable rate. A probability control (Chance) determines whether each cycle stutters or passes through. Creates rhythmic glitch patterns and beat-repeat effects.
|
||||
|
||||
---
|
||||
|
||||
## 1. Controls
|
||||
|
||||
### 1.1 Rate
|
||||
|
||||
Repeat frequency in Hz. Higher = faster stutters.
|
||||
|
||||
### 1.2 Depth
|
||||
|
||||
Intensity of the stutter effect.
|
||||
|
||||
### 1.3 Chance
|
||||
|
||||
Probability (0-100%) that each cycle triggers a stutter. 100% = constant repeat, 50% = half the time.
|
||||
|
||||
### 1.4 Mix
|
||||
|
||||
Dry/wet balance.
|
||||
|
||||
---
|
||||
|
||||
_IA audio effect — openDIAW.be / KXKM_
|
||||
@@ -40,6 +40,9 @@ import {
|
||||
KokoroTtsDeviceBox,
|
||||
PiperDeviceBox,
|
||||
MagentaDeviceBox,
|
||||
StutterDeviceBox,
|
||||
RingModDeviceBox,
|
||||
ChorusDeviceBox,
|
||||
ZeitgeistDeviceBox
|
||||
} from "@opendaw/studio-boxes"
|
||||
import {ArpeggioDeviceEditor} from "@/ui/devices/midi-effects/ArpeggioDeviceEditor.tsx"
|
||||
@@ -84,6 +87,9 @@ import {
|
||||
KokoroTtsDeviceBoxAdapter,
|
||||
PiperDeviceBoxAdapter,
|
||||
MagentaDeviceBoxAdapter,
|
||||
StutterDeviceBoxAdapter,
|
||||
RingModDeviceBoxAdapter,
|
||||
ChorusDeviceBoxAdapter,
|
||||
ZeitgeistDeviceBoxAdapter
|
||||
} from "@opendaw/studio-adapters"
|
||||
import {DelayDeviceEditor} from "@/ui/devices/audio-effects/DelayDeviceEditor.tsx"
|
||||
@@ -117,6 +123,9 @@ import {DattorroReverbDeviceEditor} from "@/ui/devices/audio-effects/DattorroRev
|
||||
import {NeuralAmpDeviceEditor} from "@/ui/devices/audio-effects/NeuralAmpDeviceEditor"
|
||||
import {WaveshaperDeviceEditor} from "@/ui/devices/audio-effects/WaveshaperDeviceEditor"
|
||||
import {SpielwerkDeviceEditor} from "@/ui/devices/midi-effects/SpielwerkDeviceEditor"
|
||||
import {StutterDeviceEditor} from "./audio-effects/StutterDeviceEditor"
|
||||
import {RingModDeviceEditor} from "./audio-effects/RingModDeviceEditor"
|
||||
import {ChorusDeviceEditor} from "./audio-effects/ChorusDeviceEditor"
|
||||
import {WerkstattDeviceEditor} from "@/ui/devices/audio-effects/WerkstattDeviceEditor"
|
||||
import {DroneDeviceEditor} from "./instruments/DroneDeviceEditor"
|
||||
import {GrainDeviceEditor} from "./instruments/GrainDeviceEditor"
|
||||
@@ -377,6 +386,21 @@ export namespace DeviceEditorFactory {
|
||||
adapter={service.project.boxAdapters.adapterFor(box, WaveshaperDeviceBoxAdapter)}
|
||||
deviceHost={deviceHost}/>
|
||||
),
|
||||
visitStutterDeviceBox: (box: StutterDeviceBox) => (
|
||||
<StutterDeviceEditor lifecycle={lifecycle} service={service}
|
||||
adapter={service.project.boxAdapters.adapterFor(box, StutterDeviceBoxAdapter)}
|
||||
deviceHost={deviceHost}/>
|
||||
),
|
||||
visitRingModDeviceBox: (box: RingModDeviceBox) => (
|
||||
<RingModDeviceEditor lifecycle={lifecycle} service={service}
|
||||
adapter={service.project.boxAdapters.adapterFor(box, RingModDeviceBoxAdapter)}
|
||||
deviceHost={deviceHost}/>
|
||||
),
|
||||
visitChorusDeviceBox: (box: ChorusDeviceBox) => (
|
||||
<ChorusDeviceEditor lifecycle={lifecycle} service={service}
|
||||
adapter={service.project.boxAdapters.adapterFor(box, ChorusDeviceBoxAdapter)}
|
||||
deviceHost={deviceHost}/>
|
||||
),
|
||||
visitWerkstattDeviceBox: (box: WerkstattDeviceBox) => (
|
||||
<WerkstattDeviceEditor lifecycle={lifecycle}
|
||||
service={service}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
@use "@/mixins"
|
||||
|
||||
component
|
||||
@include mixins.ControlLayout(1)
|
||||
@@ -0,0 +1,36 @@
|
||||
import css from "./ChorusDeviceEditor.sass?inline"
|
||||
import {Lifecycle} from "@opendaw/lib-std"
|
||||
import {createElement} from "@opendaw/lib-jsx"
|
||||
import {DeviceEditor} from "@/ui/devices/DeviceEditor.tsx"
|
||||
import {MenuItems} from "@/ui/devices/menu-items.ts"
|
||||
import {DeviceHost, ChorusDeviceBoxAdapter} from "@opendaw/studio-adapters"
|
||||
import {ControlBuilder} from "@/ui/devices/ControlBuilder.tsx"
|
||||
import {DevicePeakMeter} from "@/ui/devices/panel/DevicePeakMeter.tsx"
|
||||
import {Html} from "@opendaw/lib-dom"
|
||||
import {IconSymbol} from "@opendaw/studio-enums"
|
||||
import {StudioService} from "@/service/StudioService"
|
||||
|
||||
const className = Html.adoptStyleSheet(css, "ChorusDeviceEditor")
|
||||
|
||||
type Construct = { lifecycle: Lifecycle; service: StudioService; adapter: ChorusDeviceBoxAdapter; deviceHost: DeviceHost }
|
||||
|
||||
export const ChorusDeviceEditor = ({lifecycle, service, adapter, deviceHost}: Construct) => {
|
||||
const params = adapter.namedParameter
|
||||
const {project} = service
|
||||
const {editing, midiLearning} = project
|
||||
const knob = (parameter: typeof params[keyof typeof params]) => ControlBuilder.createKnob({lifecycle, editing, midiLearning, adapter: adapter as any, parameter})
|
||||
return (
|
||||
<DeviceEditor lifecycle={lifecycle} project={project} adapter={adapter as any}
|
||||
populateMenu={parent => MenuItems.forAudioUnitInput(parent, service, deviceHost)}
|
||||
populateControls={() => (
|
||||
<div className={className}>
|
||||
{Object.values(params).map(p => knob(p))}
|
||||
</div>
|
||||
)}
|
||||
icon={IconSymbol.Effects}
|
||||
populateMeter={() => (
|
||||
<DevicePeakMeter lifecycle={lifecycle} receiver={project.liveStreamReceiver} address={adapter.address}/>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
@use "@/mixins"
|
||||
|
||||
component
|
||||
@include mixins.ControlLayout(1)
|
||||
@@ -0,0 +1,36 @@
|
||||
import css from "./RingModDeviceEditor.sass?inline"
|
||||
import {Lifecycle} from "@opendaw/lib-std"
|
||||
import {createElement} from "@opendaw/lib-jsx"
|
||||
import {DeviceEditor} from "@/ui/devices/DeviceEditor.tsx"
|
||||
import {MenuItems} from "@/ui/devices/menu-items.ts"
|
||||
import {DeviceHost, RingModDeviceBoxAdapter} from "@opendaw/studio-adapters"
|
||||
import {ControlBuilder} from "@/ui/devices/ControlBuilder.tsx"
|
||||
import {DevicePeakMeter} from "@/ui/devices/panel/DevicePeakMeter.tsx"
|
||||
import {Html} from "@opendaw/lib-dom"
|
||||
import {IconSymbol} from "@opendaw/studio-enums"
|
||||
import {StudioService} from "@/service/StudioService"
|
||||
|
||||
const className = Html.adoptStyleSheet(css, "RingModDeviceEditor")
|
||||
|
||||
type Construct = { lifecycle: Lifecycle; service: StudioService; adapter: RingModDeviceBoxAdapter; deviceHost: DeviceHost }
|
||||
|
||||
export const RingModDeviceEditor = ({lifecycle, service, adapter, deviceHost}: Construct) => {
|
||||
const params = adapter.namedParameter
|
||||
const {project} = service
|
||||
const {editing, midiLearning} = project
|
||||
const knob = (parameter: typeof params[keyof typeof params]) => ControlBuilder.createKnob({lifecycle, editing, midiLearning, adapter: adapter as any, parameter})
|
||||
return (
|
||||
<DeviceEditor lifecycle={lifecycle} project={project} adapter={adapter as any}
|
||||
populateMenu={parent => MenuItems.forAudioUnitInput(parent, service, deviceHost)}
|
||||
populateControls={() => (
|
||||
<div className={className}>
|
||||
{Object.values(params).map(p => knob(p))}
|
||||
</div>
|
||||
)}
|
||||
icon={IconSymbol.Effects}
|
||||
populateMeter={() => (
|
||||
<DevicePeakMeter lifecycle={lifecycle} receiver={project.liveStreamReceiver} address={adapter.address}/>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
@use "@/mixins"
|
||||
|
||||
component
|
||||
@include mixins.ControlLayout(1)
|
||||
@@ -0,0 +1,36 @@
|
||||
import css from "./StutterDeviceEditor.sass?inline"
|
||||
import {Lifecycle} from "@opendaw/lib-std"
|
||||
import {createElement} from "@opendaw/lib-jsx"
|
||||
import {DeviceEditor} from "@/ui/devices/DeviceEditor.tsx"
|
||||
import {MenuItems} from "@/ui/devices/menu-items.ts"
|
||||
import {DeviceHost, StutterDeviceBoxAdapter} from "@opendaw/studio-adapters"
|
||||
import {ControlBuilder} from "@/ui/devices/ControlBuilder.tsx"
|
||||
import {DevicePeakMeter} from "@/ui/devices/panel/DevicePeakMeter.tsx"
|
||||
import {Html} from "@opendaw/lib-dom"
|
||||
import {IconSymbol} from "@opendaw/studio-enums"
|
||||
import {StudioService} from "@/service/StudioService"
|
||||
|
||||
const className = Html.adoptStyleSheet(css, "StutterDeviceEditor")
|
||||
|
||||
type Construct = { lifecycle: Lifecycle; service: StudioService; adapter: StutterDeviceBoxAdapter; deviceHost: DeviceHost }
|
||||
|
||||
export const StutterDeviceEditor = ({lifecycle, service, adapter, deviceHost}: Construct) => {
|
||||
const params = adapter.namedParameter
|
||||
const {project} = service
|
||||
const {editing, midiLearning} = project
|
||||
const knob = (parameter: typeof params[keyof typeof params]) => ControlBuilder.createKnob({lifecycle, editing, midiLearning, adapter: adapter as any, parameter})
|
||||
return (
|
||||
<DeviceEditor lifecycle={lifecycle} project={project} adapter={adapter as any}
|
||||
populateMenu={parent => MenuItems.forAudioUnitInput(parent, service, deviceHost)}
|
||||
populateControls={() => (
|
||||
<div className={className}>
|
||||
{Object.values(params).map(p => knob(p))}
|
||||
</div>
|
||||
)}
|
||||
icon={IconSymbol.Effects}
|
||||
populateMeter={() => (
|
||||
<DevicePeakMeter lifecycle={lifecycle} receiver={project.liveStreamReceiver} address={adapter.address}/>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -86,6 +86,9 @@ import {
|
||||
AceStepDeviceBox,
|
||||
KokoroTtsDeviceBox,
|
||||
PiperDeviceBox,
|
||||
StutterDeviceBox,
|
||||
RingModDeviceBox,
|
||||
ChorusDeviceBox,
|
||||
ZeitgeistDeviceBox
|
||||
} from "@opendaw/studio-boxes"
|
||||
import {AudioUnitBoxAdapter} from "./audio-unit/AudioUnitBoxAdapter"
|
||||
@@ -139,6 +142,9 @@ import {MagentaDeviceBoxAdapter} from "./devices/instruments/MagentaDeviceBoxAda
|
||||
import {AceStepDeviceBoxAdapter} from "./devices/instruments/AceStepDeviceBoxAdapter"
|
||||
import {KokoroTtsDeviceBoxAdapter} from "./devices/instruments/KokoroTtsDeviceBoxAdapter"
|
||||
import {PiperDeviceBoxAdapter} from "./devices/instruments/PiperDeviceBoxAdapter"
|
||||
import {StutterDeviceBoxAdapter} from "./devices/audio-effects/StutterDeviceBoxAdapter"
|
||||
import {RingModDeviceBoxAdapter} from "./devices/audio-effects/RingModDeviceBoxAdapter"
|
||||
import {ChorusDeviceBoxAdapter} from "./devices/audio-effects/ChorusDeviceBoxAdapter"
|
||||
import {ZeitgeistDeviceBoxAdapter} from "./devices/midi-effects/ZeitgeistDeviceBoxAdapter"
|
||||
import {GrooveShuffleBoxAdapter} from "./grooves/GrooveShuffleBoxAdapter"
|
||||
import {UnknownAudioEffectDeviceBoxAdapter} from "./devices/audio-effects/UnknownAudioEffectDeviceBoxAdapter"
|
||||
@@ -289,6 +295,9 @@ export class BoxAdapters implements Terminable {
|
||||
visitAceStepDeviceBox: (box: AceStepDeviceBox) => new AceStepDeviceBoxAdapter(this.#context, box),
|
||||
visitKokoroTtsDeviceBox: (box: KokoroTtsDeviceBox) => new KokoroTtsDeviceBoxAdapter(this.#context, box),
|
||||
visitPiperDeviceBox: (box: PiperDeviceBox) => new PiperDeviceBoxAdapter(this.#context, box),
|
||||
visitStutterDeviceBox: (box: StutterDeviceBox) => new StutterDeviceBoxAdapter(this.#context, box),
|
||||
visitRingModDeviceBox: (box: RingModDeviceBox) => new RingModDeviceBoxAdapter(this.#context, box),
|
||||
visitChorusDeviceBox: (box: ChorusDeviceBox) => new ChorusDeviceBoxAdapter(this.#context, box),
|
||||
visitZeitgeistDeviceBox: (box: ZeitgeistDeviceBox) => new ZeitgeistDeviceBoxAdapter(this.#context, box)
|
||||
}), `Could not find factory for ${unknownBox}`)
|
||||
}
|
||||
|
||||
@@ -25,6 +25,9 @@ export namespace DeviceManualUrls {
|
||||
export const NeuralAmp = "manuals/devices/audio/neural-amp"
|
||||
export const Waveshaper = "manuals/devices/audio/waveshaper"
|
||||
export const Werkstatt = "manuals/devices/audio/werkstatt"
|
||||
export const Stutter = "manuals/devices/audio/stutter"
|
||||
export const RingMod = "manuals/devices/audio/ringmod"
|
||||
export const Chorus = "manuals/devices/audio/chorus"
|
||||
|
||||
// Instruments
|
||||
export const Apparat = "manuals/devices/instruments/apparat"
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import {Option, StringMapping, UUID, ValueMapping} from "@opendaw/lib-std"
|
||||
import {ChorusDeviceBox} from "@opendaw/studio-boxes"
|
||||
import {Address, BooleanField, Int32Field, PointerField, StringField} from "@opendaw/lib-box"
|
||||
import {Pointers} from "@opendaw/studio-enums"
|
||||
import {AudioEffectDeviceAdapter, DeviceHost, Devices} from "../../DeviceAdapter"
|
||||
import {LabeledAudioOutput} from "../../LabeledAudioOutputsOwner"
|
||||
import {BoxAdaptersContext} from "../../BoxAdaptersContext"
|
||||
import {DeviceManualUrls} from "../../DeviceManualUrls"
|
||||
import {ParameterAdapterSet} from "../../ParameterAdapterSet"
|
||||
import {AudioUnitBoxAdapter} from "../../audio-unit/AudioUnitBoxAdapter"
|
||||
|
||||
export class ChorusDeviceBoxAdapter implements AudioEffectDeviceAdapter {
|
||||
readonly type = "audio-effect"
|
||||
readonly accepts = "audio"
|
||||
readonly manualUrl = DeviceManualUrls.Chorus
|
||||
readonly #context: BoxAdaptersContext
|
||||
readonly #box: ChorusDeviceBox
|
||||
readonly #parametric: ParameterAdapterSet
|
||||
readonly namedParameter
|
||||
constructor(context: BoxAdaptersContext, box: ChorusDeviceBox) {
|
||||
this.#context = context
|
||||
this.#box = box
|
||||
this.#parametric = new ParameterAdapterSet(this.#context)
|
||||
this.namedParameter = this.#wrapParameters(box)
|
||||
}
|
||||
get box(): ChorusDeviceBox {return this.#box}
|
||||
get uuid(): UUID.Bytes {return this.#box.address.uuid}
|
||||
get address(): Address {return this.#box.address}
|
||||
get indexField(): Int32Field {return this.#box.index}
|
||||
get labelField(): StringField {return this.#box.label}
|
||||
get enabledField(): BooleanField {return this.#box.enabled}
|
||||
get minimizedField(): BooleanField {return this.#box.minimized}
|
||||
get host(): PointerField<Pointers.AudioEffectHost> {return this.#box.host}
|
||||
deviceHost(): DeviceHost {
|
||||
return this.#context.boxAdapters.adapterFor(this.#box.host.targetVertex.unwrap("no device-host").box, Devices.isHost)
|
||||
}
|
||||
audioUnitBoxAdapter(): AudioUnitBoxAdapter {return this.deviceHost().audioUnitBoxAdapter()}
|
||||
*labeledAudioOutputs(): Iterable<LabeledAudioOutput> {
|
||||
yield {address: this.address, label: this.labelField.getValue(), children: () => Option.None}
|
||||
}
|
||||
terminate(): void {this.#parametric.terminate()}
|
||||
#wrapParameters(box: import("@opendaw/studio-boxes").ChorusDeviceBox) {
|
||||
return {
|
||||
rate: this.#parametric.createParameter(box.rate, ValueMapping.exponential(0.1, 10.0), StringMapping.numeric({unit: "Hz", fractionDigits: 1}), "Rate"),
|
||||
depth: this.#parametric.createParameter(box.depth, ValueMapping.unipolar(), StringMapping.percent({fractionDigits: 0}), "Depth"),
|
||||
feedback: this.#parametric.createParameter(box.feedback, ValueMapping.unipolar(), StringMapping.percent({fractionDigits: 0}), "Feedback"),
|
||||
mix: this.#parametric.createParameter(box.mix, ValueMapping.unipolar(), StringMapping.percent({fractionDigits: 0}), "Mix"),
|
||||
} as const
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import {Option, StringMapping, UUID, ValueMapping} from "@opendaw/lib-std"
|
||||
import {RingModDeviceBox} from "@opendaw/studio-boxes"
|
||||
import {Address, BooleanField, Int32Field, PointerField, StringField} from "@opendaw/lib-box"
|
||||
import {Pointers} from "@opendaw/studio-enums"
|
||||
import {AudioEffectDeviceAdapter, DeviceHost, Devices} from "../../DeviceAdapter"
|
||||
import {LabeledAudioOutput} from "../../LabeledAudioOutputsOwner"
|
||||
import {BoxAdaptersContext} from "../../BoxAdaptersContext"
|
||||
import {DeviceManualUrls} from "../../DeviceManualUrls"
|
||||
import {ParameterAdapterSet} from "../../ParameterAdapterSet"
|
||||
import {AudioUnitBoxAdapter} from "../../audio-unit/AudioUnitBoxAdapter"
|
||||
|
||||
export class RingModDeviceBoxAdapter implements AudioEffectDeviceAdapter {
|
||||
readonly type = "audio-effect"
|
||||
readonly accepts = "audio"
|
||||
readonly manualUrl = DeviceManualUrls.RingMod
|
||||
readonly #context: BoxAdaptersContext
|
||||
readonly #box: RingModDeviceBox
|
||||
readonly #parametric: ParameterAdapterSet
|
||||
readonly namedParameter
|
||||
constructor(context: BoxAdaptersContext, box: RingModDeviceBox) {
|
||||
this.#context = context
|
||||
this.#box = box
|
||||
this.#parametric = new ParameterAdapterSet(this.#context)
|
||||
this.namedParameter = this.#wrapParameters(box)
|
||||
}
|
||||
get box(): RingModDeviceBox {return this.#box}
|
||||
get uuid(): UUID.Bytes {return this.#box.address.uuid}
|
||||
get address(): Address {return this.#box.address}
|
||||
get indexField(): Int32Field {return this.#box.index}
|
||||
get labelField(): StringField {return this.#box.label}
|
||||
get enabledField(): BooleanField {return this.#box.enabled}
|
||||
get minimizedField(): BooleanField {return this.#box.minimized}
|
||||
get host(): PointerField<Pointers.AudioEffectHost> {return this.#box.host}
|
||||
deviceHost(): DeviceHost {
|
||||
return this.#context.boxAdapters.adapterFor(this.#box.host.targetVertex.unwrap("no device-host").box, Devices.isHost)
|
||||
}
|
||||
audioUnitBoxAdapter(): AudioUnitBoxAdapter {return this.deviceHost().audioUnitBoxAdapter()}
|
||||
*labeledAudioOutputs(): Iterable<LabeledAudioOutput> {
|
||||
yield {address: this.address, label: this.labelField.getValue(), children: () => Option.None}
|
||||
}
|
||||
terminate(): void {this.#parametric.terminate()}
|
||||
#wrapParameters(box: import("@opendaw/studio-boxes").RingModDeviceBox) {
|
||||
return {
|
||||
frequency: this.#parametric.createParameter(box.frequency, ValueMapping.exponential(20.0, 5000.0), StringMapping.numeric({unit: "Hz", fractionDigits: 0, unitPrefix: true}), "Freq"),
|
||||
depth: this.#parametric.createParameter(box.depth, ValueMapping.unipolar(), StringMapping.percent({fractionDigits: 0}), "Depth"),
|
||||
mix: this.#parametric.createParameter(box.mix, ValueMapping.unipolar(), StringMapping.percent({fractionDigits: 0}), "Mix"),
|
||||
} as const
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import {Option, StringMapping, UUID, ValueMapping} from "@opendaw/lib-std"
|
||||
import {StutterDeviceBox} from "@opendaw/studio-boxes"
|
||||
import {Address, BooleanField, Int32Field, PointerField, StringField} from "@opendaw/lib-box"
|
||||
import {Pointers} from "@opendaw/studio-enums"
|
||||
import {AudioEffectDeviceAdapter, DeviceHost, Devices} from "../../DeviceAdapter"
|
||||
import {LabeledAudioOutput} from "../../LabeledAudioOutputsOwner"
|
||||
import {BoxAdaptersContext} from "../../BoxAdaptersContext"
|
||||
import {DeviceManualUrls} from "../../DeviceManualUrls"
|
||||
import {ParameterAdapterSet} from "../../ParameterAdapterSet"
|
||||
import {AudioUnitBoxAdapter} from "../../audio-unit/AudioUnitBoxAdapter"
|
||||
|
||||
export class StutterDeviceBoxAdapter implements AudioEffectDeviceAdapter {
|
||||
readonly type = "audio-effect"
|
||||
readonly accepts = "audio"
|
||||
readonly manualUrl = DeviceManualUrls.Stutter
|
||||
readonly #context: BoxAdaptersContext
|
||||
readonly #box: StutterDeviceBox
|
||||
readonly #parametric: ParameterAdapterSet
|
||||
readonly namedParameter
|
||||
constructor(context: BoxAdaptersContext, box: StutterDeviceBox) {
|
||||
this.#context = context
|
||||
this.#box = box
|
||||
this.#parametric = new ParameterAdapterSet(this.#context)
|
||||
this.namedParameter = this.#wrapParameters(box)
|
||||
}
|
||||
get box(): StutterDeviceBox {return this.#box}
|
||||
get uuid(): UUID.Bytes {return this.#box.address.uuid}
|
||||
get address(): Address {return this.#box.address}
|
||||
get indexField(): Int32Field {return this.#box.index}
|
||||
get labelField(): StringField {return this.#box.label}
|
||||
get enabledField(): BooleanField {return this.#box.enabled}
|
||||
get minimizedField(): BooleanField {return this.#box.minimized}
|
||||
get host(): PointerField<Pointers.AudioEffectHost> {return this.#box.host}
|
||||
deviceHost(): DeviceHost {
|
||||
return this.#context.boxAdapters.adapterFor(this.#box.host.targetVertex.unwrap("no device-host").box, Devices.isHost)
|
||||
}
|
||||
audioUnitBoxAdapter(): AudioUnitBoxAdapter {return this.deviceHost().audioUnitBoxAdapter()}
|
||||
*labeledAudioOutputs(): Iterable<LabeledAudioOutput> {
|
||||
yield {address: this.address, label: this.labelField.getValue(), children: () => Option.None}
|
||||
}
|
||||
terminate(): void {this.#parametric.terminate()}
|
||||
#wrapParameters(box: import("@opendaw/studio-boxes").StutterDeviceBox) {
|
||||
return {
|
||||
rate: this.#parametric.createParameter(box.rate, ValueMapping.exponential(1.0, 64.0), StringMapping.numeric({unit: "Hz", fractionDigits: 1}), "Rate"),
|
||||
depth: this.#parametric.createParameter(box.depth, ValueMapping.unipolar(), StringMapping.percent({fractionDigits: 0}), "Depth"),
|
||||
chance: this.#parametric.createParameter(box.chance, ValueMapping.unipolar(), StringMapping.percent({fractionDigits: 0}), "Chance"),
|
||||
mix: this.#parametric.createParameter(box.mix, ValueMapping.unipolar(), StringMapping.percent({fractionDigits: 0}), "Mix"),
|
||||
} as const
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,9 @@ import {
|
||||
KokoroTtsDeviceBox,
|
||||
PiperDeviceBox,
|
||||
MagentaDeviceBox,
|
||||
StutterDeviceBox,
|
||||
RingModDeviceBox,
|
||||
ChorusDeviceBox,
|
||||
ZeitgeistDeviceBox
|
||||
} from "@opendaw/studio-boxes"
|
||||
import {DelayDeviceProcessor} from "./devices/audio-effects/DelayDeviceProcessor"
|
||||
@@ -80,6 +83,9 @@ import {
|
||||
KokoroTtsDeviceBoxAdapter,
|
||||
PiperDeviceBoxAdapter,
|
||||
MagentaDeviceBoxAdapter,
|
||||
StutterDeviceBoxAdapter,
|
||||
RingModDeviceBoxAdapter,
|
||||
ChorusDeviceBoxAdapter,
|
||||
ZeitgeistDeviceBoxAdapter
|
||||
} from "@opendaw/studio-adapters"
|
||||
import {NopDeviceProcessor} from "./devices/audio-effects/NopDeviceProcessor"
|
||||
@@ -113,6 +119,9 @@ import {TidalDeviceProcessor} from "./devices/audio-effects/TidalDeviceProcessor
|
||||
import {DattorroReverbDeviceProcessor} from "./devices/audio-effects/DattorroReverbDeviceProcessor"
|
||||
import {NeuralAmpDeviceProcessor} from "./devices/audio-effects/NeuralAmpDeviceProcessor"
|
||||
import {WaveshaperDeviceProcessor} from "./devices/audio-effects/WaveshaperDeviceProcessor"
|
||||
import {StutterDeviceProcessor} from "./devices/audio-effects/StutterDeviceProcessor"
|
||||
import {RingModDeviceProcessor} from "./devices/audio-effects/RingModDeviceProcessor"
|
||||
import {ChorusDeviceProcessor} from "./devices/audio-effects/ChorusDeviceProcessor"
|
||||
import {WerkstattDeviceProcessor} from "./devices/audio-effects/WerkstattDeviceProcessor"
|
||||
import {SpielwerkDeviceProcessor} from "./devices/midi-effects/SpielwerkDeviceProcessor"
|
||||
import {ApparatDeviceProcessor} from "./devices/instruments/ApparatDeviceProcessor"
|
||||
@@ -220,6 +229,12 @@ export namespace AudioEffectDeviceProcessorFactory {
|
||||
new NeuralAmpDeviceProcessor(context, context.boxAdapters.adapterFor(box, NeuralAmpDeviceBoxAdapter)),
|
||||
visitWaveshaperDeviceBox: (box: WaveshaperDeviceBox): AudioEffectDeviceProcessor =>
|
||||
new WaveshaperDeviceProcessor(context, context.boxAdapters.adapterFor(box, WaveshaperDeviceBoxAdapter)),
|
||||
visitStutterDeviceBox: (box: StutterDeviceBox): AudioEffectDeviceProcessor =>
|
||||
new StutterDeviceProcessor(context, context.boxAdapters.adapterFor(box, StutterDeviceBoxAdapter)),
|
||||
visitRingModDeviceBox: (box: RingModDeviceBox): AudioEffectDeviceProcessor =>
|
||||
new RingModDeviceProcessor(context, context.boxAdapters.adapterFor(box, RingModDeviceBoxAdapter)),
|
||||
visitChorusDeviceBox: (box: ChorusDeviceBox): AudioEffectDeviceProcessor =>
|
||||
new ChorusDeviceProcessor(context, context.boxAdapters.adapterFor(box, ChorusDeviceBoxAdapter)),
|
||||
visitWerkstattDeviceBox: (box: WerkstattDeviceBox): AudioEffectDeviceProcessor =>
|
||||
new WerkstattDeviceProcessor(context, context.boxAdapters.adapterFor(box, WerkstattDeviceBoxAdapter))
|
||||
}), `Could not create audio-effect for'${box.name}'`)
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import {int, Option, Terminable, UUID} from "@opendaw/lib-std"
|
||||
import {AudioBuffer} from "@opendaw/lib-dsp"
|
||||
import {AudioEffectDeviceAdapter, ChorusDeviceBoxAdapter} from "@opendaw/studio-adapters"
|
||||
import {EngineContext} from "../../EngineContext"
|
||||
import {Block, Processor} from "../../processing"
|
||||
import {PeakBroadcaster} from "../../PeakBroadcaster"
|
||||
import {AutomatableParameter} from "../../AutomatableParameter"
|
||||
import {AudioEffectDeviceProcessor} from "../../AudioEffectDeviceProcessor"
|
||||
import {AudioProcessor} from "../../AudioProcessor"
|
||||
|
||||
const TWO_PI = Math.PI * 2
|
||||
const MAX_DELAY = 4410 // 100ms at 44100
|
||||
|
||||
export class ChorusDeviceProcessor extends AudioProcessor implements AudioEffectDeviceProcessor {
|
||||
static ID: int = 0 | 0
|
||||
readonly #id: int = ChorusDeviceProcessor.ID++
|
||||
readonly #adapter: ChorusDeviceBoxAdapter
|
||||
readonly #output: AudioBuffer
|
||||
readonly #peaks: PeakBroadcaster
|
||||
readonly #delayL = new Float32Array(MAX_DELAY)
|
||||
readonly #delayR = new Float32Array(MAX_DELAY)
|
||||
#writePos: int = 0
|
||||
#lfoPhase = 0.0
|
||||
#source: Option<AudioBuffer> = Option.None
|
||||
rate = 1.5; depth = 0.5; feedback = 0.3; mix = 0.5
|
||||
readonly paramRate: AutomatableParameter<number>
|
||||
readonly paramDepth: AutomatableParameter<number>
|
||||
readonly paramFeedback: AutomatableParameter<number>
|
||||
readonly paramMix: AutomatableParameter<number>
|
||||
constructor(context: EngineContext, adapter: ChorusDeviceBoxAdapter) {
|
||||
super(context)
|
||||
this.#adapter = adapter
|
||||
this.#output = new AudioBuffer()
|
||||
this.#peaks = this.own(new PeakBroadcaster(context.broadcaster, adapter.address))
|
||||
this.paramRate = this.own(this.bindParameter(adapter.namedParameter.rate))
|
||||
this.paramDepth = this.own(this.bindParameter(adapter.namedParameter.depth))
|
||||
this.paramFeedback = this.own(this.bindParameter(adapter.namedParameter.feedback))
|
||||
this.paramMix = this.own(this.bindParameter(adapter.namedParameter.mix))
|
||||
this.ownAll(context.registerProcessor(this), context.audioOutputBufferRegistry.register(adapter.address, this.#output, this.outgoing))
|
||||
this.readAllParameters()
|
||||
}
|
||||
get incoming(): Processor {return this}
|
||||
get outgoing(): Processor {return this}
|
||||
reset(): void {this.#peaks.clear(); this.#output.clear(); this.eventInput.clear(); this.#delayL.fill(0); this.#delayR.fill(0); this.#writePos = 0; this.#lfoPhase = 0}
|
||||
get uuid(): UUID.Bytes {return this.#adapter.uuid}
|
||||
get audioOutput(): AudioBuffer {return this.#output}
|
||||
setAudioSource(source: AudioBuffer): Terminable {this.#source = Option.wrap(source); return {terminate: () => this.#source = Option.None}}
|
||||
index(): int {return this.#adapter.indexField.getValue()}
|
||||
adapter(): AudioEffectDeviceAdapter {return this.#adapter}
|
||||
processAudio({s0, s1}: Block): void {
|
||||
if (this.#source.isEmpty()) return
|
||||
const [inL, inR] = this.#source.unwrap().channels()
|
||||
const [outL, outR] = this.#output.channels()
|
||||
const lfoInc = (this.rate * TWO_PI) / sampleRate
|
||||
const maxDelay = MAX_DELAY - 1
|
||||
for (let i = s0; i < s1; i++) {
|
||||
const lfo = (0.5 + 0.5 * Math.sin(this.#lfoPhase)) * this.depth
|
||||
const delaySamples = 1 + lfo * (maxDelay * 0.5)
|
||||
const readPos = (this.#writePos - delaySamples + MAX_DELAY) % MAX_DELAY
|
||||
const idx = Math.floor(readPos)
|
||||
const frac = readPos - idx
|
||||
const delL = this.#delayL[idx] * (1 - frac) + this.#delayL[(idx + 1) % MAX_DELAY] * frac
|
||||
const delR = this.#delayR[idx] * (1 - frac) + this.#delayR[(idx + 1) % MAX_DELAY] * frac
|
||||
this.#delayL[this.#writePos] = inL[i] + delL * this.feedback
|
||||
this.#delayR[this.#writePos] = inR[i] + delR * this.feedback
|
||||
this.#writePos = (this.#writePos + 1) % MAX_DELAY
|
||||
outL[i] = inL[i] * (1 - this.mix) + delL * this.mix
|
||||
outR[i] = inR[i] * (1 - this.mix) + delR * this.mix
|
||||
this.#lfoPhase = (this.#lfoPhase + lfoInc) % TWO_PI
|
||||
}
|
||||
this.#peaks.process(outL, outR, s0, s1)
|
||||
}
|
||||
parameterChanged(p: AutomatableParameter): void {
|
||||
if (p === this.paramRate) this.rate = this.paramRate.getValue()
|
||||
else if (p === this.paramDepth) this.depth = this.paramDepth.getValue()
|
||||
else if (p === this.paramFeedback) this.feedback = this.paramFeedback.getValue()
|
||||
else if (p === this.paramMix) this.mix = this.paramMix.getValue()
|
||||
}
|
||||
toString(): string {return `{ChorusDevice (${this.#id})}`}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import {int, Option, Terminable, UUID} from "@opendaw/lib-std"
|
||||
import {AudioBuffer} from "@opendaw/lib-dsp"
|
||||
import {AudioEffectDeviceAdapter, RingModDeviceBoxAdapter} from "@opendaw/studio-adapters"
|
||||
import {EngineContext} from "../../EngineContext"
|
||||
import {Block, Processor} from "../../processing"
|
||||
import {PeakBroadcaster} from "../../PeakBroadcaster"
|
||||
import {AutomatableParameter} from "../../AutomatableParameter"
|
||||
import {AudioEffectDeviceProcessor} from "../../AudioEffectDeviceProcessor"
|
||||
import {AudioProcessor} from "../../AudioProcessor"
|
||||
|
||||
const TWO_PI = Math.PI * 2
|
||||
|
||||
export class RingModDeviceProcessor extends AudioProcessor implements AudioEffectDeviceProcessor {
|
||||
static ID: int = 0 | 0
|
||||
readonly #id: int = RingModDeviceProcessor.ID++
|
||||
readonly #adapter: RingModDeviceBoxAdapter
|
||||
readonly #output: AudioBuffer
|
||||
readonly #peaks: PeakBroadcaster
|
||||
#phase = 0.0
|
||||
#source: Option<AudioBuffer> = Option.None
|
||||
freq = 440.0; depth = 1.0; mix = 0.5
|
||||
readonly paramFreq: AutomatableParameter<number>
|
||||
readonly paramDepth: AutomatableParameter<number>
|
||||
readonly paramMix: AutomatableParameter<number>
|
||||
constructor(context: EngineContext, adapter: RingModDeviceBoxAdapter) {
|
||||
super(context)
|
||||
this.#adapter = adapter
|
||||
this.#output = new AudioBuffer()
|
||||
this.#peaks = this.own(new PeakBroadcaster(context.broadcaster, adapter.address))
|
||||
this.paramFreq = this.own(this.bindParameter(adapter.namedParameter.frequency))
|
||||
this.paramDepth = this.own(this.bindParameter(adapter.namedParameter.depth))
|
||||
this.paramMix = this.own(this.bindParameter(adapter.namedParameter.mix))
|
||||
this.ownAll(context.registerProcessor(this), context.audioOutputBufferRegistry.register(adapter.address, this.#output, this.outgoing))
|
||||
this.readAllParameters()
|
||||
}
|
||||
get incoming(): Processor {return this}
|
||||
get outgoing(): Processor {return this}
|
||||
reset(): void {this.#peaks.clear(); this.#output.clear(); this.eventInput.clear(); this.#phase = 0}
|
||||
get uuid(): UUID.Bytes {return this.#adapter.uuid}
|
||||
get audioOutput(): AudioBuffer {return this.#output}
|
||||
setAudioSource(source: AudioBuffer): Terminable {this.#source = Option.wrap(source); return {terminate: () => this.#source = Option.None}}
|
||||
index(): int {return this.#adapter.indexField.getValue()}
|
||||
adapter(): AudioEffectDeviceAdapter {return this.#adapter}
|
||||
processAudio({s0, s1}: Block): void {
|
||||
if (this.#source.isEmpty()) return
|
||||
const [inL, inR] = this.#source.unwrap().channels()
|
||||
const [outL, outR] = this.#output.channels()
|
||||
const phaseInc = (this.freq * TWO_PI) / sampleRate
|
||||
for (let i = s0; i < s1; i++) {
|
||||
const mod = Math.sin(this.#phase) * this.depth
|
||||
const wetL = inL[i] * mod
|
||||
const wetR = inR[i] * mod
|
||||
outL[i] = inL[i] * (1 - this.mix) + wetL * this.mix
|
||||
outR[i] = inR[i] * (1 - this.mix) + wetR * this.mix
|
||||
this.#phase = (this.#phase + phaseInc) % TWO_PI
|
||||
}
|
||||
this.#peaks.process(outL, outR, s0, s1)
|
||||
}
|
||||
parameterChanged(p: AutomatableParameter): void {
|
||||
if (p === this.paramFreq) this.freq = this.paramFreq.getValue()
|
||||
else if (p === this.paramDepth) this.depth = this.paramDepth.getValue()
|
||||
else if (p === this.paramMix) this.mix = this.paramMix.getValue()
|
||||
}
|
||||
toString(): string {return `{RingModDevice (${this.#id})}`}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import {int, Option, Terminable, UUID} from "@opendaw/lib-std"
|
||||
import {AudioBuffer} from "@opendaw/lib-dsp"
|
||||
import {AudioEffectDeviceAdapter, StutterDeviceBoxAdapter} from "@opendaw/studio-adapters"
|
||||
import {EngineContext} from "../../EngineContext"
|
||||
import {Block, Processor} from "../../processing"
|
||||
import {PeakBroadcaster} from "../../PeakBroadcaster"
|
||||
import {AutomatableParameter} from "../../AutomatableParameter"
|
||||
import {AudioEffectDeviceProcessor} from "../../AudioEffectDeviceProcessor"
|
||||
import {AudioProcessor} from "../../AudioProcessor"
|
||||
|
||||
const MAX_BUF = 48000
|
||||
|
||||
export class StutterDeviceProcessor extends AudioProcessor implements AudioEffectDeviceProcessor {
|
||||
static ID: int = 0 | 0
|
||||
readonly #id: int = StutterDeviceProcessor.ID++
|
||||
readonly #adapter: StutterDeviceBoxAdapter
|
||||
readonly #output: AudioBuffer
|
||||
readonly #peaks: PeakBroadcaster
|
||||
readonly #bufL = new Float32Array(MAX_BUF)
|
||||
readonly #bufR = new Float32Array(MAX_BUF)
|
||||
#writePos: int = 0
|
||||
#readPos: number = 0
|
||||
#timer: number = 0
|
||||
#stuttering = false
|
||||
#source: Option<AudioBuffer> = Option.None
|
||||
rate = 8.0; depth = 1.0; chance = 0.5; mix = 1.0
|
||||
readonly paramRate: AutomatableParameter<number>
|
||||
readonly paramDepth: AutomatableParameter<number>
|
||||
readonly paramChance: AutomatableParameter<number>
|
||||
readonly paramMix: AutomatableParameter<number>
|
||||
constructor(context: EngineContext, adapter: StutterDeviceBoxAdapter) {
|
||||
super(context)
|
||||
this.#adapter = adapter
|
||||
this.#output = new AudioBuffer()
|
||||
this.#peaks = this.own(new PeakBroadcaster(context.broadcaster, adapter.address))
|
||||
this.paramRate = this.own(this.bindParameter(adapter.namedParameter.rate))
|
||||
this.paramDepth = this.own(this.bindParameter(adapter.namedParameter.depth))
|
||||
this.paramChance = this.own(this.bindParameter(adapter.namedParameter.chance))
|
||||
this.paramMix = this.own(this.bindParameter(adapter.namedParameter.mix))
|
||||
this.ownAll(context.registerProcessor(this), context.audioOutputBufferRegistry.register(adapter.address, this.#output, this.outgoing))
|
||||
this.readAllParameters()
|
||||
}
|
||||
get incoming(): Processor {return this}
|
||||
get outgoing(): Processor {return this}
|
||||
reset(): void {this.#peaks.clear(); this.#output.clear(); this.eventInput.clear(); this.#writePos = 0; this.#readPos = 0; this.#timer = 0; this.#stuttering = false}
|
||||
get uuid(): UUID.Bytes {return this.#adapter.uuid}
|
||||
get audioOutput(): AudioBuffer {return this.#output}
|
||||
setAudioSource(source: AudioBuffer): Terminable {this.#source = Option.wrap(source); return {terminate: () => this.#source = Option.None}}
|
||||
index(): int {return this.#adapter.indexField.getValue()}
|
||||
adapter(): AudioEffectDeviceAdapter {return this.#adapter}
|
||||
processAudio({s0, s1}: Block): void {
|
||||
if (this.#source.isEmpty()) return
|
||||
const [inL, inR] = this.#source.unwrap().channels()
|
||||
const [outL, outR] = this.#output.channels()
|
||||
const interval = sampleRate / this.rate
|
||||
const bufSize = Math.min(MAX_BUF, Math.floor(interval))
|
||||
for (let i = s0; i < s1; i++) {
|
||||
this.#bufL[this.#writePos] = inL[i]
|
||||
this.#bufR[this.#writePos] = inR[i]
|
||||
this.#writePos = (this.#writePos + 1) % bufSize
|
||||
this.#timer++
|
||||
if (this.#timer >= interval) {
|
||||
this.#timer = 0
|
||||
this.#stuttering = Math.random() < this.chance
|
||||
this.#readPos = 0
|
||||
}
|
||||
if (this.#stuttering) {
|
||||
const idx = Math.floor(this.#readPos) % bufSize
|
||||
const wet = this.depth
|
||||
outL[i] = inL[i] * (1 - this.mix) + this.#bufL[idx] * wet * this.mix
|
||||
outR[i] = inR[i] * (1 - this.mix) + this.#bufR[idx] * wet * this.mix
|
||||
this.#readPos++
|
||||
} else {
|
||||
outL[i] = inL[i]
|
||||
outR[i] = inR[i]
|
||||
}
|
||||
}
|
||||
this.#peaks.process(outL, outR, s0, s1)
|
||||
}
|
||||
parameterChanged(p: AutomatableParameter): void {
|
||||
if (p === this.paramRate) this.rate = this.paramRate.getValue()
|
||||
else if (p === this.paramDepth) this.depth = this.paramDepth.getValue()
|
||||
else if (p === this.paramChance) this.chance = this.paramChance.getValue()
|
||||
else if (p === this.paramMix) this.mix = this.paramMix.getValue()
|
||||
}
|
||||
toString(): string {return `{StutterDevice (${this.#id})}`}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import {BoxSchema} from "@opendaw/lib-box-forge"
|
||||
import {Pointers} from "@opendaw/studio-enums"
|
||||
import {ParameterPointerRules} from "../../std/Defaults"
|
||||
import {DeviceFactory} from "../../std/DeviceFactory"
|
||||
|
||||
export const ChorusDeviceBox: BoxSchema<Pointers> = DeviceFactory.createAudioEffect("ChorusDeviceBox", {
|
||||
10: {
|
||||
type: "float32", name: "rate", pointerRules: ParameterPointerRules,
|
||||
value: 1.5, constraints: {"min": 0.1, "max": 10.0, "scaling": "exponential"}, unit: "Hz"
|
||||
},
|
||||
11: {
|
||||
type: "float32", name: "depth", pointerRules: ParameterPointerRules,
|
||||
value: 0.5, constraints: "unipolar", unit: "%"
|
||||
},
|
||||
12: {
|
||||
type: "float32", name: "feedback", pointerRules: ParameterPointerRules,
|
||||
value: 0.3, constraints: "unipolar", unit: "%"
|
||||
},
|
||||
13: {
|
||||
type: "float32", name: "mix", pointerRules: ParameterPointerRules,
|
||||
value: 0.5, constraints: "unipolar", unit: "%"
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,19 @@
|
||||
import {BoxSchema} from "@opendaw/lib-box-forge"
|
||||
import {Pointers} from "@opendaw/studio-enums"
|
||||
import {ParameterPointerRules} from "../../std/Defaults"
|
||||
import {DeviceFactory} from "../../std/DeviceFactory"
|
||||
|
||||
export const RingModDeviceBox: BoxSchema<Pointers> = DeviceFactory.createAudioEffect("RingModDeviceBox", {
|
||||
10: {
|
||||
type: "float32", name: "frequency", pointerRules: ParameterPointerRules,
|
||||
value: 440.0, constraints: {"min": 20.0, "max": 5000.0, "scaling": "exponential"}, unit: "Hz"
|
||||
},
|
||||
11: {
|
||||
type: "float32", name: "depth", pointerRules: ParameterPointerRules,
|
||||
value: 1.0, constraints: "unipolar", unit: "%"
|
||||
},
|
||||
12: {
|
||||
type: "float32", name: "mix", pointerRules: ParameterPointerRules,
|
||||
value: 0.5, constraints: "unipolar", unit: "%"
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,23 @@
|
||||
import {BoxSchema} from "@opendaw/lib-box-forge"
|
||||
import {Pointers} from "@opendaw/studio-enums"
|
||||
import {ParameterPointerRules} from "../../std/Defaults"
|
||||
import {DeviceFactory} from "../../std/DeviceFactory"
|
||||
|
||||
export const StutterDeviceBox: BoxSchema<Pointers> = DeviceFactory.createAudioEffect("StutterDeviceBox", {
|
||||
10: {
|
||||
type: "float32", name: "rate", pointerRules: ParameterPointerRules,
|
||||
value: 8.0, constraints: {"min": 1.0, "max": 64.0, "scaling": "exponential"}, unit: "Hz"
|
||||
},
|
||||
11: {
|
||||
type: "float32", name: "depth", pointerRules: ParameterPointerRules,
|
||||
value: 1.0, constraints: "unipolar", unit: "%"
|
||||
},
|
||||
12: {
|
||||
type: "float32", name: "chance", pointerRules: ParameterPointerRules,
|
||||
value: 0.5, constraints: "unipolar", unit: "%"
|
||||
},
|
||||
13: {
|
||||
type: "float32", name: "mix", pointerRules: ParameterPointerRules,
|
||||
value: 1.0, constraints: "unipolar", unit: "%"
|
||||
}
|
||||
})
|
||||
@@ -30,6 +30,9 @@ import {WerkstattDeviceBox} from "./audio-effects/WerkstattDeviceBox"
|
||||
import {WerkstattParameterBox} from "./audio-effects/WerkstattParameterBox"
|
||||
import {WerkstattSampleBox} from "./audio-effects/WerkstattSampleBox"
|
||||
import {SpielwerkDeviceBox} from "./midi-effects/SpielwerkDeviceBox"
|
||||
import {StutterDeviceBox} from "./audio-effects/StutterDeviceBox"
|
||||
import {RingModDeviceBox} from "./audio-effects/RingModDeviceBox"
|
||||
import {ChorusDeviceBox} from "./audio-effects/ChorusDeviceBox"
|
||||
import {ApparatDeviceBox} from "./instruments/ApparatDeviceBox"
|
||||
import {MagentaDeviceBox} from "./instruments/MagentaDeviceBox"
|
||||
import {AceStepDeviceBox} from "./instruments/AceStepDeviceBox"
|
||||
@@ -85,5 +88,8 @@ export const DeviceDefinitions = [
|
||||
GrainDeviceBox,
|
||||
GlitchDeviceBox,
|
||||
CircusDeviceBox,
|
||||
HonkDeviceBox
|
||||
HonkDeviceBox,
|
||||
StutterDeviceBox,
|
||||
RingModDeviceBox,
|
||||
ChorusDeviceBox
|
||||
]
|
||||
Reference in New Issue
Block a user