@blibliki/engine 0.1.27 → 0.3.2

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.
Files changed (123) hide show
  1. package/README.md +252 -76
  2. package/dist/index.cjs +2 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +765 -0
  5. package/dist/index.d.ts +765 -0
  6. package/dist/index.js +2 -19484
  7. package/dist/index.js.map +1 -0
  8. package/package.json +19 -28
  9. package/src/Engine.ts +158 -177
  10. package/src/core/IO/AudioIO.ts +72 -0
  11. package/src/core/IO/Base.ts +118 -0
  12. package/src/core/IO/Collection.ts +123 -47
  13. package/src/core/IO/MidiIO.ts +43 -0
  14. package/src/core/IO/PolyAudioIO.ts +115 -0
  15. package/src/core/IO/index.ts +7 -61
  16. package/src/core/Note/frequencyTable.ts +144 -144
  17. package/src/core/Note/index.ts +49 -59
  18. package/src/core/Route.ts +79 -0
  19. package/src/core/Timing/Scheduler.ts +37 -0
  20. package/src/core/Timing/Time.ts +103 -0
  21. package/src/core/Timing/Transport.ts +104 -0
  22. package/src/core/Timing/index.ts +16 -0
  23. package/src/core/index.ts +36 -0
  24. package/src/core/midi/{ComputerKeyboardInput.ts → ComputerKeyboardDevice.ts} +31 -11
  25. package/src/core/midi/MidiDevice.ts +38 -31
  26. package/src/core/midi/MidiDeviceManager.ts +54 -55
  27. package/src/core/midi/MidiEvent.ts +36 -60
  28. package/src/core/module/Module.ts +233 -0
  29. package/src/core/module/PolyModule.ts +246 -0
  30. package/src/core/module/VoiceScheduler.ts +121 -0
  31. package/src/core/module/index.ts +3 -0
  32. package/src/core/schema.ts +41 -0
  33. package/src/index.ts +31 -9
  34. package/src/modules/BiquadFilter.ts +162 -0
  35. package/src/modules/Constant.ts +72 -0
  36. package/src/modules/Envelope.ts +178 -0
  37. package/src/modules/Filter.ts +109 -104
  38. package/src/modules/Gain.ts +78 -0
  39. package/src/modules/Inspector.ts +59 -0
  40. package/src/modules/Master.ts +18 -21
  41. package/src/modules/MidiSelector.ts +50 -50
  42. package/src/modules/Oscillator.ts +203 -158
  43. package/src/modules/Scale.ts +79 -0
  44. package/src/modules/StepSequencer.ts +61 -0
  45. package/src/modules/VirtualMidi.ts +33 -49
  46. package/src/modules/index.ts +159 -74
  47. package/src/nodePolyfill.ts +25 -0
  48. package/src/processors/filter-processor.ts +82 -0
  49. package/src/processors/index.ts +28 -0
  50. package/src/processors/scale-processor.ts +81 -0
  51. package/dist/index.umd.cjs +0 -227
  52. package/dist/src/Engine.d.ts +0 -83
  53. package/dist/src/core/IO/AudioNode.d.ts +0 -36
  54. package/dist/src/core/IO/Collection.d.ts +0 -14
  55. package/dist/src/core/IO/ForwardNode/Base.d.ts +0 -19
  56. package/dist/src/core/IO/ForwardNode/index.d.ts +0 -28
  57. package/dist/src/core/IO/MidiNode.d.ts +0 -31
  58. package/dist/src/core/IO/Node.d.ts +0 -41
  59. package/dist/src/core/IO/index.d.ts +0 -22
  60. package/dist/src/core/Module/MonoModule.d.ts +0 -68
  61. package/dist/src/core/Module/PolyModule.d.ts +0 -62
  62. package/dist/src/core/Module/index.d.ts +0 -7
  63. package/dist/src/core/Note/frequencyTable.d.ts +0 -4
  64. package/dist/src/core/Note/index.d.ts +0 -28
  65. package/dist/src/core/midi/ComputerKeyboardInput.d.ts +0 -12
  66. package/dist/src/core/midi/MidiDevice.d.ts +0 -30
  67. package/dist/src/core/midi/MidiDeviceManager.d.ts +0 -15
  68. package/dist/src/core/midi/MidiEvent.d.ts +0 -22
  69. package/dist/src/core/midi/index.d.ts +0 -5
  70. package/dist/src/index.d.ts +0 -9
  71. package/dist/src/main.d.ts +0 -0
  72. package/dist/src/modules/BitCrusher.d.ts +0 -17
  73. package/dist/src/modules/Delay.d.ts +0 -20
  74. package/dist/src/modules/Distortion.d.ts +0 -17
  75. package/dist/src/modules/Effect.d.ts +0 -20
  76. package/dist/src/modules/Envelope/AmpEnvelope.d.ts +0 -19
  77. package/dist/src/modules/Envelope/Base.d.ts +0 -67
  78. package/dist/src/modules/Envelope/FreqEnvelope.d.ts +0 -26
  79. package/dist/src/modules/Envelope/index.d.ts +0 -3
  80. package/dist/src/modules/Filter.d.ts +0 -43
  81. package/dist/src/modules/LFO.d.ts +0 -45
  82. package/dist/src/modules/Master.d.ts +0 -12
  83. package/dist/src/modules/MidiSelector.d.ts +0 -22
  84. package/dist/src/modules/Oscillator.d.ts +0 -56
  85. package/dist/src/modules/Reverb.d.ts +0 -20
  86. package/dist/src/modules/Sequencer.d.ts +0 -43
  87. package/dist/src/modules/VirtualMidi.d.ts +0 -33
  88. package/dist/src/modules/VoiceScheduler.d.ts +0 -45
  89. package/dist/src/modules/Volume.d.ts +0 -27
  90. package/dist/src/modules/index.d.ts +0 -18
  91. package/dist/src/routes.d.ts +0 -19
  92. package/dist/src/types.d.ts +0 -5
  93. package/dist/src/utils.d.ts +0 -1
  94. package/dist/test/MockingModules.d.ts +0 -22
  95. package/dist/test/Module/Oscillator.test.d.ts +0 -1
  96. package/dist/test/core/IO.test.d.ts +0 -1
  97. package/src/core/IO/AudioNode.ts +0 -82
  98. package/src/core/IO/ForwardNode/Base.ts +0 -99
  99. package/src/core/IO/ForwardNode/index.ts +0 -60
  100. package/src/core/IO/MidiNode.ts +0 -67
  101. package/src/core/IO/Node.ts +0 -118
  102. package/src/core/Module/MonoModule.ts +0 -219
  103. package/src/core/Module/PolyModule.ts +0 -218
  104. package/src/core/Module/index.ts +0 -15
  105. package/src/core/midi/index.ts +0 -5
  106. package/src/main.ts +0 -1
  107. package/src/modules/BitCrusher.ts +0 -45
  108. package/src/modules/Delay.ts +0 -53
  109. package/src/modules/Distortion.ts +0 -45
  110. package/src/modules/Effect.ts +0 -46
  111. package/src/modules/Envelope/AmpEnvelope.ts +0 -23
  112. package/src/modules/Envelope/Base.ts +0 -176
  113. package/src/modules/Envelope/FreqEnvelope.ts +0 -64
  114. package/src/modules/Envelope/index.ts +0 -3
  115. package/src/modules/LFO.ts +0 -149
  116. package/src/modules/Reverb.ts +0 -53
  117. package/src/modules/Sequencer.ts +0 -178
  118. package/src/modules/VoiceScheduler.ts +0 -145
  119. package/src/modules/Volume.ts +0 -72
  120. package/src/routes.ts +0 -49
  121. package/src/types.ts +0 -3
  122. package/src/utils.ts +0 -18
  123. package/src/vite-env.d.ts +0 -1
@@ -1,146 +1,146 @@
1
- const frequencyTable: { [key: string]: number } = {
2
- C0: 16.35,
3
- "C#0": 17.32,
4
- Db0: 17.32,
5
- D0: 18.35,
6
- "D#0": 19.45,
7
- Eb0: 19.45,
8
- E0: 20.6,
9
- F0: 21.83,
10
- "F#0": 23.12,
11
- Gb0: 23.12,
12
- G0: 24.5,
13
- "G#0": 25.96,
14
- Ab0: 25.96,
15
- A0: 27.5,
16
- "A#0": 29.14,
17
- Bb0: 29.14,
18
- B0: 30.87,
19
- C1: 32.7,
20
- "C#1": 34.65,
21
- Db1: 34.65,
22
- D1: 36.71,
23
- "D#1": 38.89,
24
- Eb1: 38.89,
25
- E1: 41.2,
26
- F1: 43.65,
27
- "F#1": 46.25,
28
- Gb1: 46.25,
29
- G1: 49.0,
30
- "G#1": 51.91,
31
- Ab1: 51.91,
32
- A1: 55.0,
33
- "A#1": 58.27,
34
- Bb1: 58.27,
35
- B1: 61.74,
36
- C2: 65.41,
37
- "C#2": 69.3,
38
- Db2: 69.3,
39
- D2: 73.42,
40
- "D#2": 77.78,
41
- Eb2: 77.78,
42
- E2: 82.41,
43
- F2: 87.31,
44
- "F#2": 92.5,
45
- Gb2: 92.5,
46
- G2: 98.0,
47
- "G#2": 103.83,
48
- Ab2: 103.83,
49
- A2: 110.0,
50
- "A#2": 116.54,
51
- Bb2: 116.54,
52
- B2: 123.47,
53
- C3: 130.81,
54
- "C#3": 138.59,
55
- Db3: 138.59,
56
- D3: 146.83,
57
- "D#3": 155.56,
58
- Eb3: 155.56,
59
- E3: 164.81,
60
- F3: 174.61,
61
- "F#3": 185.0,
62
- Gb3: 185.0,
63
- G3: 196.0,
64
- "G#3": 207.65,
65
- Ab3: 207.65,
66
- A3: 220.0,
67
- "A#3": 233.08,
68
- Bb3: 233.08,
69
- B3: 246.94,
70
- C4: 261.63,
71
- "C#4": 277.18,
72
- Db4: 277.18,
73
- D4: 293.66,
74
- "D#4": 311.13,
75
- Eb4: 311.13,
76
- E4: 329.63,
77
- F4: 349.23,
78
- "F#4": 369.99,
79
- Gb4: 369.99,
80
- G4: 392.0,
81
- "G#4": 415.3,
82
- Ab4: 415.3,
83
- A4: 440.0,
84
- "A#4": 466.16,
85
- Bb4: 466.16,
86
- B4: 493.88,
87
- C5: 523.25,
88
- "C#5": 554.37,
89
- Db5: 554.37,
90
- D5: 587.33,
91
- "D#5": 622.25,
92
- Eb5: 622.25,
93
- E5: 659.26,
94
- F5: 698.46,
95
- "F#5": 739.99,
96
- Gb5: 739.99,
97
- G5: 783.99,
98
- "G#5": 830.61,
99
- Ab5: 830.61,
100
- A5: 880.0,
101
- "A#5": 932.33,
102
- Bb5: 932.33,
103
- B5: 987.77,
104
- C6: 1046.5,
105
- "C#6": 1108.73,
106
- Db6: 1108.73,
107
- D6: 1174.66,
108
- "D#6": 1244.51,
109
- Eb6: 1244.51,
110
- E6: 1318.51,
111
- F6: 1396.91,
112
- "F#6": 1479.98,
113
- Gb6: 1479.98,
114
- G6: 1567.98,
115
- "G#6": 1661.22,
116
- Ab6: 1661.22,
117
- A6: 1760.0,
118
- "A#6": 1864.66,
119
- Bb6: 1864.66,
120
- B6: 1975.53,
121
- C7: 2093.0,
122
- "C#7": 2217.46,
123
- Db7: 2217.46,
124
- D7: 2349.32,
125
- "D#7": 2489.02,
126
- Eb7: 2489.02,
127
- E7: 2637.02,
128
- F7: 2793.83,
129
- "F#7": 2959.96,
130
- Gb7: 2959.96,
131
- G7: 3135.96,
132
- "G#7": 3322.44,
133
- Ab7: 3322.44,
134
- A7: 3520.0,
135
- "A#7": 3729.31,
136
- Bb7: 3729.31,
137
- B7: 3951.07,
138
- C8: 4186.01,
139
- "C#8": 4434.92,
140
- Db8: 4434.92,
141
- D8: 4698.64,
142
- "D#8": 4978.03,
143
- Eb8: 4978.03,
144
- };
1
+ const frequencyTable = new Map<string, number>([
2
+ ["C0", 16.35],
3
+ ["C#0", 17.32],
4
+ ["Db0", 17.32],
5
+ ["D0", 18.35],
6
+ ["D#0", 19.45],
7
+ ["Eb0", 19.45],
8
+ ["E0", 20.6],
9
+ ["F0", 21.83],
10
+ ["F#0", 23.12],
11
+ ["Gb0", 23.12],
12
+ ["G0", 24.5],
13
+ ["G#0", 25.96],
14
+ ["Ab0", 25.96],
15
+ ["A0", 27.5],
16
+ ["A#0", 29.14],
17
+ ["Bb0", 29.14],
18
+ ["B0", 30.87],
19
+ ["C1", 32.7],
20
+ ["C#1", 34.65],
21
+ ["Db1", 34.65],
22
+ ["D1", 36.71],
23
+ ["D#1", 38.89],
24
+ ["Eb1", 38.89],
25
+ ["E1", 41.2],
26
+ ["F1", 43.65],
27
+ ["F#1", 46.25],
28
+ ["Gb1", 46.25],
29
+ ["G1", 49.0],
30
+ ["G#1", 51.91],
31
+ ["Ab1", 51.91],
32
+ ["A1", 55.0],
33
+ ["A#1", 58.27],
34
+ ["Bb1", 58.27],
35
+ ["B1", 61.74],
36
+ ["C2", 65.41],
37
+ ["C#2", 69.3],
38
+ ["Db2", 69.3],
39
+ ["D2", 73.42],
40
+ ["D#2", 77.78],
41
+ ["Eb2", 77.78],
42
+ ["E2", 82.41],
43
+ ["F2", 87.31],
44
+ ["F#2", 92.5],
45
+ ["Gb2", 92.5],
46
+ ["G2", 98.0],
47
+ ["G#2", 103.83],
48
+ ["Ab2", 103.83],
49
+ ["A2", 110.0],
50
+ ["A#2", 116.54],
51
+ ["Bb2", 116.54],
52
+ ["B2", 123.47],
53
+ ["C3", 130.81],
54
+ ["C#3", 138.59],
55
+ ["Db3", 138.59],
56
+ ["D3", 146.83],
57
+ ["D#3", 155.56],
58
+ ["Eb3", 155.56],
59
+ ["E3", 164.81],
60
+ ["F3", 174.61],
61
+ ["F#3", 185.0],
62
+ ["Gb3", 185.0],
63
+ ["G3", 196.0],
64
+ ["G#3", 207.65],
65
+ ["Ab3", 207.65],
66
+ ["A3", 220.0],
67
+ ["A#3", 233.08],
68
+ ["Bb3", 233.08],
69
+ ["B3", 246.94],
70
+ ["C4", 261.63],
71
+ ["C#4", 277.18],
72
+ ["Db4", 277.18],
73
+ ["D4", 293.66],
74
+ ["D#4", 311.13],
75
+ ["Eb4", 311.13],
76
+ ["E4", 329.63],
77
+ ["F4", 349.23],
78
+ ["F#4", 369.99],
79
+ ["Gb4", 369.99],
80
+ ["G4", 392.0],
81
+ ["G#4", 415.3],
82
+ ["Ab4", 415.3],
83
+ ["A4", 440.0],
84
+ ["A#4", 466.16],
85
+ ["Bb4", 466.16],
86
+ ["B4", 493.88],
87
+ ["C5", 523.25],
88
+ ["C#5", 554.37],
89
+ ["Db5", 554.37],
90
+ ["D5", 587.33],
91
+ ["D#5", 622.25],
92
+ ["Eb5", 622.25],
93
+ ["E5", 659.26],
94
+ ["F5", 698.46],
95
+ ["F#5", 739.99],
96
+ ["Gb5", 739.99],
97
+ ["G5", 783.99],
98
+ ["G#5", 830.61],
99
+ ["Ab5", 830.61],
100
+ ["A5", 880.0],
101
+ ["A#5", 932.33],
102
+ ["Bb5", 932.33],
103
+ ["B5", 987.77],
104
+ ["C6", 1046.5],
105
+ ["C#6", 1108.73],
106
+ ["Db6", 1108.73],
107
+ ["D6", 1174.66],
108
+ ["D#6", 1244.51],
109
+ ["Eb6", 1244.51],
110
+ ["E6", 1318.51],
111
+ ["F6", 1396.91],
112
+ ["F#6", 1479.98],
113
+ ["Gb6", 1479.98],
114
+ ["G6", 1567.98],
115
+ ["G#6", 1661.22],
116
+ ["Ab6", 1661.22],
117
+ ["A6", 1760.0],
118
+ ["A#6", 1864.66],
119
+ ["Bb6", 1864.66],
120
+ ["B6", 1975.53],
121
+ ["C7", 2093.0],
122
+ ["C#7", 2217.46],
123
+ ["Db7", 2217.46],
124
+ ["D7", 2349.32],
125
+ ["D#7", 2489.02],
126
+ ["Eb7", 2489.02],
127
+ ["E7", 2637.02],
128
+ ["F7", 2793.83],
129
+ ["F#7", 2959.96],
130
+ ["Gb7", 2959.96],
131
+ ["G7", 3135.96],
132
+ ["G#7", 3322.44],
133
+ ["Ab7", 3322.44],
134
+ ["A7", 3520.0],
135
+ ["A#7", 3729.31],
136
+ ["Bb7", 3729.31],
137
+ ["B7", 3951.07],
138
+ ["C8", 4186.01],
139
+ ["C#8", 4434.92],
140
+ ["Db8", 4434.92],
141
+ ["D8", 4698.64],
142
+ ["D#8", 4978.03],
143
+ ["Eb8", 4978.03],
144
+ ]);
145
145
 
146
146
  export default frequencyTable;
@@ -1,55 +1,75 @@
1
+ import { Message } from "webmidi";
2
+ import { TTime } from "../Timing/Time";
1
3
  import frequencyTable from "./frequencyTable";
2
4
 
3
5
  const Notes = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
4
6
 
5
- const NotesLength = Notes.length;
6
-
7
7
  const MIDI_OCTAVE_SYTSTEM = 2;
8
8
 
9
- export interface INote {
10
- note: string;
9
+ export type INote = {
10
+ name: string;
11
+ octave: number;
11
12
  frequency: number;
12
- duration: string;
13
+ duration?: TTime;
13
14
  velocity?: number;
14
- }
15
+ };
15
16
 
16
- export default class Note {
17
+ export default class Note implements INote {
17
18
  static _notes: Note[];
18
- name: string;
19
- octave: number;
19
+ name!: string;
20
+ octave!: number;
20
21
  velocity = 1;
21
- duration: string;
22
- frequency: number;
22
+ duration?: TTime;
23
23
 
24
- constructor(
25
- eventOrString: Partial<INote> | MIDIMessageEvent | string | number
26
- ) {
27
- if (typeof eventOrString === "number") {
28
- this.fromFrequency(eventOrString);
29
- } else if (typeof eventOrString === "string") {
30
- this.fromString(eventOrString);
31
- } else if (eventOrString instanceof MIDIMessageEvent) {
32
- this.fromEvent(eventOrString);
33
- } else {
34
- this.fromProps(eventOrString);
24
+ static fromFrequency(frequency: number) {
25
+ let noteName: string | undefined;
26
+
27
+ for (const [note, freq] of frequencyTable) {
28
+ if (freq !== frequency) continue;
29
+
30
+ noteName = note;
31
+ break;
35
32
  }
33
+
34
+ if (!noteName) throw Error("Not matching frequency with a note");
35
+
36
+ return new Note(noteName);
37
+ }
38
+
39
+ static fromEvent(message: Message) {
40
+ const name = Notes[message.data[1] % 12];
41
+ const octave = Math.floor(message.data[1] / 12) - 2;
42
+
43
+ return new Note(`${name}${octave}`);
36
44
  }
37
45
 
38
46
  static notes(octave = 3) {
39
47
  return Notes.map((note: string) => new Note(`${note}${octave}`));
40
48
  }
41
49
 
50
+ constructor(note: Omit<INote, "frequency"> | string) {
51
+ if (typeof note === "string") {
52
+ this.fromString(note);
53
+ } else {
54
+ this.fromProps(note);
55
+ }
56
+ }
57
+
42
58
  get isSemi() {
43
- return this.name.slice(-1) === "#";
59
+ return this.name.endsWith("#");
44
60
  }
45
61
 
46
62
  get fullName() {
47
63
  return `${this.name}${this.octave}`;
48
64
  }
49
65
 
50
- midiData(noteOn: boolean = true): Uint8Array {
66
+ get frequency(): number {
67
+ return frequencyTable.get(`${this.name}${this.octave}`)!;
68
+ }
69
+
70
+ midiData(noteOn = true): Uint8Array {
51
71
  const statusByte = noteOn ? 0x90 : 0x80;
52
- return new Uint8Array([statusByte, 0, this.velocity * 100]);
72
+ return new Uint8Array([statusByte, this.midiNumber, this.velocity * 100]);
53
73
  }
54
74
 
55
75
  get midiNumber(): number {
@@ -60,58 +80,28 @@ export default class Note {
60
80
  return Notes.indexOf(this.name);
61
81
  }
62
82
 
63
- adjustFrequency(range = 0, coarse = 0) {
64
- if (this.frequency) return this.frequency;
65
-
66
- let newOctave = this.octave + range;
67
- const coarseIndex = Notes.indexOf(this.name) + coarse;
68
- let nameIndex = coarseIndex;
69
-
70
- if (coarseIndex >= NotesLength) {
71
- newOctave++;
72
- nameIndex = coarseIndex % NotesLength;
73
- } else if (coarseIndex < 0) {
74
- newOctave--;
75
- nameIndex = NotesLength + nameIndex;
76
- }
77
- const newName = Notes[nameIndex];
78
-
79
- return frequencyTable[`${newName}${newOctave}`];
80
- }
81
-
82
83
  valueOf() {
83
84
  return this.fullName;
84
85
  }
85
86
 
86
87
  serialize(): INote {
87
88
  return {
88
- note: this.fullName,
89
+ name: this.name,
90
+ octave: this.octave,
89
91
  frequency: this.frequency,
90
92
  velocity: this.velocity,
91
93
  duration: this.duration,
92
94
  };
93
95
  }
94
96
 
95
- private fromFrequency(frequency: number) {
96
- this.frequency = frequency;
97
- }
98
-
99
97
  private fromString(string: string) {
100
- const matches = string.match(/(\w#?)(\d)?/) || [];
98
+ const matches = /(\w#?)(\d)?/.exec(string) ?? [];
101
99
 
102
100
  this.name = matches[1];
103
101
  this.octave = matches[2] ? parseInt(matches[2]) : 1;
104
102
  }
105
103
 
106
- private fromEvent(event: MIDIMessageEvent) {
107
- this.name = Notes[event.data[1] % 12];
108
- this.octave = Math.floor(event.data[1] / 12) - 2;
109
- }
110
-
111
- private fromProps(props: Partial<INote>) {
104
+ private fromProps(props: Omit<INote, "frequency">) {
112
105
  Object.assign(this, props);
113
- if (!props.note) throw Error("note props is mandatory");
114
-
115
- this.fromString(props.note);
116
106
  }
117
107
  }
@@ -0,0 +1,79 @@
1
+ import { Optional, uuidv4 } from "@blibliki/utils";
2
+ import { Engine } from "@/Engine";
3
+
4
+ type IPlug = {
5
+ moduleId: string;
6
+ ioName: string;
7
+ };
8
+
9
+ export type IRoute = {
10
+ id: string;
11
+ source: IPlug;
12
+ destination: IPlug;
13
+ };
14
+
15
+ export class Routes {
16
+ engine: Engine;
17
+ routes: Map<string, IRoute>;
18
+
19
+ constructor(engine: Engine) {
20
+ this.engine = engine;
21
+ this.routes = new Map();
22
+ }
23
+
24
+ addRoute(props: Optional<IRoute, "id">): IRoute {
25
+ const id = props.id ?? uuidv4();
26
+ const route = { ...props, id };
27
+ this.routes.set(id, route);
28
+
29
+ this.plug(id);
30
+
31
+ return route;
32
+ }
33
+
34
+ removeRoute(id: string) {
35
+ this.unPlug(id);
36
+ this.routes.delete(id);
37
+ }
38
+
39
+ clear() {
40
+ for (const id in this.routes) {
41
+ this.removeRoute(id);
42
+ }
43
+ }
44
+
45
+ private plug(id: string) {
46
+ const { sourceIO, destinationIO } = this.getIOs(id);
47
+ sourceIO.plug(destinationIO);
48
+ }
49
+
50
+ private unPlug(id: string) {
51
+ const { sourceIO, destinationIO } = this.getIOs(id);
52
+ sourceIO.unPlug(destinationIO);
53
+ }
54
+
55
+ private find(id: string): IRoute {
56
+ const route = this.routes.get(id);
57
+ if (!route) throw Error(`Route with id ${id} not found`);
58
+
59
+ return route;
60
+ }
61
+
62
+ private getIOs(id: string) {
63
+ const route = this.find(id);
64
+ const { source, destination } = route;
65
+
66
+ const sourceIO = this.engine.findIO(
67
+ source.moduleId,
68
+ source.ioName,
69
+ "output",
70
+ );
71
+ const destinationIO = this.engine.findIO(
72
+ destination.moduleId,
73
+ destination.ioName,
74
+ "input",
75
+ );
76
+
77
+ return { sourceIO, destinationIO };
78
+ }
79
+ }
@@ -0,0 +1,37 @@
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
+ }
@@ -0,0 +1,103 @@
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
+ }