Skip to content

Commit

Permalink
correct off by 4x timer and sequencer timings, make range more dynamic
Browse files Browse the repository at this point in the history
  • Loading branch information
scottweiss committed Nov 28, 2024
1 parent 1c0d788 commit 4f3ab81
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 40 deletions.
45 changes: 32 additions & 13 deletions src/Timer.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,43 @@
// import { AudioEngine } from "./audio/AudioEngine";
// import { CanvasController } from "./canvas/CanvasController";
import { AudioEngine } from "./audio/AudioEngine";

export type TimerProps = {
bpm?: number;
};
export class Timer {
// private audioEngine: AudioEngine;
// private canvasController: CanvasController;
private static instance: Timer;
private audioEngine: AudioEngine = AudioEngine.getInstance();
// private fpsInterval: number = 1000 / 60; // 60 fps
private then: number = 0;
private bpm: number = 60000 / 360;

public draw(timestamp: number, callback?: (e: number) => void): void {
requestAnimationFrame((timestamp) => this.draw(timestamp, callback));
public beat(timestamp: number, callback?: (e: number) => void): void {
requestAnimationFrame((timestamp) => this.beat(timestamp, callback));
const bpm = this.audioEngine.bpm;
if (timestamp < this.then + 6000 / bpm) return;

if (timestamp < this.then + this.bpm) return; // Skip frames to maintain 60 fps

this.then = timestamp - ((timestamp - this.then) % this.bpm);
this.then = timestamp - ((timestamp - this.then) % (6000 / bpm));

if (callback) {
// Clear the canvas before drawing each frame

// Call the provided draw callback function
callback(this.then / 1000);
}
}

// public draw(timestamp: number, callback?: () => void): void {
// requestAnimationFrame((timestamp) => this.draw(timestamp, callback));

// if (timestamp < this.then + this.fpsInterval) return; // Skip frames to maintain 60 fps

// this.then = timestamp - ((timestamp - this.then) % this.fpsInterval);

// if (callback) {
// // Call the provided draw callback function
// callback();
// }
// }

public static getInstance(): Timer {
if (!Timer.instance) {
Timer.instance = new Timer();
}
return Timer.instance;
}
}
67 changes: 42 additions & 25 deletions src/components/csd-range/csd-range.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,41 @@
import { CanvasController } from "../../canvas/CanvasController";
import styles from "./csd-range.scss?inline";

export type CsdRangeProps = {
min?: number;
max?: number;
stepSize?: number;
label: string;
value?: string;
value?: number;
};

export class CsdRange extends HTMLElement {
// rangeDomReference;
_value: string;
_value: number;
label: string;
rangeElement: HTMLInputElement;
inputElement: HTMLOutputElement;
outputElement: HTMLOutputElement;
min: number;
max: number;
stepSize: number;
canvasController: CanvasController;
#canvas: HTMLCanvasElement;
mousePositionOnMousedown: { x: number; y: number } | undefined;
#ctx;

constructor(props: CsdRangeProps) {
super();
this.#canvas = document.createElement("canvas");
this.#ctx = this.#canvas.getContext("2d");
console.log(props);

this._value = "0";
this.canvasController = new CanvasController();

this.#canvas = this.canvasController.getCanvasElement();
this.#ctx = this.canvasController.getCtx();

this._value = props.value || 0;
this.min = props.min || 0.1;
this.max = props.max || 1;
this.stepSize = props.stepSize || 0.01;
this.label = props.label;

const sheet = new CSSStyleSheet();
Expand All @@ -35,7 +44,7 @@ export class CsdRange extends HTMLElement {
const shadowRoot = this.attachShadow({ mode: "open" });

this.rangeElement = this.renderRangeElement();
this.inputElement = this.renderRangeValueDisplayElement();
this.outputElement = this.renderRangeValueDisplayElement();

const rangeLabel = document.createElement("label");
const labelSpan = document.createElement("span");
Expand All @@ -46,7 +55,7 @@ export class CsdRange extends HTMLElement {
rangeLabel.append(labelSpan, this.rangeElement);

shadowRoot.adoptedStyleSheets.push(sheet);
shadowRoot.appendChild(this.inputElement);
shadowRoot.appendChild(this.outputElement);
shadowRoot.appendChild(this.#canvas);

shadowRoot.appendChild(rangeLabel);
Expand All @@ -56,10 +65,10 @@ export class CsdRange extends HTMLElement {
}
}

set value(value: string) {
set value(value: number) {
this._value = value;
this.inputElement.value = value;
this.rangeElement.value = value;
this.outputElement.value = String(value);
this.rangeElement.value = String(value);

requestAnimationFrame(() => {
this.dispatchEvent(
Expand All @@ -73,14 +82,14 @@ export class CsdRange extends HTMLElement {
this.drawCanvas();
}

get value(): string {
get value(): number {
return this._value;
}

renderRangeValueDisplayElement(): HTMLOutputElement {
const rangeValueInput = document.createElement("output");
rangeValueInput.setAttribute("type", "text");
rangeValueInput.value = this.value;
rangeValueInput.value = String(this.value);

// rangeValueInput.addEventListener('input', (event) => {
// this.value = (event.target as ).value;
Expand All @@ -91,15 +100,15 @@ export class CsdRange extends HTMLElement {

renderRangeElement(): HTMLInputElement {
const rangeElement = document.createElement("input");

console.log(this.value);
rangeElement.setAttribute("type", "range");
rangeElement.setAttribute("min", String(this.min));
rangeElement.setAttribute("max", String(this.max));
rangeElement.setAttribute("step", "0.01");
rangeElement.value = this.value;
rangeElement.setAttribute("step", String(this.stepSize));
rangeElement.value = String(this.value);

rangeElement.addEventListener("input", (event) => {
this.value = (event.target as HTMLInputElement).value;
this.value = Number((event.target as HTMLInputElement).value);
});

return rangeElement;
Expand Down Expand Up @@ -132,21 +141,29 @@ export class CsdRange extends HTMLElement {
this.#ctx.stroke();
this.#ctx.fill();
// Draw the line that represents the current volume level
const angle = (180 - Number(this.value) * 180) * (Math.PI / 180);

const normalizedAnglePerStepSize =
(this.value - this.min) / (this.max - this.min);
const angle = (315 - normalizedAnglePerStepSize * 360) * (Math.PI / 270);
this.#ctx.beginPath();
this.#ctx.moveTo(canvasWidth / 2, canvasHeight / 2);
// this.#ctx.moveTo(canvasWidth / 2, canvasHeight / 2);
this.#ctx.moveTo(
canvasWidth / 2 + radius * Math.cos(angle),
canvasHeight / 2 - radius * Math.sin(angle),
);
this.#ctx.lineTo(
canvasWidth / 2 + radius * Math.cos(angle),
canvasHeight / 2 - radius * Math.sin(angle),
);

this.#ctx.strokeStyle = "orange";
this.#ctx.lineWidth = 4;
this.#ctx.lineWidth = 8;
this.#ctx.stroke();
}

connectedCallback() {
this.drawCanvas();
this.canvasController.draw(0, this.drawCanvas.bind(this));
this.canvasController.resize();

this.#canvas.addEventListener("mousedown", (event) => {
this.mousePositionOnMousedown = { x: event.x, y: event.y };
// console.log(this.mousePositionOnMousedown)
Expand All @@ -170,10 +187,10 @@ export class CsdRange extends HTMLElement {
Math.min(
Math.max(
Number(this.value) -
(event.y - this.mousePositionOnMousedown.y) / 100,
0,
(event.y - this.mousePositionOnMousedown.y) * this.stepSize,
this.min,
),
1,
this.max,
).toFixed(2),
);

Expand Down
21 changes: 19 additions & 2 deletions src/components/csd-sequencer/csd-sequencer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { AudioEngine } from "../../audio/AudioEngine";
import { CanvasController } from "../../canvas/CanvasController";
import { Drum } from "../../midi/Drum";
import { Timer } from "../../Timer";
import { CsdRange } from "../csd-range/csd-range";
import { CsdSequencerTrack } from "./csd-sequencer-track/csd-sequencer-track";
import styles from "./index.scss?inline";

Expand All @@ -18,27 +19,43 @@ export class CsdSequencer extends HTMLElement {
play: boolean = false;
tracks: Array<CsdSequencerTrack> = [];
playToggle: HTMLButtonElement;
bpmRange: CsdRange;

constructor() {
super();
this.context = this.canvasController.getCtx();
this.playToggle = this.renderPlayToggle();
this.kick = new Drum(700, "triangle", 0.1, 0.1);
this.snare = new Drum(600, "triangle", 0.1, 0.1);
this.bpmRange = new CsdRange({
label: "bpm",
min: 24,
max: 250,
stepSize: 1,
value: this.audioEngine.bpm,
});
this.bpmRange.addEventListener("csdRange", (event) => {
const newValue = (event as CustomEvent).detail.value;
if (newValue == null) {
return;
}
this.audioEngine.bpm = newValue / 4;
});

// add styles
const sheet = new CSSStyleSheet();
sheet.replaceSync(styles);
const shadowRoot = this.attachShadow({ mode: "open" });
shadowRoot.adoptedStyleSheets.push(sheet);

shadowRoot.append(this.buildTrackTable());
shadowRoot.append(this.bpmRange, this.buildTrackTable());
}

connectedCallback() {
this.canvasController.resize();
this.canvasController.draw(0, this.draw.bind(this));

this.timer.draw(0, this.beatCallback.bind(this));
this.timer.beat(0, this.beatCallback.bind(this));
}

renderPlayToggle(): HTMLButtonElement {
Expand Down

0 comments on commit 4f3ab81

Please sign in to comment.