@marmooo/midi-player 0.0.1 → 0.0.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.
- package/LICENSE +21 -0
- package/README.md +127 -0
- package/esm/midi-player.d.ts +6 -1
- package/esm/midi-player.d.ts.map +1 -1
- package/esm/midi-player.js +91 -76
- package/package.json +1 -1
- package/script/midi-player.d.ts +6 -1
- package/script/midi-player.d.ts.map +1 -1
- package/script/midi-player.js +91 -76
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 marmooo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# @marmooo/midi-player
|
|
2
|
+
|
|
3
|
+
`<midi-player>` HTML elements powered by
|
|
4
|
+
[Midy](https://github.com/marmooo/midy).
|
|
5
|
+
|
|
6
|
+
## Demo
|
|
7
|
+
|
|
8
|
+
- [Basic usage](https://marmooo.github.io/midi-player/)
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
1. Import icon font.
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
@font-face {
|
|
16
|
+
font-family: "MIDIPlayerIcons";
|
|
17
|
+
src: url("midi-player-icons.woff2") format("woff2");
|
|
18
|
+
}
|
|
19
|
+
.midi-player-btn {
|
|
20
|
+
font-family: MIDIPlayerIcons;
|
|
21
|
+
font-size: 24px;
|
|
22
|
+
line-height: 1;
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
2. Import the appropriate level of Midy.
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
// import { MidyGMLite as Midy } from "midy/dist/midy-GMLite.min.js";
|
|
30
|
+
// import { MidyGM1 as Midy } from "midy/dist/midy-GM1.min.js";
|
|
31
|
+
// import { MidyGM2 as Midy } from "midy/dist/midy-GM2.min.js";
|
|
32
|
+
import { Midy } from "midy/dist/midy.min.js";
|
|
33
|
+
|
|
34
|
+
const midy = new Midy(new AudioContext());
|
|
35
|
+
await midy.audioContext.suspend();
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
3. Add Player.
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
import { MIDIPlayer } from "@marmooo/midi-player";
|
|
42
|
+
|
|
43
|
+
const midiPlayer = new MIDIPlayer(midy);
|
|
44
|
+
midiPlayer.defaultLayout();
|
|
45
|
+
document.getElementById("root").appendChild(midiPlayer.root);
|
|
46
|
+
await midiPlayer.midy.loadMIDI("test.mid");
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Configuration
|
|
50
|
+
|
|
51
|
+
### SoundFont
|
|
52
|
+
|
|
53
|
+
This library supports SF2 and SF3. In addition, it supports multiple soundfonts
|
|
54
|
+
and [splitted soundfonts](https://github.com/marmooo/free-soundfonts) that are
|
|
55
|
+
optimized for playback on the web. it will automatically use splitted
|
|
56
|
+
[GeneralUser GS](https://www.schristiancollins.com/generaluser) for playback,
|
|
57
|
+
but you can also set it as follows.
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
const midiPlayer = new MIDIPlayer(midy);
|
|
61
|
+
midiPlayer.soundFontURL = "https://soundfonts.pages.dev/SGM-V2.01";
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
const midiPlayer = new MIDIPlayer(midy);
|
|
66
|
+
await midiPlayer.midy.loadSoundFont("test.sf3")
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Layout
|
|
70
|
+
|
|
71
|
+
All parts can freely change their layout by not using `defaultLayout()`.
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
const midiPlayer = new MIDIPlayer(midy);
|
|
75
|
+
const div = midiPlayer.row();
|
|
76
|
+
div.appendChild(midiPlayer.playPauseResume());
|
|
77
|
+
div.appendChild(midiPlayer.seekBar());
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Theme
|
|
81
|
+
|
|
82
|
+
All parts have midi-player-* class so you can be themed with CSS.
|
|
83
|
+
|
|
84
|
+
- Basic classes
|
|
85
|
+
- `midi-player-row`
|
|
86
|
+
- `midi-player-btn`
|
|
87
|
+
- `midi-player-range`
|
|
88
|
+
- `midi-player-text`
|
|
89
|
+
- Part classes
|
|
90
|
+
- `midi-player-play`
|
|
91
|
+
- `midi-player-pause`
|
|
92
|
+
- `midi-player-resume`
|
|
93
|
+
- `midi-player-stop`
|
|
94
|
+
- `midi-player-currTime`
|
|
95
|
+
- `midi-player-timeSeparator`
|
|
96
|
+
- `midi-player-totalTime`
|
|
97
|
+
- `midi-player-seekBar`
|
|
98
|
+
- `midi-player-volumeOn`
|
|
99
|
+
- `midi-player-volumeff`
|
|
100
|
+
- `midi-player-volumeBar`
|
|
101
|
+
|
|
102
|
+
You can also style the parts using JavaScript and CSS Framework.
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
const midiPlayer = new MIDIPlayer(midy);
|
|
106
|
+
for (const btn of root.getElementsByClassName("midi-player-btn")) {
|
|
107
|
+
btn.classList.add("btn", "btn-light-subtle", "p-1");
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Icon font
|
|
112
|
+
|
|
113
|
+
We use [Material Icons](https://github.com/marella/material-icons) licensed
|
|
114
|
+
under the
|
|
115
|
+
[Apache-2.0](https://github.com/marella/material-icons/blob/main/LICENSE).
|
|
116
|
+
Search for the ligature names you want to use from the
|
|
117
|
+
[official web app](https://marella.me/material-icons/demo/), save them, and
|
|
118
|
+
minimize them using [fontconv](https://github.com/marmooo/fontconv).
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
fontconv --ligature play_arrow,pause,stop,volume_down,volume_off \
|
|
122
|
+
material-icons.woff2 src/midi-player-icons.woff2
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
MIT
|
package/esm/midi-player.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export class MIDIPlayer {
|
|
2
2
|
constructor(midy: any);
|
|
3
|
-
|
|
3
|
+
soundFontURL: string;
|
|
4
4
|
midy: any;
|
|
5
5
|
timer: any;
|
|
6
6
|
currentTime: number;
|
|
@@ -22,8 +22,13 @@ export class MIDIPlayer {
|
|
|
22
22
|
startTimer(): void;
|
|
23
23
|
loadMIDI(file: any): Promise<void>;
|
|
24
24
|
setSoundFontDir(dir: any): void;
|
|
25
|
+
getSoundFontPaths(): string[];
|
|
25
26
|
start(): Promise<void>;
|
|
27
|
+
handleStop(): Promise<void>;
|
|
26
28
|
stop(): any;
|
|
29
|
+
handlePlay(): Promise<void>;
|
|
30
|
+
handlePause(): void;
|
|
31
|
+
handleResume(): Promise<void>;
|
|
27
32
|
playPauseResume(): any;
|
|
28
33
|
volumeText(): any;
|
|
29
34
|
volume(): any;
|
package/esm/midi-player.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midi-player.d.ts","sourceRoot":"","sources":["../src/midi-player.js"],"names":[],"mappings":"AAAA;IAcE,uBAGC;IAhBD,qBAAoE;IACpE,UAAK;IACL,WAAM;IACN,oBAAgB;IAChB,yBAAuB;IACvB,kBAAa;IACb,mBAAc;IACd,iBAAY;IACZ,cAAS;IACT,eAAU;IACV,gBAAW;IACX,cAAS;IAIP,UAAiD;IAGnD,sBAMC;IAED,WAOC;IAED,iCASC;IAED,qEASC;IAED,qEAOC;IAED,qCAKC;IAED,mBAYC;IAED,mCAKC;IAED,gCAEC;IAED,
|
|
1
|
+
{"version":3,"file":"midi-player.d.ts","sourceRoot":"","sources":["../src/midi-player.js"],"names":[],"mappings":"AAAA;IAcE,uBAGC;IAhBD,qBAAoE;IACpE,UAAK;IACL,WAAM;IACN,oBAAgB;IAChB,yBAAuB;IACvB,kBAAa;IACb,mBAAc;IACd,iBAAY;IACZ,cAAS;IACT,eAAU;IACV,gBAAW;IACX,cAAS;IAIP,UAAiD;IAGnD,sBAMC;IAED,WAOC;IAED,iCASC;IAED,qEASC;IAED,qEAOC;IAED,qCAKC;IAED,mBAYC;IAED,mCAKC;IAED,gCAEC;IAED,8BAcC;IAED,uBAaC;IAED,4BAQC;IAED,YAKC;IAED,4BAUC;IAED,oBAOC;IAED,8BAYC;IAED,uBA2BC;IAED,kBAEC;IAED,cAwCC;IAED,eAgBC;IAED,gBAIC;IAED,iBAIC;IAED,yBAWC;CACF"}
|
package/esm/midi-player.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export class MIDIPlayer {
|
|
2
2
|
constructor(midy) {
|
|
3
|
-
Object.defineProperty(this, "
|
|
3
|
+
Object.defineProperty(this, "soundFontURL", {
|
|
4
4
|
enumerable: true,
|
|
5
5
|
configurable: true,
|
|
6
6
|
writable: true,
|
|
@@ -144,111 +144,126 @@ export class MIDIPlayer {
|
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
setSoundFontDir(dir) {
|
|
147
|
-
this.
|
|
147
|
+
this.soundFontURL = dir;
|
|
148
|
+
}
|
|
149
|
+
getSoundFontPaths() {
|
|
150
|
+
const paths = [];
|
|
151
|
+
const { midy, soundFontURL } = this;
|
|
152
|
+
for (const instrument of midy.instruments) {
|
|
153
|
+
const [bankNumber, programNumber] = instrument.split(":").map(Number);
|
|
154
|
+
const table = midy.soundFontTable[programNumber];
|
|
155
|
+
if (table.has(bankNumber))
|
|
156
|
+
continue;
|
|
157
|
+
const program = programNumber.toString().padStart(3, "0");
|
|
158
|
+
const path = bankNumber === 128
|
|
159
|
+
? `${soundFontURL}/128.sf3`
|
|
160
|
+
: `${soundFontURL}/${program}.sf3`;
|
|
161
|
+
paths.push(path);
|
|
162
|
+
}
|
|
163
|
+
return paths;
|
|
148
164
|
}
|
|
149
165
|
async start() {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
continue;
|
|
155
|
-
const program = programNumber.toString().padStart(3, "0");
|
|
156
|
-
if (bankNumber === 128) {
|
|
157
|
-
await this.midy.loadSoundFont(`${this.soundFontDir}/128.sf3`);
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
await this.midy.loadSoundFont(`${this.soundFontDir}/${program}.sf3`);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
166
|
+
const midy = this.midy;
|
|
167
|
+
if (midy.soundFonts.length === 0) {
|
|
168
|
+
const paths = this.getSoundFontPaths();
|
|
169
|
+
await midy.loadSoundFont(paths);
|
|
163
170
|
}
|
|
164
171
|
this.startTimer();
|
|
165
|
-
await
|
|
172
|
+
await midy.start();
|
|
166
173
|
clearInterval(this.timer);
|
|
167
|
-
if (!
|
|
174
|
+
if (!midy.isPaused && this.currTimeNode) {
|
|
168
175
|
this.currTimeNode.textContent = "0:00";
|
|
169
176
|
this.seekBarNode.value = 0;
|
|
170
177
|
}
|
|
171
178
|
}
|
|
179
|
+
async handleStop() {
|
|
180
|
+
const midy = this.midy;
|
|
181
|
+
if (!midy.isPlaying)
|
|
182
|
+
return;
|
|
183
|
+
clearInterval(this.timer);
|
|
184
|
+
this.playNode.style.display = "initial";
|
|
185
|
+
this.pauseNode.style.display = "none";
|
|
186
|
+
this.resumeNode.style.display = "none";
|
|
187
|
+
await midy.stop();
|
|
188
|
+
}
|
|
172
189
|
stop() {
|
|
173
|
-
const stop = this.button("start", "start", "stop", "initial");
|
|
174
|
-
stop.onclick = () =>
|
|
175
|
-
if (!this.midy.isPlaying)
|
|
176
|
-
return;
|
|
177
|
-
clearInterval(this.timer);
|
|
178
|
-
this.playNode.style.display = "initial";
|
|
179
|
-
this.pauseNode.style.display = "none";
|
|
180
|
-
this.resumeNode.style.display = "none";
|
|
181
|
-
this.midy.stop();
|
|
182
|
-
};
|
|
190
|
+
const stop = this.button("start", "midi-player-start", "stop", "initial");
|
|
191
|
+
stop.onclick = () => this.handleStop();
|
|
183
192
|
this.stopNode = stop;
|
|
184
193
|
return stop;
|
|
185
194
|
}
|
|
195
|
+
async handlePlay() {
|
|
196
|
+
const { midy, playNode, pauseNode } = this;
|
|
197
|
+
if (midy.isPlaying || midy.isPaused)
|
|
198
|
+
return;
|
|
199
|
+
playNode.style.display = "none";
|
|
200
|
+
pauseNode.style.display = "initial";
|
|
201
|
+
await this.start();
|
|
202
|
+
if (!midy.isPaused) {
|
|
203
|
+
pauseNode.style.display = "none";
|
|
204
|
+
playNode.style.display = "initial";
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
handlePause() {
|
|
208
|
+
const midy = this.midy;
|
|
209
|
+
if (!midy.isPlaying || midy.isPaused)
|
|
210
|
+
return;
|
|
211
|
+
this.pauseNode.style.display = "none";
|
|
212
|
+
this.resumeNode.style.display = "initial";
|
|
213
|
+
clearInterval(this.timer);
|
|
214
|
+
midy.pause();
|
|
215
|
+
}
|
|
216
|
+
async handleResume() {
|
|
217
|
+
const { midy, playNode, pauseNode, resumeNode } = this;
|
|
218
|
+
if (!midy.isPaused)
|
|
219
|
+
return;
|
|
220
|
+
pauseNode.style.display = "initial";
|
|
221
|
+
resumeNode.style.display = "none";
|
|
222
|
+
this.startTimer();
|
|
223
|
+
await midy.resume();
|
|
224
|
+
clearInterval(this.timer);
|
|
225
|
+
if (!midy.isPaused) {
|
|
226
|
+
pauseNode.style.display = "none";
|
|
227
|
+
playNode.style.display = "initial";
|
|
228
|
+
}
|
|
229
|
+
}
|
|
186
230
|
playPauseResume() {
|
|
187
|
-
const play = this.button("start", "start", "play_arrow", "initial");
|
|
188
|
-
const pause = this.button("pause", "pause", "pause", "none");
|
|
189
|
-
const resume = this.button("resume", "resume", "play_arrow", "none");
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (!this.midy.isPaused) {
|
|
197
|
-
pause.style.display = "none";
|
|
198
|
-
play.style.display = "initial";
|
|
199
|
-
}
|
|
200
|
-
};
|
|
201
|
-
pause.onclick = () => {
|
|
202
|
-
if (!this.midy.isPlaying || this.midy.isPaused)
|
|
203
|
-
return;
|
|
204
|
-
pause.style.display = "none";
|
|
205
|
-
resume.style.display = "initial";
|
|
206
|
-
clearInterval(this.timer);
|
|
207
|
-
this.midy.pause();
|
|
208
|
-
};
|
|
209
|
-
resume.onclick = async () => {
|
|
210
|
-
if (!this.midy.isPaused)
|
|
211
|
-
return;
|
|
212
|
-
pause.style.display = "initial";
|
|
213
|
-
resume.style.display = "none";
|
|
214
|
-
this.startTimer();
|
|
215
|
-
await this.midy.resume();
|
|
216
|
-
clearInterval(this.timer);
|
|
217
|
-
if (!this.midy.isPaused) {
|
|
218
|
-
pause.style.display = "none";
|
|
219
|
-
play.style.display = "initial";
|
|
220
|
-
}
|
|
221
|
-
};
|
|
231
|
+
const play = this.button("start", "midi-player-start", "play_arrow", "initial");
|
|
232
|
+
const pause = this.button("pause", "midi-player-pause", "pause", "none");
|
|
233
|
+
const resume = this.button("resume", "midi-player-resume", "play_arrow", "none");
|
|
234
|
+
this.playNode = play;
|
|
235
|
+
this.pauseNode = pause;
|
|
236
|
+
this.resumeNode = resume;
|
|
237
|
+
play.onclick = () => this.handlePlay();
|
|
238
|
+
pause.onclick = () => this.handlePause();
|
|
239
|
+
resume.onclick = () => this.handleResume();
|
|
222
240
|
const div = document.createElement("div");
|
|
223
241
|
div.style.display = "flex";
|
|
224
242
|
div.style.alignItems = "center";
|
|
225
243
|
div.appendChild(play);
|
|
226
244
|
div.appendChild(pause);
|
|
227
245
|
div.appendChild(resume);
|
|
228
|
-
this.playNode = play;
|
|
229
|
-
this.pauseNode = pause;
|
|
230
|
-
this.resumeNode = resume;
|
|
231
246
|
return div;
|
|
232
247
|
}
|
|
233
248
|
volumeText() {
|
|
234
|
-
return this.text("volume", "volumeText");
|
|
249
|
+
return this.text("volume", "midi-player-volumeText");
|
|
235
250
|
}
|
|
236
251
|
volume() {
|
|
237
|
-
const muteOn = this.button("mute ON", "muteOn", "volume_down", "initial");
|
|
238
|
-
const muteOff = this.button("mute OFF", "muteOff", "volume_off", "none");
|
|
239
|
-
const volumeBar = this.formRange("volume", "volume", 1, "none");
|
|
252
|
+
const muteOn = this.button("mute ON", "midi-player-muteOn", "volume_down", "initial");
|
|
253
|
+
const muteOff = this.button("mute OFF", "midi-player-muteOff", "volume_off", "none");
|
|
254
|
+
const volumeBar = this.formRange("volume", "midi-player-volume", 1, "none");
|
|
240
255
|
muteOn.onclick = () => {
|
|
241
256
|
muteOn.style.display = "none";
|
|
242
257
|
muteOff.style.display = "initial";
|
|
243
|
-
this.midy.
|
|
258
|
+
this.midy.setMasterVolume(0);
|
|
244
259
|
};
|
|
245
260
|
muteOff.onclick = () => {
|
|
246
261
|
muteOn.style.display = "initial";
|
|
247
262
|
muteOff.style.display = "none";
|
|
248
|
-
this.midy.
|
|
263
|
+
this.midy.setMasterVolume(1);
|
|
249
264
|
};
|
|
250
265
|
volumeBar.oninput = (event) => {
|
|
251
|
-
this.midy.
|
|
266
|
+
this.midy.setMasterVolume(event.target.value);
|
|
252
267
|
};
|
|
253
268
|
const div = document.createElement("div");
|
|
254
269
|
div.style.display = "flex";
|
|
@@ -265,7 +280,7 @@ export class MIDIPlayer {
|
|
|
265
280
|
return div;
|
|
266
281
|
}
|
|
267
282
|
seekBar() {
|
|
268
|
-
const seekBar = this.formRange("playback position", "seekBar", 0, "initial");
|
|
283
|
+
const seekBar = this.formRange("playback position", "midi-player-seekBar", 0, "initial");
|
|
269
284
|
seekBar.oninput = (event) => {
|
|
270
285
|
const time = event.target.value * this.midy.totalTime;
|
|
271
286
|
this.midy.seekTo(time);
|
|
@@ -277,18 +292,18 @@ export class MIDIPlayer {
|
|
|
277
292
|
return seekBar;
|
|
278
293
|
}
|
|
279
294
|
currTime() {
|
|
280
|
-
const currTime = this.text("0:00", "currTime");
|
|
295
|
+
const currTime = this.text("0:00", "midi-player-currTime");
|
|
281
296
|
this.currTimeNode = currTime;
|
|
282
297
|
return currTime;
|
|
283
298
|
}
|
|
284
299
|
totalTime() {
|
|
285
|
-
const totalTime = this.text("0:00", "totalTime");
|
|
300
|
+
const totalTime = this.text("0:00", "midi-player-totalTime");
|
|
286
301
|
this.totalTimeNode = totalTime;
|
|
287
302
|
return totalTime;
|
|
288
303
|
}
|
|
289
304
|
currTimeTotalTime() {
|
|
290
305
|
const currTime = this.currTime();
|
|
291
|
-
const separator = this.text("/", "timeSeparator");
|
|
306
|
+
const separator = this.text("/", "midi-player-timeSeparator");
|
|
292
307
|
const totalTime = this.totalTime();
|
|
293
308
|
const div = document.createElement("div");
|
|
294
309
|
div.style.display = "flex";
|
package/package.json
CHANGED
package/script/midi-player.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export class MIDIPlayer {
|
|
2
2
|
constructor(midy: any);
|
|
3
|
-
|
|
3
|
+
soundFontURL: string;
|
|
4
4
|
midy: any;
|
|
5
5
|
timer: any;
|
|
6
6
|
currentTime: number;
|
|
@@ -22,8 +22,13 @@ export class MIDIPlayer {
|
|
|
22
22
|
startTimer(): void;
|
|
23
23
|
loadMIDI(file: any): Promise<void>;
|
|
24
24
|
setSoundFontDir(dir: any): void;
|
|
25
|
+
getSoundFontPaths(): string[];
|
|
25
26
|
start(): Promise<void>;
|
|
27
|
+
handleStop(): Promise<void>;
|
|
26
28
|
stop(): any;
|
|
29
|
+
handlePlay(): Promise<void>;
|
|
30
|
+
handlePause(): void;
|
|
31
|
+
handleResume(): Promise<void>;
|
|
27
32
|
playPauseResume(): any;
|
|
28
33
|
volumeText(): any;
|
|
29
34
|
volume(): any;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midi-player.d.ts","sourceRoot":"","sources":["../src/midi-player.js"],"names":[],"mappings":"AAAA;IAcE,uBAGC;IAhBD,qBAAoE;IACpE,UAAK;IACL,WAAM;IACN,oBAAgB;IAChB,yBAAuB;IACvB,kBAAa;IACb,mBAAc;IACd,iBAAY;IACZ,cAAS;IACT,eAAU;IACV,gBAAW;IACX,cAAS;IAIP,UAAiD;IAGnD,sBAMC;IAED,WAOC;IAED,iCASC;IAED,qEASC;IAED,qEAOC;IAED,qCAKC;IAED,mBAYC;IAED,mCAKC;IAED,gCAEC;IAED,
|
|
1
|
+
{"version":3,"file":"midi-player.d.ts","sourceRoot":"","sources":["../src/midi-player.js"],"names":[],"mappings":"AAAA;IAcE,uBAGC;IAhBD,qBAAoE;IACpE,UAAK;IACL,WAAM;IACN,oBAAgB;IAChB,yBAAuB;IACvB,kBAAa;IACb,mBAAc;IACd,iBAAY;IACZ,cAAS;IACT,eAAU;IACV,gBAAW;IACX,cAAS;IAIP,UAAiD;IAGnD,sBAMC;IAED,WAOC;IAED,iCASC;IAED,qEASC;IAED,qEAOC;IAED,qCAKC;IAED,mBAYC;IAED,mCAKC;IAED,gCAEC;IAED,8BAcC;IAED,uBAaC;IAED,4BAQC;IAED,YAKC;IAED,4BAUC;IAED,oBAOC;IAED,8BAYC;IAED,uBA2BC;IAED,kBAEC;IAED,cAwCC;IAED,eAgBC;IAED,gBAIC;IAED,iBAIC;IAED,yBAWC;CACF"}
|
package/script/midi-player.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.MIDIPlayer = void 0;
|
|
4
4
|
class MIDIPlayer {
|
|
5
5
|
constructor(midy) {
|
|
6
|
-
Object.defineProperty(this, "
|
|
6
|
+
Object.defineProperty(this, "soundFontURL", {
|
|
7
7
|
enumerable: true,
|
|
8
8
|
configurable: true,
|
|
9
9
|
writable: true,
|
|
@@ -147,111 +147,126 @@ class MIDIPlayer {
|
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
setSoundFontDir(dir) {
|
|
150
|
-
this.
|
|
150
|
+
this.soundFontURL = dir;
|
|
151
|
+
}
|
|
152
|
+
getSoundFontPaths() {
|
|
153
|
+
const paths = [];
|
|
154
|
+
const { midy, soundFontURL } = this;
|
|
155
|
+
for (const instrument of midy.instruments) {
|
|
156
|
+
const [bankNumber, programNumber] = instrument.split(":").map(Number);
|
|
157
|
+
const table = midy.soundFontTable[programNumber];
|
|
158
|
+
if (table.has(bankNumber))
|
|
159
|
+
continue;
|
|
160
|
+
const program = programNumber.toString().padStart(3, "0");
|
|
161
|
+
const path = bankNumber === 128
|
|
162
|
+
? `${soundFontURL}/128.sf3`
|
|
163
|
+
: `${soundFontURL}/${program}.sf3`;
|
|
164
|
+
paths.push(path);
|
|
165
|
+
}
|
|
166
|
+
return paths;
|
|
151
167
|
}
|
|
152
168
|
async start() {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
continue;
|
|
158
|
-
const program = programNumber.toString().padStart(3, "0");
|
|
159
|
-
if (bankNumber === 128) {
|
|
160
|
-
await this.midy.loadSoundFont(`${this.soundFontDir}/128.sf3`);
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
163
|
-
await this.midy.loadSoundFont(`${this.soundFontDir}/${program}.sf3`);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
169
|
+
const midy = this.midy;
|
|
170
|
+
if (midy.soundFonts.length === 0) {
|
|
171
|
+
const paths = this.getSoundFontPaths();
|
|
172
|
+
await midy.loadSoundFont(paths);
|
|
166
173
|
}
|
|
167
174
|
this.startTimer();
|
|
168
|
-
await
|
|
175
|
+
await midy.start();
|
|
169
176
|
clearInterval(this.timer);
|
|
170
|
-
if (!
|
|
177
|
+
if (!midy.isPaused && this.currTimeNode) {
|
|
171
178
|
this.currTimeNode.textContent = "0:00";
|
|
172
179
|
this.seekBarNode.value = 0;
|
|
173
180
|
}
|
|
174
181
|
}
|
|
182
|
+
async handleStop() {
|
|
183
|
+
const midy = this.midy;
|
|
184
|
+
if (!midy.isPlaying)
|
|
185
|
+
return;
|
|
186
|
+
clearInterval(this.timer);
|
|
187
|
+
this.playNode.style.display = "initial";
|
|
188
|
+
this.pauseNode.style.display = "none";
|
|
189
|
+
this.resumeNode.style.display = "none";
|
|
190
|
+
await midy.stop();
|
|
191
|
+
}
|
|
175
192
|
stop() {
|
|
176
|
-
const stop = this.button("start", "start", "stop", "initial");
|
|
177
|
-
stop.onclick = () =>
|
|
178
|
-
if (!this.midy.isPlaying)
|
|
179
|
-
return;
|
|
180
|
-
clearInterval(this.timer);
|
|
181
|
-
this.playNode.style.display = "initial";
|
|
182
|
-
this.pauseNode.style.display = "none";
|
|
183
|
-
this.resumeNode.style.display = "none";
|
|
184
|
-
this.midy.stop();
|
|
185
|
-
};
|
|
193
|
+
const stop = this.button("start", "midi-player-start", "stop", "initial");
|
|
194
|
+
stop.onclick = () => this.handleStop();
|
|
186
195
|
this.stopNode = stop;
|
|
187
196
|
return stop;
|
|
188
197
|
}
|
|
198
|
+
async handlePlay() {
|
|
199
|
+
const { midy, playNode, pauseNode } = this;
|
|
200
|
+
if (midy.isPlaying || midy.isPaused)
|
|
201
|
+
return;
|
|
202
|
+
playNode.style.display = "none";
|
|
203
|
+
pauseNode.style.display = "initial";
|
|
204
|
+
await this.start();
|
|
205
|
+
if (!midy.isPaused) {
|
|
206
|
+
pauseNode.style.display = "none";
|
|
207
|
+
playNode.style.display = "initial";
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
handlePause() {
|
|
211
|
+
const midy = this.midy;
|
|
212
|
+
if (!midy.isPlaying || midy.isPaused)
|
|
213
|
+
return;
|
|
214
|
+
this.pauseNode.style.display = "none";
|
|
215
|
+
this.resumeNode.style.display = "initial";
|
|
216
|
+
clearInterval(this.timer);
|
|
217
|
+
midy.pause();
|
|
218
|
+
}
|
|
219
|
+
async handleResume() {
|
|
220
|
+
const { midy, playNode, pauseNode, resumeNode } = this;
|
|
221
|
+
if (!midy.isPaused)
|
|
222
|
+
return;
|
|
223
|
+
pauseNode.style.display = "initial";
|
|
224
|
+
resumeNode.style.display = "none";
|
|
225
|
+
this.startTimer();
|
|
226
|
+
await midy.resume();
|
|
227
|
+
clearInterval(this.timer);
|
|
228
|
+
if (!midy.isPaused) {
|
|
229
|
+
pauseNode.style.display = "none";
|
|
230
|
+
playNode.style.display = "initial";
|
|
231
|
+
}
|
|
232
|
+
}
|
|
189
233
|
playPauseResume() {
|
|
190
|
-
const play = this.button("start", "start", "play_arrow", "initial");
|
|
191
|
-
const pause = this.button("pause", "pause", "pause", "none");
|
|
192
|
-
const resume = this.button("resume", "resume", "play_arrow", "none");
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (!this.midy.isPaused) {
|
|
200
|
-
pause.style.display = "none";
|
|
201
|
-
play.style.display = "initial";
|
|
202
|
-
}
|
|
203
|
-
};
|
|
204
|
-
pause.onclick = () => {
|
|
205
|
-
if (!this.midy.isPlaying || this.midy.isPaused)
|
|
206
|
-
return;
|
|
207
|
-
pause.style.display = "none";
|
|
208
|
-
resume.style.display = "initial";
|
|
209
|
-
clearInterval(this.timer);
|
|
210
|
-
this.midy.pause();
|
|
211
|
-
};
|
|
212
|
-
resume.onclick = async () => {
|
|
213
|
-
if (!this.midy.isPaused)
|
|
214
|
-
return;
|
|
215
|
-
pause.style.display = "initial";
|
|
216
|
-
resume.style.display = "none";
|
|
217
|
-
this.startTimer();
|
|
218
|
-
await this.midy.resume();
|
|
219
|
-
clearInterval(this.timer);
|
|
220
|
-
if (!this.midy.isPaused) {
|
|
221
|
-
pause.style.display = "none";
|
|
222
|
-
play.style.display = "initial";
|
|
223
|
-
}
|
|
224
|
-
};
|
|
234
|
+
const play = this.button("start", "midi-player-start", "play_arrow", "initial");
|
|
235
|
+
const pause = this.button("pause", "midi-player-pause", "pause", "none");
|
|
236
|
+
const resume = this.button("resume", "midi-player-resume", "play_arrow", "none");
|
|
237
|
+
this.playNode = play;
|
|
238
|
+
this.pauseNode = pause;
|
|
239
|
+
this.resumeNode = resume;
|
|
240
|
+
play.onclick = () => this.handlePlay();
|
|
241
|
+
pause.onclick = () => this.handlePause();
|
|
242
|
+
resume.onclick = () => this.handleResume();
|
|
225
243
|
const div = document.createElement("div");
|
|
226
244
|
div.style.display = "flex";
|
|
227
245
|
div.style.alignItems = "center";
|
|
228
246
|
div.appendChild(play);
|
|
229
247
|
div.appendChild(pause);
|
|
230
248
|
div.appendChild(resume);
|
|
231
|
-
this.playNode = play;
|
|
232
|
-
this.pauseNode = pause;
|
|
233
|
-
this.resumeNode = resume;
|
|
234
249
|
return div;
|
|
235
250
|
}
|
|
236
251
|
volumeText() {
|
|
237
|
-
return this.text("volume", "volumeText");
|
|
252
|
+
return this.text("volume", "midi-player-volumeText");
|
|
238
253
|
}
|
|
239
254
|
volume() {
|
|
240
|
-
const muteOn = this.button("mute ON", "muteOn", "volume_down", "initial");
|
|
241
|
-
const muteOff = this.button("mute OFF", "muteOff", "volume_off", "none");
|
|
242
|
-
const volumeBar = this.formRange("volume", "volume", 1, "none");
|
|
255
|
+
const muteOn = this.button("mute ON", "midi-player-muteOn", "volume_down", "initial");
|
|
256
|
+
const muteOff = this.button("mute OFF", "midi-player-muteOff", "volume_off", "none");
|
|
257
|
+
const volumeBar = this.formRange("volume", "midi-player-volume", 1, "none");
|
|
243
258
|
muteOn.onclick = () => {
|
|
244
259
|
muteOn.style.display = "none";
|
|
245
260
|
muteOff.style.display = "initial";
|
|
246
|
-
this.midy.
|
|
261
|
+
this.midy.setMasterVolume(0);
|
|
247
262
|
};
|
|
248
263
|
muteOff.onclick = () => {
|
|
249
264
|
muteOn.style.display = "initial";
|
|
250
265
|
muteOff.style.display = "none";
|
|
251
|
-
this.midy.
|
|
266
|
+
this.midy.setMasterVolume(1);
|
|
252
267
|
};
|
|
253
268
|
volumeBar.oninput = (event) => {
|
|
254
|
-
this.midy.
|
|
269
|
+
this.midy.setMasterVolume(event.target.value);
|
|
255
270
|
};
|
|
256
271
|
const div = document.createElement("div");
|
|
257
272
|
div.style.display = "flex";
|
|
@@ -268,7 +283,7 @@ class MIDIPlayer {
|
|
|
268
283
|
return div;
|
|
269
284
|
}
|
|
270
285
|
seekBar() {
|
|
271
|
-
const seekBar = this.formRange("playback position", "seekBar", 0, "initial");
|
|
286
|
+
const seekBar = this.formRange("playback position", "midi-player-seekBar", 0, "initial");
|
|
272
287
|
seekBar.oninput = (event) => {
|
|
273
288
|
const time = event.target.value * this.midy.totalTime;
|
|
274
289
|
this.midy.seekTo(time);
|
|
@@ -280,18 +295,18 @@ class MIDIPlayer {
|
|
|
280
295
|
return seekBar;
|
|
281
296
|
}
|
|
282
297
|
currTime() {
|
|
283
|
-
const currTime = this.text("0:00", "currTime");
|
|
298
|
+
const currTime = this.text("0:00", "midi-player-currTime");
|
|
284
299
|
this.currTimeNode = currTime;
|
|
285
300
|
return currTime;
|
|
286
301
|
}
|
|
287
302
|
totalTime() {
|
|
288
|
-
const totalTime = this.text("0:00", "totalTime");
|
|
303
|
+
const totalTime = this.text("0:00", "midi-player-totalTime");
|
|
289
304
|
this.totalTimeNode = totalTime;
|
|
290
305
|
return totalTime;
|
|
291
306
|
}
|
|
292
307
|
currTimeTotalTime() {
|
|
293
308
|
const currTime = this.currTime();
|
|
294
|
-
const separator = this.text("/", "timeSeparator");
|
|
309
|
+
const separator = this.text("/", "midi-player-timeSeparator");
|
|
295
310
|
const totalTime = this.totalTime();
|
|
296
311
|
const div = document.createElement("div");
|
|
297
312
|
div.style.display = "flex";
|