Also removes arguments from the source constructors -- these should be handled by setting exposed properties afterward.master
| @@ -52,34 +52,28 @@ export type NumberMetadata = { | |||
| name: string; | |||
| min: number; | |||
| max: number; | |||
| format?: (value: number) => string; | |||
| }; | |||
| export type RangeMetadata = { | |||
| name: string; | |||
| min: number; | |||
| max: number; | |||
| format?: (value: number) => string; | |||
| }; | |||
| export const exposedMetadataNumber = Symbol("exposedNumber"); | |||
| // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types | |||
| export function exposedNumber(name: string, min: number, max: number) { | |||
| return Reflect.metadata(exposedMetadataNumber, { | |||
| name: name, | |||
| min: min, | |||
| max: max, | |||
| }); | |||
| export function exposedNumber(options: NumberMetadata) { | |||
| return Reflect.metadata(exposedMetadataNumber, options); | |||
| } | |||
| export const exposedRangeMetadata = Symbol("exposedRange"); | |||
| // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types | |||
| export function exposedRange(name: string, min: number, max: number) { | |||
| return Reflect.metadata(exposedRangeMetadata, { | |||
| name: name, | |||
| min: min, | |||
| max: max, | |||
| }); | |||
| export function exposedRange(options: RangeMetadata) { | |||
| return Reflect.metadata(exposedRangeMetadata, options); | |||
| } | |||
| export let context: AudioContext; | |||
| @@ -5,23 +5,25 @@ | |||
| v-for="(metadata, index) in numberProps" | |||
| :key="index" | |||
| > | |||
| {{ metadata.name }} | |||
| <div class="prop-name">{{ metadata.name }}</div> | |||
| <Slider | |||
| v-model="node[metadata.key]" | |||
| :min="metadata.min" | |||
| :max="metadata.max" | |||
| :step="-1" | |||
| :showTooltip="'drag'" | |||
| :format="metadata.format" | |||
| /> | |||
| </div> | |||
| <div class="node-prop" v-for="(metadata, index) in rangeProps" :key="index"> | |||
| {{ metadata.name }} | |||
| <div class="prop-name">{{ metadata.name }}</div> | |||
| <Slider | |||
| v-model="node[metadata.key]" | |||
| :min="metadata.min" | |||
| :max="metadata.max" | |||
| :step="-1" | |||
| :showTooltip="'drag'" | |||
| :format="metadata.format" | |||
| /> | |||
| </div> | |||
| </div> | |||
| @@ -62,12 +64,11 @@ export default class NodeProps extends Vue { | |||
| ); | |||
| if (metadata !== undefined) { | |||
| this.numberProps.push({ | |||
| name: metadata.name, | |||
| key: key, | |||
| min: metadata.min, | |||
| max: metadata.max, | |||
| }); | |||
| const modified: NumberMetadata & { key: string } = Object.assign( | |||
| metadata, | |||
| { key: key } | |||
| ); | |||
| this.numberProps.push(modified); | |||
| } | |||
| }); | |||
| @@ -79,12 +80,11 @@ export default class NodeProps extends Vue { | |||
| ); | |||
| if (metadata !== undefined) { | |||
| this.rangeProps.push({ | |||
| name: metadata.name, | |||
| key: key, | |||
| min: metadata.min, | |||
| max: metadata.max, | |||
| }); | |||
| const modified: RangeMetadata & { key: string } = Object.assign( | |||
| metadata, | |||
| { key: key } | |||
| ); | |||
| this.rangeProps.push(modified); | |||
| } | |||
| }); | |||
| } | |||
| @@ -96,4 +96,8 @@ export default class NodeProps extends Vue { | |||
| margin: 20px; | |||
| user-select: none; | |||
| } | |||
| .prop-name { | |||
| font-size: 150%; | |||
| margin-bottom: 4px; | |||
| } | |||
| </style> | |||
| @@ -4,7 +4,12 @@ export class HighpassFilter extends Filter { | |||
| public kind = "Biquad Filter"; | |||
| private biquad: BiquadFilterNode; | |||
| @exposedNumber("Cutoff", 10, 10000) | |||
| @exposedNumber({ | |||
| name: "Cutoff Frequency", | |||
| min: 10, | |||
| max: 10000, | |||
| format: (value: number) => value + "Hz", | |||
| }) | |||
| public cutoff = 500; | |||
| constructor() { | |||
| @@ -4,7 +4,12 @@ export class BiquadFilter extends Filter { | |||
| public kind = "Biquad Filter"; | |||
| private biquad: BiquadFilterNode; | |||
| @exposedNumber("Cutoff", 10, 10000) | |||
| @exposedNumber({ | |||
| name: "Cutoff Frequency", | |||
| min: 10, | |||
| max: 10000, | |||
| format: (value: number) => value + "Hz", | |||
| }) | |||
| public cutoff = 1000; | |||
| constructor() { | |||
| @@ -5,7 +5,12 @@ export class StereoWidthFilter extends Filter { | |||
| private mono: GainNode; | |||
| private stereo: GainNode; | |||
| @exposedNumber("Width", 0, 1) | |||
| @exposedNumber({ | |||
| name: "Width", | |||
| min: 0, | |||
| max: 1, | |||
| format: (value: number) => Math.round(value * 100) + "%", | |||
| }) | |||
| public width = 1; | |||
| constructor() { | |||
| @@ -1,31 +1,54 @@ | |||
| import { Source } from "./Source"; | |||
| import { exposedNumber, exposedRange, context } from "../audio"; | |||
| import { exposedNumber, context, exposedRange } from "../audio"; | |||
| export class IntervalSource extends Source { | |||
| kind = "Interval"; | |||
| @exposedNumber("Pitch", 0.25, 4) | |||
| @exposedNumber({ | |||
| name: "Pitch", | |||
| min: 0.25, | |||
| max: 4, | |||
| format: (value: number) => Math.round(value * 100) + "%", | |||
| }) | |||
| public pitch = 1; | |||
| @exposedRange("Interval", 0.25, 30) | |||
| public interval: [number, number] = [1, 5]; | |||
| @exposedRange("Panning", -1, 1) | |||
| // | |||
| @exposedNumber({ | |||
| name: "Interval", | |||
| min: 0.25, | |||
| max: 30, | |||
| format: (value: number) => { | |||
| return ( | |||
| value.toLocaleString(undefined, { | |||
| maximumFractionDigits: 2, | |||
| }) + "s" | |||
| ); | |||
| }, | |||
| }) | |||
| public interval: [number, number] = [4, 6]; | |||
| @exposedRange({ | |||
| name: "Left/Right Range", | |||
| min: -1, | |||
| max: 1, | |||
| format: (value: number) => { | |||
| if (value < 0) { | |||
| return Math.round(value * 100) + "L"; | |||
| } else if (value > 0) { | |||
| return Math.round(value * 100) + "R"; | |||
| } else { | |||
| return "0"; | |||
| } | |||
| }, | |||
| }) | |||
| public panning: [number, number] = [-0.2, 0.2]; | |||
| private remaining = 0; | |||
| private started = false; | |||
| constructor( | |||
| name: string, | |||
| minTime: number, | |||
| maxTime: number, | |||
| public randomness = 0 | |||
| ) { | |||
| constructor(name: string) { | |||
| super(name); | |||
| this.interval = [minTime, maxTime]; | |||
| this.setTimer(); | |||
| } | |||
| @@ -7,7 +7,12 @@ export class LoopingSource extends Source { | |||
| private started = false; | |||
| private running = false; | |||
| @exposedNumber("Pitch", 0.25, 4) | |||
| @exposedNumber({ | |||
| name: "Pitch", | |||
| min: 0.25, | |||
| max: 4, | |||
| format: (value: number) => Math.round(value * 100) + "%", | |||
| }) | |||
| public pitch = 1; | |||
| constructor(name: string) { | |||
| @@ -3,7 +3,7 @@ import { LoopingSource } from "./LoopingSource"; | |||
| import { Source } from "./Source"; | |||
| export function makeGlorps(): Source { | |||
| const source: Source = new IntervalSource("Guts", 5, 8); | |||
| const source: Source = new IntervalSource("Guts"); | |||
| source.loadSound("bowels-to-intestines"); | |||
| source.loadSound("intestines-to-bowels"); | |||
| source.loadSound("intestines-to-stomach"); | |||
| @@ -27,7 +27,7 @@ export function makeDigestion(): Source { | |||
| } | |||
| export function makeBurps(): Source { | |||
| const source: Source = new IntervalSource("Burps", 5, 15); | |||
| const source: Source = new IntervalSource("Burps"); | |||
| source.loadSound("belch (1)"); | |||
| source.loadSound("belch (2)"); | |||
| source.loadSound("belch (3)"); | |||
| @@ -51,7 +51,7 @@ export function makeBurps(): Source { | |||
| } | |||
| export function makeGurgles(): Source { | |||
| const source: Source = new IntervalSource("Gurgles", 3, 10); | |||
| const source: Source = new IntervalSource("Gurgles"); | |||
| source.loadSound("gurgles/gurgle (1)"); | |||
| source.loadSound("gurgles/gurgle (2)"); | |||
| source.loadSound("gurgles/gurgle (3)"); | |||
| @@ -20,7 +20,12 @@ export abstract class Source extends Node { | |||
| ); | |||
| } | |||
| @exposedNumber("Volume", 0, 1) | |||
| @exposedNumber({ | |||
| name: "Volume", | |||
| min: 0, | |||
| max: 1, | |||
| format: (value: number) => Math.round(value * 100) + "%", | |||
| }) | |||
| public volume = 1; | |||
| constructor(name: string) { | |||