@marmooo/midi-player 0.0.1 → 0.0.3
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 +90 -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 +90 -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/GeneralUser_GS_v1.471";
|
|
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,8BAaC;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,125 @@ 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 [bank, program] = instrument.split(":");
|
|
154
|
+
const bankNumber = Number(bank);
|
|
155
|
+
const programNumber = Number(program);
|
|
156
|
+
const index = midy.soundFontTable[programNumber][bankNumber];
|
|
157
|
+
if (index !== undefined)
|
|
158
|
+
continue;
|
|
159
|
+
const baseName = bankNumber === 128 ? "128" : program;
|
|
160
|
+
paths.push(`${soundFontURL}/${baseName}.sf3`);
|
|
161
|
+
}
|
|
162
|
+
return paths;
|
|
148
163
|
}
|
|
149
164
|
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
|
-
}
|
|
165
|
+
const midy = this.midy;
|
|
166
|
+
if (midy.soundFonts.length === 0) {
|
|
167
|
+
const paths = this.getSoundFontPaths();
|
|
168
|
+
await midy.loadSoundFont(paths);
|
|
163
169
|
}
|
|
164
170
|
this.startTimer();
|
|
165
|
-
await
|
|
171
|
+
await midy.start();
|
|
166
172
|
clearInterval(this.timer);
|
|
167
|
-
if (!
|
|
173
|
+
if (!midy.isPaused && this.currTimeNode) {
|
|
168
174
|
this.currTimeNode.textContent = "0:00";
|
|
169
175
|
this.seekBarNode.value = 0;
|
|
170
176
|
}
|
|
171
177
|
}
|
|
178
|
+
async handleStop() {
|
|
179
|
+
const midy = this.midy;
|
|
180
|
+
if (!midy.isPlaying)
|
|
181
|
+
return;
|
|
182
|
+
clearInterval(this.timer);
|
|
183
|
+
this.playNode.style.display = "initial";
|
|
184
|
+
this.pauseNode.style.display = "none";
|
|
185
|
+
this.resumeNode.style.display = "none";
|
|
186
|
+
await midy.stop();
|
|
187
|
+
}
|
|
172
188
|
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
|
-
};
|
|
189
|
+
const stop = this.button("start", "midi-player-start", "stop", "initial");
|
|
190
|
+
stop.onclick = () => this.handleStop();
|
|
183
191
|
this.stopNode = stop;
|
|
184
192
|
return stop;
|
|
185
193
|
}
|
|
194
|
+
async handlePlay() {
|
|
195
|
+
const { midy, playNode, pauseNode } = this;
|
|
196
|
+
if (midy.isPlaying || midy.isPaused)
|
|
197
|
+
return;
|
|
198
|
+
playNode.style.display = "none";
|
|
199
|
+
pauseNode.style.display = "initial";
|
|
200
|
+
await this.start();
|
|
201
|
+
if (!midy.isPaused) {
|
|
202
|
+
pauseNode.style.display = "none";
|
|
203
|
+
playNode.style.display = "initial";
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
handlePause() {
|
|
207
|
+
const midy = this.midy;
|
|
208
|
+
if (!midy.isPlaying || midy.isPaused)
|
|
209
|
+
return;
|
|
210
|
+
this.pauseNode.style.display = "none";
|
|
211
|
+
this.resumeNode.style.display = "initial";
|
|
212
|
+
clearInterval(this.timer);
|
|
213
|
+
midy.pause();
|
|
214
|
+
}
|
|
215
|
+
async handleResume() {
|
|
216
|
+
const { midy, playNode, pauseNode, resumeNode } = this;
|
|
217
|
+
if (!midy.isPaused)
|
|
218
|
+
return;
|
|
219
|
+
pauseNode.style.display = "initial";
|
|
220
|
+
resumeNode.style.display = "none";
|
|
221
|
+
this.startTimer();
|
|
222
|
+
await midy.resume();
|
|
223
|
+
clearInterval(this.timer);
|
|
224
|
+
if (!midy.isPaused) {
|
|
225
|
+
pauseNode.style.display = "none";
|
|
226
|
+
playNode.style.display = "initial";
|
|
227
|
+
}
|
|
228
|
+
}
|
|
186
229
|
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
|
-
};
|
|
230
|
+
const play = this.button("start", "midi-player-start", "play_arrow", "initial");
|
|
231
|
+
const pause = this.button("pause", "midi-player-pause", "pause", "none");
|
|
232
|
+
const resume = this.button("resume", "midi-player-resume", "play_arrow", "none");
|
|
233
|
+
this.playNode = play;
|
|
234
|
+
this.pauseNode = pause;
|
|
235
|
+
this.resumeNode = resume;
|
|
236
|
+
play.onclick = () => this.handlePlay();
|
|
237
|
+
pause.onclick = () => this.handlePause();
|
|
238
|
+
resume.onclick = () => this.handleResume();
|
|
222
239
|
const div = document.createElement("div");
|
|
223
240
|
div.style.display = "flex";
|
|
224
241
|
div.style.alignItems = "center";
|
|
225
242
|
div.appendChild(play);
|
|
226
243
|
div.appendChild(pause);
|
|
227
244
|
div.appendChild(resume);
|
|
228
|
-
this.playNode = play;
|
|
229
|
-
this.pauseNode = pause;
|
|
230
|
-
this.resumeNode = resume;
|
|
231
245
|
return div;
|
|
232
246
|
}
|
|
233
247
|
volumeText() {
|
|
234
|
-
return this.text("volume", "volumeText");
|
|
248
|
+
return this.text("volume", "midi-player-volumeText");
|
|
235
249
|
}
|
|
236
250
|
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");
|
|
251
|
+
const muteOn = this.button("mute ON", "midi-player-muteOn", "volume_down", "initial");
|
|
252
|
+
const muteOff = this.button("mute OFF", "midi-player-muteOff", "volume_off", "none");
|
|
253
|
+
const volumeBar = this.formRange("volume", "midi-player-volume", 1, "none");
|
|
240
254
|
muteOn.onclick = () => {
|
|
241
255
|
muteOn.style.display = "none";
|
|
242
256
|
muteOff.style.display = "initial";
|
|
243
|
-
this.midy.
|
|
257
|
+
this.midy.setMasterVolume(0);
|
|
244
258
|
};
|
|
245
259
|
muteOff.onclick = () => {
|
|
246
260
|
muteOn.style.display = "initial";
|
|
247
261
|
muteOff.style.display = "none";
|
|
248
|
-
this.midy.
|
|
262
|
+
this.midy.setMasterVolume(1);
|
|
249
263
|
};
|
|
250
264
|
volumeBar.oninput = (event) => {
|
|
251
|
-
this.midy.
|
|
265
|
+
this.midy.setMasterVolume(event.target.value);
|
|
252
266
|
};
|
|
253
267
|
const div = document.createElement("div");
|
|
254
268
|
div.style.display = "flex";
|
|
@@ -265,7 +279,7 @@ export class MIDIPlayer {
|
|
|
265
279
|
return div;
|
|
266
280
|
}
|
|
267
281
|
seekBar() {
|
|
268
|
-
const seekBar = this.formRange("playback position", "seekBar", 0, "initial");
|
|
282
|
+
const seekBar = this.formRange("playback position", "midi-player-seekBar", 0, "initial");
|
|
269
283
|
seekBar.oninput = (event) => {
|
|
270
284
|
const time = event.target.value * this.midy.totalTime;
|
|
271
285
|
this.midy.seekTo(time);
|
|
@@ -277,18 +291,18 @@ export class MIDIPlayer {
|
|
|
277
291
|
return seekBar;
|
|
278
292
|
}
|
|
279
293
|
currTime() {
|
|
280
|
-
const currTime = this.text("0:00", "currTime");
|
|
294
|
+
const currTime = this.text("0:00", "midi-player-currTime");
|
|
281
295
|
this.currTimeNode = currTime;
|
|
282
296
|
return currTime;
|
|
283
297
|
}
|
|
284
298
|
totalTime() {
|
|
285
|
-
const totalTime = this.text("0:00", "totalTime");
|
|
299
|
+
const totalTime = this.text("0:00", "midi-player-totalTime");
|
|
286
300
|
this.totalTimeNode = totalTime;
|
|
287
301
|
return totalTime;
|
|
288
302
|
}
|
|
289
303
|
currTimeTotalTime() {
|
|
290
304
|
const currTime = this.currTime();
|
|
291
|
-
const separator = this.text("/", "timeSeparator");
|
|
305
|
+
const separator = this.text("/", "midi-player-timeSeparator");
|
|
292
306
|
const totalTime = this.totalTime();
|
|
293
307
|
const div = document.createElement("div");
|
|
294
308
|
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,8BAaC;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,125 @@ 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 [bank, program] = instrument.split(":");
|
|
157
|
+
const bankNumber = Number(bank);
|
|
158
|
+
const programNumber = Number(program);
|
|
159
|
+
const index = midy.soundFontTable[programNumber][bankNumber];
|
|
160
|
+
if (index !== undefined)
|
|
161
|
+
continue;
|
|
162
|
+
const baseName = bankNumber === 128 ? "128" : program;
|
|
163
|
+
paths.push(`${soundFontURL}/${baseName}.sf3`);
|
|
164
|
+
}
|
|
165
|
+
return paths;
|
|
151
166
|
}
|
|
152
167
|
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
|
-
}
|
|
168
|
+
const midy = this.midy;
|
|
169
|
+
if (midy.soundFonts.length === 0) {
|
|
170
|
+
const paths = this.getSoundFontPaths();
|
|
171
|
+
await midy.loadSoundFont(paths);
|
|
166
172
|
}
|
|
167
173
|
this.startTimer();
|
|
168
|
-
await
|
|
174
|
+
await midy.start();
|
|
169
175
|
clearInterval(this.timer);
|
|
170
|
-
if (!
|
|
176
|
+
if (!midy.isPaused && this.currTimeNode) {
|
|
171
177
|
this.currTimeNode.textContent = "0:00";
|
|
172
178
|
this.seekBarNode.value = 0;
|
|
173
179
|
}
|
|
174
180
|
}
|
|
181
|
+
async handleStop() {
|
|
182
|
+
const midy = this.midy;
|
|
183
|
+
if (!midy.isPlaying)
|
|
184
|
+
return;
|
|
185
|
+
clearInterval(this.timer);
|
|
186
|
+
this.playNode.style.display = "initial";
|
|
187
|
+
this.pauseNode.style.display = "none";
|
|
188
|
+
this.resumeNode.style.display = "none";
|
|
189
|
+
await midy.stop();
|
|
190
|
+
}
|
|
175
191
|
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
|
-
};
|
|
192
|
+
const stop = this.button("start", "midi-player-start", "stop", "initial");
|
|
193
|
+
stop.onclick = () => this.handleStop();
|
|
186
194
|
this.stopNode = stop;
|
|
187
195
|
return stop;
|
|
188
196
|
}
|
|
197
|
+
async handlePlay() {
|
|
198
|
+
const { midy, playNode, pauseNode } = this;
|
|
199
|
+
if (midy.isPlaying || midy.isPaused)
|
|
200
|
+
return;
|
|
201
|
+
playNode.style.display = "none";
|
|
202
|
+
pauseNode.style.display = "initial";
|
|
203
|
+
await this.start();
|
|
204
|
+
if (!midy.isPaused) {
|
|
205
|
+
pauseNode.style.display = "none";
|
|
206
|
+
playNode.style.display = "initial";
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
handlePause() {
|
|
210
|
+
const midy = this.midy;
|
|
211
|
+
if (!midy.isPlaying || midy.isPaused)
|
|
212
|
+
return;
|
|
213
|
+
this.pauseNode.style.display = "none";
|
|
214
|
+
this.resumeNode.style.display = "initial";
|
|
215
|
+
clearInterval(this.timer);
|
|
216
|
+
midy.pause();
|
|
217
|
+
}
|
|
218
|
+
async handleResume() {
|
|
219
|
+
const { midy, playNode, pauseNode, resumeNode } = this;
|
|
220
|
+
if (!midy.isPaused)
|
|
221
|
+
return;
|
|
222
|
+
pauseNode.style.display = "initial";
|
|
223
|
+
resumeNode.style.display = "none";
|
|
224
|
+
this.startTimer();
|
|
225
|
+
await midy.resume();
|
|
226
|
+
clearInterval(this.timer);
|
|
227
|
+
if (!midy.isPaused) {
|
|
228
|
+
pauseNode.style.display = "none";
|
|
229
|
+
playNode.style.display = "initial";
|
|
230
|
+
}
|
|
231
|
+
}
|
|
189
232
|
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
|
-
};
|
|
233
|
+
const play = this.button("start", "midi-player-start", "play_arrow", "initial");
|
|
234
|
+
const pause = this.button("pause", "midi-player-pause", "pause", "none");
|
|
235
|
+
const resume = this.button("resume", "midi-player-resume", "play_arrow", "none");
|
|
236
|
+
this.playNode = play;
|
|
237
|
+
this.pauseNode = pause;
|
|
238
|
+
this.resumeNode = resume;
|
|
239
|
+
play.onclick = () => this.handlePlay();
|
|
240
|
+
pause.onclick = () => this.handlePause();
|
|
241
|
+
resume.onclick = () => this.handleResume();
|
|
225
242
|
const div = document.createElement("div");
|
|
226
243
|
div.style.display = "flex";
|
|
227
244
|
div.style.alignItems = "center";
|
|
228
245
|
div.appendChild(play);
|
|
229
246
|
div.appendChild(pause);
|
|
230
247
|
div.appendChild(resume);
|
|
231
|
-
this.playNode = play;
|
|
232
|
-
this.pauseNode = pause;
|
|
233
|
-
this.resumeNode = resume;
|
|
234
248
|
return div;
|
|
235
249
|
}
|
|
236
250
|
volumeText() {
|
|
237
|
-
return this.text("volume", "volumeText");
|
|
251
|
+
return this.text("volume", "midi-player-volumeText");
|
|
238
252
|
}
|
|
239
253
|
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");
|
|
254
|
+
const muteOn = this.button("mute ON", "midi-player-muteOn", "volume_down", "initial");
|
|
255
|
+
const muteOff = this.button("mute OFF", "midi-player-muteOff", "volume_off", "none");
|
|
256
|
+
const volumeBar = this.formRange("volume", "midi-player-volume", 1, "none");
|
|
243
257
|
muteOn.onclick = () => {
|
|
244
258
|
muteOn.style.display = "none";
|
|
245
259
|
muteOff.style.display = "initial";
|
|
246
|
-
this.midy.
|
|
260
|
+
this.midy.setMasterVolume(0);
|
|
247
261
|
};
|
|
248
262
|
muteOff.onclick = () => {
|
|
249
263
|
muteOn.style.display = "initial";
|
|
250
264
|
muteOff.style.display = "none";
|
|
251
|
-
this.midy.
|
|
265
|
+
this.midy.setMasterVolume(1);
|
|
252
266
|
};
|
|
253
267
|
volumeBar.oninput = (event) => {
|
|
254
|
-
this.midy.
|
|
268
|
+
this.midy.setMasterVolume(event.target.value);
|
|
255
269
|
};
|
|
256
270
|
const div = document.createElement("div");
|
|
257
271
|
div.style.display = "flex";
|
|
@@ -268,7 +282,7 @@ class MIDIPlayer {
|
|
|
268
282
|
return div;
|
|
269
283
|
}
|
|
270
284
|
seekBar() {
|
|
271
|
-
const seekBar = this.formRange("playback position", "seekBar", 0, "initial");
|
|
285
|
+
const seekBar = this.formRange("playback position", "midi-player-seekBar", 0, "initial");
|
|
272
286
|
seekBar.oninput = (event) => {
|
|
273
287
|
const time = event.target.value * this.midy.totalTime;
|
|
274
288
|
this.midy.seekTo(time);
|
|
@@ -280,18 +294,18 @@ class MIDIPlayer {
|
|
|
280
294
|
return seekBar;
|
|
281
295
|
}
|
|
282
296
|
currTime() {
|
|
283
|
-
const currTime = this.text("0:00", "currTime");
|
|
297
|
+
const currTime = this.text("0:00", "midi-player-currTime");
|
|
284
298
|
this.currTimeNode = currTime;
|
|
285
299
|
return currTime;
|
|
286
300
|
}
|
|
287
301
|
totalTime() {
|
|
288
|
-
const totalTime = this.text("0:00", "totalTime");
|
|
302
|
+
const totalTime = this.text("0:00", "midi-player-totalTime");
|
|
289
303
|
this.totalTimeNode = totalTime;
|
|
290
304
|
return totalTime;
|
|
291
305
|
}
|
|
292
306
|
currTimeTotalTime() {
|
|
293
307
|
const currTime = this.currTime();
|
|
294
|
-
const separator = this.text("/", "timeSeparator");
|
|
308
|
+
const separator = this.text("/", "midi-player-timeSeparator");
|
|
295
309
|
const totalTime = this.totalTime();
|
|
296
310
|
const div = document.createElement("div");
|
|
297
311
|
div.style.display = "flex";
|