This commit is contained in:
2023-12-01 11:16:16 +01:00
parent 31adc17a36
commit a34f1a33eb
7 changed files with 117 additions and 172 deletions

View File

@ -1581,7 +1581,9 @@ export class UserAPI {
// Low Frequency Oscillators // Low Frequency Oscillators
// ============================================================= // =============================================================
line = (start: number, end: number, step: number = 1): number[] => { public range = (v: number, a: number, b: number): number => v * (b - a) + a;
public line = (start: number, end: number, step: number = 1): number[] => {
/** /**
* Returns an array of values between start and end, with a given step. * Returns an array of values between start and end, with a given step.
* *
@ -1603,7 +1605,11 @@ export class UserAPI {
return result; return result;
}; };
sine = (freq: number = 1, times: number = 1, offset: number = 0): number => { public sine = (
freq: number = 1,
times: number = 1,
offset: number = 0,
): number => {
/** /**
* Returns a sine wave between -1 and 1. * Returns a sine wave between -1 and 1.
* *
@ -1617,7 +1623,11 @@ export class UserAPI {
); );
}; };
usine = (freq: number = 1, times: number = 1, offset: number = 0): number => { public usine = (
freq: number = 1,
times: number = 1,
offset: number = 0,
): number => {
/** /**
* Returns a sine wave between 0 and 1. * Returns a sine wave between 0 and 1.
* *

View File

@ -4,7 +4,8 @@ import { tryEvaluate } from "./Evaluator";
import { getAudioContext } from "superdough"; import { getAudioContext } from "superdough";
// @ts-ignore // @ts-ignore
import "zyklus"; import "zyklus";
const zeroPad = (num: number, places: number) => String(num).padStart(places, "0"); const zeroPad = (num: number, places: number) =>
String(num).padStart(places, "0");
export interface TimePosition { export interface TimePosition {
/** /**
@ -61,7 +62,10 @@ export class Clock {
this.running = true; this.running = true;
this.deadline = 0; this.deadline = 0;
this.timeviewer = document.getElementById("timeviewer")!; this.timeviewer = document.getElementById("timeviewer")!;
this.clock = getAudioContext().createClock(this.clockCallback, this.pulse_duration) this.clock = getAudioContext().createClock(
this.clockCallback,
this.pulse_duration,
);
} }
clockCallback = (time: number, duration: number, tick: number) => { clockCallback = (time: number, duration: number, tick: number) => {
@ -89,7 +93,6 @@ export class Clock {
} }
// Implement TransportNode clock callback and update clock info with it // Implement TransportNode clock callback and update clock info with it
}; };
convertTicksToTimeposition(ticks: number): TimePosition { convertTicksToTimeposition(ticks: number): TimePosition {
@ -183,13 +186,13 @@ export class Clock {
this.app.audioContext.resume(); this.app.audioContext.resume();
this.running = true; this.running = true;
this.app.api.MidiConnection.sendStartMessage(); this.app.api.MidiConnection.sendStartMessage();
this.clock.start() this.clock.start();
} }
public pause(): void { public pause(): void {
this.running = false; this.running = false;
this.app.api.MidiConnection.sendStopMessage(); this.app.api.MidiConnection.sendStopMessage();
this.clock.pause() this.clock.pause();
} }
public stop(): void { public stop(): void {

View File

@ -1,68 +0,0 @@
import { tryEvaluate } from "./Evaluator";
const zeroPad = (num, places) => String(num).padStart(places, "0");
export class TransportNode extends AudioWorkletNode {
constructor(context, options, application) {
super(context, "transport", options);
this.app = application;
this.port.addEventListener("message", this.handleMessage);
this.port.start();
this.timeviewer = document.getElementById("timeviewer");
}
/** @type {(this: MessagePort, ev: MessageEvent<any>) => any} */
handleMessage = (message) => {
if (message.data) {
if (message.data.type === "bang") {
if (this.app.clock.running) {
if (this.app.settings.send_clock) {
this.app.api.MidiConnection.sendMidiClock();
}
const futureTimeStamp = this.app.clock.convertTicksToTimeposition(
this.app.clock.tick,
);
this.app.clock.time_position = futureTimeStamp;
if (futureTimeStamp.pulse % this.app.clock.ppqn == 0) {
this.timeviewer.innerHTML = `${zeroPad(futureTimeStamp.bar, 2)}:${
futureTimeStamp.beat + 1
} / ${this.app.clock.bpm}`;
}
if (this.app.exampleIsPlaying) {
tryEvaluate(this.app, this.app.example_buffer);
} else {
tryEvaluate(this.app, this.app.global_buffer);
}
this.app.clock.incrementTick(message.data.bpm);
}
}
}
};
start() {
this.port.postMessage({ type: "start" });
}
pause() {
this.port.postMessage({ type: "pause" });
}
resume() {
this.port.postMessage({ type: "resume" });
}
setBPM(bpm) {
this.port.postMessage({ type: "bpm", value: bpm });
}
setPPQN(ppqn) {
this.port.postMessage({ type: "ppqn", value: ppqn });
}
setNudge(nudge) {
this.port.postMessage({ type: "nudge", value: nudge });
}
stop() {
this.port.postMessage({ type: "stop" });
}
}

View File

@ -1,5 +1,11 @@
import { type Editor } from "../main"; import { type Editor } from "../main";
import { freqToMidi, chord as parseChord, noteNameToMidi, resolvePitchBend, safeScale } from "zifferjs"; import {
freqToMidi,
chord as parseChord,
noteNameToMidi,
resolvePitchBend,
safeScale,
} from "zifferjs";
import { SkipEvent } from "./SkipEvent"; import { SkipEvent } from "./SkipEvent";
export type EventOperation<T> = (instance: T, ...args: any[]) => void; export type EventOperation<T> = (instance: T, ...args: any[]) => void;
@ -311,16 +317,16 @@ export abstract class AudibleEvent extends AbstractEvent {
return this; return this;
}; };
protected updateValue<T>( protected updateValue<T>(key: string, value: T | T[] | null): this {
key: string,
value: T | T[] | null
): this {
if (value == null) return this; if (value == null) return this;
this.values[key] = value; this.values[key] = value;
return this; return this;
} }
public note = (value: number | string | null, ...kwargs: number[]|string[]) => { public note = (
value: number | string | null,
...kwargs: number[] | string[]
) => {
if (typeof value === "string") { if (typeof value === "string") {
const parsedNote = noteNameToMidi(value); const parsedNote = noteNameToMidi(value);
return this.updateValue("note", [parsedNote, ...kwargs].flat(Infinity)); return this.updateValue("note", [parsedNote, ...kwargs].flat(Infinity));

View File

@ -5,10 +5,7 @@ import {
arrayOfObjectsToObjectWithArrays, arrayOfObjectsToObjectWithArrays,
objectWithArraysToArrayOfObjects, objectWithArraysToArrayOfObjects,
} from "../Utils/Generic"; } from "../Utils/Generic";
import { import { midiToFreq, noteFromPc } from "zifferjs";
midiToFreq,
noteFromPc,
} from "zifferjs";
import { import {
superdough, superdough,
@ -111,13 +108,13 @@ export class SoundEvent extends AudibleEvent {
return self; return self;
}, },
scope: function (self: SoundEvent) { scope: function (self: SoundEvent) {
self.updateValue("analyze", true) self.updateValue("analyze", true);
return self return self;
}, },
debug: function (self: SoundEvent, callback?: Function) { debug: function (self: SoundEvent, callback?: Function) {
self.updateValue("debug", true) self.updateValue("debug", true);
if (callback) { if (callback) {
self.updateValue("debugFunction", callback) self.updateValue("debugFunction", callback);
} }
return self; return self;
}, },
@ -467,16 +464,12 @@ export class SoundEvent extends AudibleEvent {
} }
if (this.values["debug"]) { if (this.values["debug"]) {
if (this.values["debugFunction"]) { if (this.values["debugFunction"]) {
this.values["debugFunction"](filteredEvent) this.values["debugFunction"](filteredEvent);
} else { } else {
console.log(filteredEvent) console.log(filteredEvent);
} }
} }
superdough( superdough(filteredEvent, this.app.clock.deadline, filteredEvent.dur);
filteredEvent,
this.app.clock.deadline,
filteredEvent.dur,
);
} }
}; };
} }

View File

@ -13,7 +13,8 @@ ${makeExample(
"Feeding a sine to the oscilloscope", "Feeding a sine to the oscilloscope",
` `
beat(1)::sound('sine').freq(200).ad(0, .2).scope().out() beat(1)::sound('sine').freq(200).ad(0, .2).scope().out()
`, true `,
true,
)} )}
Here is a layout of the scope configuration options: Here is a layout of the scope configuration options: