@blibliki/engine 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +45 -98
- package/dist/index.d.ts +45 -98
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/src/Engine.ts +54 -24
- package/src/core/Note/index.ts +3 -3
- package/src/core/index.ts +0 -3
- package/src/core/midi/ComputerKeyboardDevice.ts +9 -2
- package/src/core/midi/MidiDevice.ts +5 -3
- package/src/core/midi/MidiDeviceManager.ts +14 -5
- package/src/core/midi/MidiEvent.ts +10 -6
- package/src/core/module/Module.ts +7 -8
- package/src/core/module/PolyModule.ts +3 -3
- package/src/core/module/VoiceScheduler.ts +3 -3
- package/src/index.ts +4 -1
- package/src/modules/BiquadFilter.ts +4 -4
- package/src/modules/Constant.ts +12 -11
- package/src/modules/Envelope.ts +16 -21
- package/src/modules/Filter.ts +3 -2
- package/src/modules/Gain.ts +4 -3
- package/src/modules/Inspector.ts +4 -3
- package/src/modules/Master.ts +3 -4
- package/src/modules/Oscillator.ts +18 -21
- package/src/modules/Scale.ts +3 -2
- package/src/modules/StepSequencer.ts +1 -2
- package/src/modules/VirtualMidi.ts +3 -3
- package/src/processors/index.ts +7 -11
- package/src/core/Timing/Scheduler.ts +0 -37
- package/src/core/Timing/Time.ts +0 -103
- package/src/core/Timing/Transport.ts +0 -104
- package/src/core/Timing/index.ts +0 -16
package/src/modules/Filter.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Context } from "@blibliki/utils";
|
|
2
|
+
import { Module } from "@/core";
|
|
2
3
|
import { IModuleConstructor } from "@/core/module/Module";
|
|
3
4
|
import { IPolyModuleConstructor, PolyModule } from "@/core/module/PolyModule";
|
|
4
5
|
import { PropSchema } from "@/core/schema";
|
|
@@ -54,7 +55,7 @@ class MonoFilter extends Module<ModuleType.Filter> {
|
|
|
54
55
|
constructor(engineId: string, params: ICreateModule<ModuleType.Filter>) {
|
|
55
56
|
const props = { ...DEFAULT_PROPS, ...params.props };
|
|
56
57
|
|
|
57
|
-
const audioNodeConstructor = (context:
|
|
58
|
+
const audioNodeConstructor = (context: Context) =>
|
|
58
59
|
newAudioWorklet(context, CustomWorklet.FilterProcessor);
|
|
59
60
|
|
|
60
61
|
super(engineId, {
|
package/src/modules/Gain.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Context } from "@blibliki/utils";
|
|
2
|
+
import { IModule, Module } from "@/core";
|
|
2
3
|
import { IModuleConstructor } from "@/core/module/Module";
|
|
3
4
|
import { IPolyModuleConstructor, PolyModule } from "@/core/module/PolyModule";
|
|
4
5
|
import { PropSchema } from "@/core/schema";
|
|
@@ -26,8 +27,8 @@ export class MonoGain extends Module<ModuleType.Gain> {
|
|
|
26
27
|
|
|
27
28
|
constructor(engineId: string, params: ICreateModule<ModuleType.Gain>) {
|
|
28
29
|
const props = { ...DEFAULT_PROPS, ...params.props };
|
|
29
|
-
const audioNodeConstructor = (context:
|
|
30
|
-
new GainNode(context);
|
|
30
|
+
const audioNodeConstructor = (context: Context) =>
|
|
31
|
+
new GainNode(context.audioContext);
|
|
31
32
|
|
|
32
33
|
super(engineId, {
|
|
33
34
|
...params,
|
package/src/modules/Inspector.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Context } from "@blibliki/utils";
|
|
2
|
+
import { IModule, Module } from "@/core";
|
|
2
3
|
import { PropSchema } from "@/core/schema";
|
|
3
4
|
import { ICreateModule, ModuleType } from ".";
|
|
4
5
|
|
|
@@ -23,8 +24,8 @@ export default class Inspector extends Module<ModuleType.Inspector> {
|
|
|
23
24
|
|
|
24
25
|
constructor(engineId: string, params: ICreateModule<ModuleType.Inspector>) {
|
|
25
26
|
const props = { ...DEFAULT_PROPS, ...params.props };
|
|
26
|
-
const audioNodeConstructor = (context:
|
|
27
|
-
new AnalyserNode(context);
|
|
27
|
+
const audioNodeConstructor = (context: Context) =>
|
|
28
|
+
new AnalyserNode(context.audioContext);
|
|
28
29
|
|
|
29
30
|
super(engineId, {
|
|
30
31
|
...params,
|
package/src/modules/Master.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { EmptyObject } from "@blibliki/utils";
|
|
2
|
-
import {
|
|
1
|
+
import { Context, EmptyObject } from "@blibliki/utils";
|
|
2
|
+
import { IModule, Module } from "@/core";
|
|
3
3
|
import { PropSchema } from "@/core/schema";
|
|
4
4
|
import { ICreateModule, ModuleType } from ".";
|
|
5
5
|
|
|
@@ -15,8 +15,7 @@ export default class Master extends Module<ModuleType.Master> {
|
|
|
15
15
|
|
|
16
16
|
constructor(engineId: string, params: ICreateModule<ModuleType.Master>) {
|
|
17
17
|
const props = { ...DEFAULT_PROPS, ...params.props };
|
|
18
|
-
const audioNodeConstructor = (context:
|
|
19
|
-
context.destination;
|
|
18
|
+
const audioNodeConstructor = (context: Context) => context.destination;
|
|
20
19
|
|
|
21
20
|
super(engineId, { ...params, audioNodeConstructor, props });
|
|
22
21
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { ContextTime } from "@blibliki/transport";
|
|
2
|
+
import { Context, dbToGain } from "@blibliki/utils";
|
|
3
|
+
import { IModule, Module } from "@/core";
|
|
3
4
|
import Note from "@/core/Note";
|
|
4
|
-
import { nt, TTime } from "@/core/Timing/Time";
|
|
5
5
|
import { IModuleConstructor } from "@/core/module/Module";
|
|
6
6
|
import { IPolyModuleConstructor, PolyModule } from "@/core/module/PolyModule";
|
|
7
7
|
import { PropSchema } from "@/core/schema";
|
|
@@ -95,8 +95,8 @@ export class MonoOscillator extends Module<ModuleType.Oscillator> {
|
|
|
95
95
|
|
|
96
96
|
constructor(engineId: string, params: ICreateModule<ModuleType.Oscillator>) {
|
|
97
97
|
const props = { ...DEFAULT_PROPS, ...params.props };
|
|
98
|
-
const audioNodeConstructor = (context:
|
|
99
|
-
new OscillatorNode(context);
|
|
98
|
+
const audioNodeConstructor = (context: Context) =>
|
|
99
|
+
new OscillatorNode(context.audioContext);
|
|
100
100
|
|
|
101
101
|
super(engineId, {
|
|
102
102
|
...params,
|
|
@@ -104,7 +104,7 @@ export class MonoOscillator extends Module<ModuleType.Oscillator> {
|
|
|
104
104
|
audioNodeConstructor,
|
|
105
105
|
});
|
|
106
106
|
|
|
107
|
-
this.lowOutputGain = new GainNode(this.context, {
|
|
107
|
+
this.lowOutputGain = new GainNode(this.context.audioContext, {
|
|
108
108
|
gain: dbToGain(LOW_GAIN),
|
|
109
109
|
});
|
|
110
110
|
|
|
@@ -140,17 +140,17 @@ export class MonoOscillator extends Module<ModuleType.Oscillator> {
|
|
|
140
140
|
this.rePlugAll();
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
start(time:
|
|
143
|
+
start(time: ContextTime) {
|
|
144
144
|
if (this.isStated) return;
|
|
145
145
|
|
|
146
146
|
this.isStated = true;
|
|
147
|
-
this.audioNode.start(
|
|
147
|
+
this.audioNode.start(time);
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
stop(time:
|
|
151
|
-
this.audioNode.stop(
|
|
150
|
+
stop(time: ContextTime) {
|
|
151
|
+
this.audioNode.stop(time);
|
|
152
152
|
this.rePlugAll(() => {
|
|
153
|
-
this.audioNode = new OscillatorNode(this.context, {
|
|
153
|
+
this.audioNode = new OscillatorNode(this.context.audioContext, {
|
|
154
154
|
type: this.props.wave,
|
|
155
155
|
frequency: this.finalFrequency,
|
|
156
156
|
});
|
|
@@ -161,7 +161,7 @@ export class MonoOscillator extends Module<ModuleType.Oscillator> {
|
|
|
161
161
|
this.isStated = false;
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
-
triggerAttack = (note: Note, triggeredAt:
|
|
164
|
+
triggerAttack = (note: Note, triggeredAt: ContextTime) => {
|
|
165
165
|
super.triggerAttack(note, triggeredAt);
|
|
166
166
|
|
|
167
167
|
this.props = { frequency: note.frequency };
|
|
@@ -169,7 +169,7 @@ export class MonoOscillator extends Module<ModuleType.Oscillator> {
|
|
|
169
169
|
this.start(triggeredAt);
|
|
170
170
|
};
|
|
171
171
|
|
|
172
|
-
triggerRelease(note: Note, triggeredAt:
|
|
172
|
+
triggerRelease(note: Note, triggeredAt: ContextTime) {
|
|
173
173
|
super.triggerRelease(note, triggeredAt);
|
|
174
174
|
|
|
175
175
|
const lastNote = this.activeNotes.length
|
|
@@ -190,14 +190,11 @@ export class MonoOscillator extends Module<ModuleType.Oscillator> {
|
|
|
190
190
|
return transposed;
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
private updateFrequency(actionAt?:
|
|
193
|
+
private updateFrequency(actionAt?: ContextTime) {
|
|
194
194
|
if (this.finalFrequency === undefined) return;
|
|
195
195
|
|
|
196
196
|
if (actionAt) {
|
|
197
|
-
this.audioNode.frequency.setValueAtTime(
|
|
198
|
-
this.finalFrequency,
|
|
199
|
-
nt(actionAt),
|
|
200
|
-
);
|
|
197
|
+
this.audioNode.frequency.setValueAtTime(this.finalFrequency, actionAt);
|
|
201
198
|
} else {
|
|
202
199
|
this.audioNode.frequency.value = this.finalFrequency;
|
|
203
200
|
}
|
|
@@ -208,7 +205,7 @@ export class MonoOscillator extends Module<ModuleType.Oscillator> {
|
|
|
208
205
|
}
|
|
209
206
|
|
|
210
207
|
private initializeGainDetune() {
|
|
211
|
-
this.detuneGain = new GainNode(this.context, { gain: 100 });
|
|
208
|
+
this.detuneGain = new GainNode(this.context.audioContext, { gain: 100 });
|
|
212
209
|
this.detuneGain.connect(this.audioNode.detune);
|
|
213
210
|
}
|
|
214
211
|
|
|
@@ -249,13 +246,13 @@ export default class Oscillator extends PolyModule<ModuleType.Oscillator> {
|
|
|
249
246
|
this.registerDefaultIOs("out");
|
|
250
247
|
}
|
|
251
248
|
|
|
252
|
-
start(time:
|
|
249
|
+
start(time: ContextTime) {
|
|
253
250
|
this.audioModules.forEach((audioModule) => {
|
|
254
251
|
audioModule.start(time);
|
|
255
252
|
});
|
|
256
253
|
}
|
|
257
254
|
|
|
258
|
-
stop(time:
|
|
255
|
+
stop(time: ContextTime) {
|
|
259
256
|
this.audioModules.forEach((audioModule) => {
|
|
260
257
|
audioModule.stop(time);
|
|
261
258
|
});
|
package/src/modules/Scale.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Context } from "@blibliki/utils";
|
|
2
|
+
import { IModule, Module } from "@/core";
|
|
2
3
|
import { PropSchema } from "@/core/schema";
|
|
3
4
|
import { CustomWorklet, newAudioWorklet } from "@/processors";
|
|
4
5
|
import { ICreateModule, ModuleType } from ".";
|
|
@@ -41,7 +42,7 @@ export default class Scale extends Module<ModuleType.Scale> {
|
|
|
41
42
|
|
|
42
43
|
constructor(engineId: string, params: ICreateModule<ModuleType.Scale>) {
|
|
43
44
|
const props = { ...DEFAULT_PROPS, ...params.props };
|
|
44
|
-
const audioNodeConstructor = (context:
|
|
45
|
+
const audioNodeConstructor = (context: Context) =>
|
|
45
46
|
newAudioWorklet(context, CustomWorklet.ScaleProcessor);
|
|
46
47
|
|
|
47
48
|
super(engineId, {
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { INote, PropSchema, Module, IModule, MidiOutput } from "@/core";
|
|
2
|
-
import { BarsBeatsSixteenths } from "@/core/Timing/Time";
|
|
3
2
|
import { ICreateModule, ModuleType } from ".";
|
|
4
3
|
|
|
5
4
|
export type IStepSequencer = IModule<ModuleType.StepSequencer>;
|
|
6
5
|
|
|
7
6
|
export type ISequence = {
|
|
8
7
|
active: boolean;
|
|
9
|
-
time: BarsBeatsSixteenths;
|
|
8
|
+
// time: BarsBeatsSixteenths;
|
|
10
9
|
duration: string;
|
|
11
10
|
notes: INote[];
|
|
12
11
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { ContextTime } from "@blibliki/transport";
|
|
1
2
|
import { PropSchema, Module, IModule, MidiOutput, Note } from "@/core";
|
|
2
|
-
import { TTime } from "@/core/Timing/Time";
|
|
3
3
|
import MidiEvent from "@/core/midi/MidiEvent";
|
|
4
4
|
import { ICreateModule, ModuleType } from ".";
|
|
5
5
|
|
|
@@ -37,13 +37,13 @@ export default class VirtualMidi extends Module<ModuleType.VirtualMidi> {
|
|
|
37
37
|
this.midiOutput.onMidiEvent(midiEvent);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
triggerAttack = (note: Note, triggerAttack:
|
|
40
|
+
triggerAttack = (note: Note, triggerAttack: ContextTime) => {
|
|
41
41
|
this.props = { activeNotes: [...this.props.activeNotes, note.fullName] };
|
|
42
42
|
this.triggerPropsUpdate();
|
|
43
43
|
this.sendMidi(MidiEvent.fromNote(note, true, triggerAttack));
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
-
triggerRelease = (note: Note, triggerAttack:
|
|
46
|
+
triggerRelease = (note: Note, triggerAttack: ContextTime) => {
|
|
47
47
|
this.props = {
|
|
48
48
|
activeNotes: this.props.activeNotes.filter(
|
|
49
49
|
(name) => name !== note.fullName,
|
package/src/processors/index.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { assertNever } from "@blibliki/utils";
|
|
2
|
-
import { IAnyAudioContext } from "@/core";
|
|
1
|
+
import { assertNever, Context } from "@blibliki/utils";
|
|
3
2
|
import { filterProcessorURL } from "./filter-processor";
|
|
4
3
|
import { scaleProcessorURL } from "./scale-processor";
|
|
5
4
|
|
|
@@ -8,20 +7,17 @@ export enum CustomWorklet {
|
|
|
8
7
|
FilterProcessor = "FilterProcessor",
|
|
9
8
|
}
|
|
10
9
|
|
|
11
|
-
export async function loadProcessors(context:
|
|
12
|
-
await context.
|
|
13
|
-
await context.
|
|
10
|
+
export async function loadProcessors(context: Context) {
|
|
11
|
+
await context.addModule(scaleProcessorURL);
|
|
12
|
+
await context.addModule(filterProcessorURL);
|
|
14
13
|
}
|
|
15
14
|
|
|
16
|
-
export function newAudioWorklet(
|
|
17
|
-
context: IAnyAudioContext,
|
|
18
|
-
worklet: CustomWorklet,
|
|
19
|
-
) {
|
|
15
|
+
export function newAudioWorklet(context: Context, worklet: CustomWorklet) {
|
|
20
16
|
switch (worklet) {
|
|
21
17
|
case CustomWorklet.ScaleProcessor:
|
|
22
|
-
return
|
|
18
|
+
return context.newAudioWorklet("scale-processor");
|
|
23
19
|
case CustomWorklet.FilterProcessor:
|
|
24
|
-
return
|
|
20
|
+
return context.newAudioWorklet("filter-processor");
|
|
25
21
|
default:
|
|
26
22
|
assertNever(worklet);
|
|
27
23
|
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { Scheduler as InternalScheduler } from "@ircam/sc-scheduling";
|
|
2
|
-
import { now } from ".";
|
|
3
|
-
import { nt, t, TTime } from "./Time";
|
|
4
|
-
import Transport from "./Transport";
|
|
5
|
-
|
|
6
|
-
export default class Scheduler {
|
|
7
|
-
transport: Transport;
|
|
8
|
-
internalScheduler: InternalScheduler;
|
|
9
|
-
|
|
10
|
-
constructor(transport: Transport) {
|
|
11
|
-
this.transport = transport;
|
|
12
|
-
this.internalScheduler = new InternalScheduler(now, {
|
|
13
|
-
currentTimeToProcessorTimeFunction: () => this.transport.playhead,
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
start(actionAt: TTime, callback: () => void) {
|
|
18
|
-
this.internalScheduler.add(this.processor, nt(actionAt));
|
|
19
|
-
this.defer(callback, actionAt);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
stop(actionAt: TTime, callback: () => void) {
|
|
23
|
-
this.defer(() => {
|
|
24
|
-
this.internalScheduler.remove(this.processor);
|
|
25
|
-
callback();
|
|
26
|
-
}, actionAt);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
defer(callback: () => void, actionAt: TTime) {
|
|
30
|
-
this.internalScheduler.defer(callback, nt(actionAt));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
private processor = (currentTime: number, playhead: TTime) => {
|
|
34
|
-
console.log(`playhead: ${t(playhead).toNotation()}`);
|
|
35
|
-
return currentTime + 0.5;
|
|
36
|
-
};
|
|
37
|
-
}
|
package/src/core/Timing/Time.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { isNumber } from "@blibliki/utils";
|
|
2
|
-
import { Engine } from "@/Engine";
|
|
3
|
-
import { now } from ".";
|
|
4
|
-
|
|
5
|
-
export type BarsBeatsSixteenths = `${number}:${number}:${number}`;
|
|
6
|
-
export type TTime = number | BarsBeatsSixteenths | Time;
|
|
7
|
-
|
|
8
|
-
export const t = (value?: TTime): Time => {
|
|
9
|
-
value ??= now();
|
|
10
|
-
|
|
11
|
-
if (value instanceof Time) return value;
|
|
12
|
-
|
|
13
|
-
return new Time(value);
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export const nt = (value?: TTime): number => {
|
|
17
|
-
value ??= now();
|
|
18
|
-
|
|
19
|
-
if (typeof value === "number") return value;
|
|
20
|
-
|
|
21
|
-
return t(value).toNumber();
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export default class Time {
|
|
25
|
-
private value: number | BarsBeatsSixteenths;
|
|
26
|
-
private _notation?: BarsBeatsSixteenths;
|
|
27
|
-
private _number?: number;
|
|
28
|
-
|
|
29
|
-
constructor(value: TTime) {
|
|
30
|
-
this.value = value instanceof Time ? value.value : value;
|
|
31
|
-
|
|
32
|
-
if (isNumber(this.value)) {
|
|
33
|
-
this._number = this.value;
|
|
34
|
-
} else {
|
|
35
|
-
this._notation = this.value;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
add(value: TTime): Time {
|
|
40
|
-
return t(this.toNumber() + t(value).toNumber());
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
subtrack(value: TTime): Time {
|
|
44
|
-
return t(this.toNumber() - nt(value));
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
isBefore(value: TTime): boolean {
|
|
48
|
-
return this.toNumber() < nt(value);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
isAfter(value: TTime): boolean {
|
|
52
|
-
return this.toNumber() > nt(value);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
isEqual(value: TTime): boolean {
|
|
56
|
-
return this.toNumber() > nt(value);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
toNotation(): BarsBeatsSixteenths {
|
|
60
|
-
if (this._notation) return this._notation;
|
|
61
|
-
|
|
62
|
-
const [count, factor] = this.transport.timeSignature;
|
|
63
|
-
const divisionsPerBeat = 16 / factor;
|
|
64
|
-
const divisionsPerBar = count * divisionsPerBeat;
|
|
65
|
-
const secondsPerBeat = 60 / this.transport.bpm;
|
|
66
|
-
|
|
67
|
-
const totalDivisions = Math.floor(
|
|
68
|
-
((this.value as number) / secondsPerBeat) * divisionsPerBeat,
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
const bars = Math.floor(totalDivisions / divisionsPerBar);
|
|
72
|
-
const beats = Math.floor(
|
|
73
|
-
(totalDivisions % divisionsPerBar) / divisionsPerBeat,
|
|
74
|
-
);
|
|
75
|
-
const divisions = totalDivisions % divisionsPerBeat;
|
|
76
|
-
|
|
77
|
-
this._notation = `${bars}:${beats}:${divisions}`;
|
|
78
|
-
|
|
79
|
-
return this._notation;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
toNumber(): number {
|
|
83
|
-
if (isNumber(this._number)) return this._number;
|
|
84
|
-
|
|
85
|
-
const [beatsPerBar, sixteenths] = this.transport.timeSignature;
|
|
86
|
-
const secondsPerBeat = 60 / this.transport.bpm;
|
|
87
|
-
|
|
88
|
-
const [bars, beats, divisions] = (this.value as BarsBeatsSixteenths)
|
|
89
|
-
.split(":")
|
|
90
|
-
.map(Number);
|
|
91
|
-
|
|
92
|
-
const totalBeats =
|
|
93
|
-
bars * beatsPerBar + beats + divisions / (sixteenths / 4);
|
|
94
|
-
|
|
95
|
-
this._number = totalBeats * secondsPerBeat;
|
|
96
|
-
|
|
97
|
-
return this._number;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
private get transport() {
|
|
101
|
-
return Engine.current.transport;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { now } from ".";
|
|
2
|
-
import Scheduler from "./Scheduler";
|
|
3
|
-
import Time, { t, TTime } from "./Time";
|
|
4
|
-
|
|
5
|
-
export enum TransportState {
|
|
6
|
-
playing = "playing",
|
|
7
|
-
stopped = "stopped",
|
|
8
|
-
paused = "paused",
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export type TransportEvents = {
|
|
12
|
-
start: { actionAt: TTime; offset: TTime };
|
|
13
|
-
stop: { actionAt: TTime };
|
|
14
|
-
pause: { actionAt: TTime };
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
type TPlaybackCallback = (actionAt: TTime) => void;
|
|
18
|
-
|
|
19
|
-
type TransportProps = {
|
|
20
|
-
onStart?: TPlaybackCallback;
|
|
21
|
-
onStop?: TPlaybackCallback;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export default class Transport {
|
|
25
|
-
bpm = 120;
|
|
26
|
-
timeSignature: [number, number] = [4, 4];
|
|
27
|
-
loopStart: TTime;
|
|
28
|
-
loopEnd?: TTime;
|
|
29
|
-
|
|
30
|
-
state: TransportState = TransportState.stopped;
|
|
31
|
-
offset: TTime = 0;
|
|
32
|
-
|
|
33
|
-
private startTime: TTime = 0;
|
|
34
|
-
private onStart: TransportProps["onStart"];
|
|
35
|
-
private onStop: TransportProps["onStop"];
|
|
36
|
-
private scheduler: Scheduler;
|
|
37
|
-
|
|
38
|
-
constructor(props: TransportProps) {
|
|
39
|
-
this.onStart = props.onStart;
|
|
40
|
-
this.onStop = props.onStop;
|
|
41
|
-
this.loopStart = t("0:0:0");
|
|
42
|
-
this.scheduler = new Scheduler(this);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
start({
|
|
46
|
-
offset = this.offset,
|
|
47
|
-
actionAt = now(),
|
|
48
|
-
}: {
|
|
49
|
-
offset?: TTime;
|
|
50
|
-
actionAt?: TTime;
|
|
51
|
-
}) {
|
|
52
|
-
if (this.state === TransportState.playing) return;
|
|
53
|
-
|
|
54
|
-
this.validateFutureTime(actionAt);
|
|
55
|
-
|
|
56
|
-
this.scheduler.start(actionAt, () => {
|
|
57
|
-
this.state = TransportState.playing;
|
|
58
|
-
this.offset = offset;
|
|
59
|
-
this.startTime = t(actionAt).subtrack(this.offset);
|
|
60
|
-
});
|
|
61
|
-
this.onStart?.(actionAt);
|
|
62
|
-
|
|
63
|
-
return actionAt;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
stop({ actionAt: actionAt = now() }: { actionAt?: TTime }) {
|
|
67
|
-
if (this.state === TransportState.stopped) return;
|
|
68
|
-
|
|
69
|
-
this.validateFutureTime(actionAt);
|
|
70
|
-
|
|
71
|
-
this.scheduler.stop(actionAt, () => {
|
|
72
|
-
this.state = TransportState.stopped;
|
|
73
|
-
this.offset = 0;
|
|
74
|
-
});
|
|
75
|
-
this.onStop?.(actionAt);
|
|
76
|
-
|
|
77
|
-
return actionAt;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
pause({ actionAt: actionAt = now() }: { actionAt?: TTime }) {
|
|
81
|
-
if (this.state === TransportState.paused) return;
|
|
82
|
-
|
|
83
|
-
this.validateFutureTime(actionAt);
|
|
84
|
-
|
|
85
|
-
this.scheduler.stop(actionAt, () => {
|
|
86
|
-
this.state = TransportState.paused;
|
|
87
|
-
this.offset = t(actionAt).subtrack(this.startTime);
|
|
88
|
-
});
|
|
89
|
-
this.onStop?.(actionAt);
|
|
90
|
-
|
|
91
|
-
return actionAt;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
get playhead(): Time {
|
|
95
|
-
if (this.state === TransportState.stopped) return t(0);
|
|
96
|
-
if (this.state === TransportState.paused) return t(this.offset);
|
|
97
|
-
|
|
98
|
-
return t(now()).subtrack(this.startTime);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
private validateFutureTime(time: TTime) {
|
|
102
|
-
if (t(time).isBefore(now())) throw Error("Past time not allowed");
|
|
103
|
-
}
|
|
104
|
-
}
|
package/src/core/Timing/index.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { Engine } from "@/Engine";
|
|
2
|
-
|
|
3
|
-
export { default as Transport, TransportState } from "./Transport";
|
|
4
|
-
export { default as Time } from "./Time";
|
|
5
|
-
|
|
6
|
-
export type { TransportEvents } from "./Transport";
|
|
7
|
-
export type { TTime } from "./Time";
|
|
8
|
-
|
|
9
|
-
export function now() {
|
|
10
|
-
return Engine.current.context.currentTime;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function browserToContextTime(time: number): number {
|
|
14
|
-
const differenceBetweenClocks = performance.now() / 1000 - now();
|
|
15
|
-
return time / 1000 - differenceBetweenClocks;
|
|
16
|
-
}
|