@modernized/osmd-audio-player 1.0.1

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 (50) hide show
  1. package/.circleci/config.yml +18 -0
  2. package/.gitattributes +12 -0
  3. package/.github/workflows/ci.yml +23 -0
  4. package/.prettierrc +4 -0
  5. package/.prettierrc.js +4 -0
  6. package/CLAUDE.md +96 -0
  7. package/LICENSE +21 -0
  8. package/README.md +79 -0
  9. package/dist/PlaybackEngine.d.ts +55 -0
  10. package/dist/PlaybackEngine.js +211 -0
  11. package/dist/PlaybackScheduler.d.ts +34 -0
  12. package/dist/PlaybackScheduler.js +105 -0
  13. package/dist/index.d.ts +2 -0
  14. package/dist/index.js +2 -0
  15. package/dist/internals/EventEmitter.d.ts +7 -0
  16. package/dist/internals/EventEmitter.js +17 -0
  17. package/dist/internals/StepQueue.d.ts +16 -0
  18. package/dist/internals/StepQueue.js +32 -0
  19. package/dist/internals/noteHelpers.d.ts +5 -0
  20. package/dist/internals/noteHelpers.js +24 -0
  21. package/dist/midi/midiInstruments.d.ts +1 -0
  22. package/dist/midi/midiInstruments.js +130 -0
  23. package/dist/players/InstrumentPlayer.d.ts +15 -0
  24. package/dist/players/InstrumentPlayer.js +1 -0
  25. package/dist/players/NotePlaybackOptions.d.ts +13 -0
  26. package/dist/players/NotePlaybackOptions.js +6 -0
  27. package/dist/players/SoundfontPlayer.d.ts +17 -0
  28. package/dist/players/SoundfontPlayer.js +54 -0
  29. package/dist/players/musyngkiteInstruments.d.ts +2 -0
  30. package/dist/players/musyngkiteInstruments.js +130 -0
  31. package/eslint.config.js +12 -0
  32. package/package.json +66 -0
  33. package/rollup.umd.config.js +23 -0
  34. package/src/PlaybackEngine.test.ts +99 -0
  35. package/src/PlaybackEngine.ts +294 -0
  36. package/src/PlaybackScheduler.ts +146 -0
  37. package/src/index.ts +3 -0
  38. package/src/internals/EventEmitter.test.ts +57 -0
  39. package/src/internals/EventEmitter.ts +19 -0
  40. package/src/internals/StepQueue.ts +45 -0
  41. package/src/internals/noteHelpers.ts +26 -0
  42. package/src/midi/midiInstruments.ts +130 -0
  43. package/src/players/InstrumentPlayer.ts +17 -0
  44. package/src/players/NotePlaybackOptions.ts +15 -0
  45. package/src/players/SoundfontPlayer.ts +76 -0
  46. package/src/players/musyngkiteInstruments.ts +130 -0
  47. package/tsconfig.build.json +7 -0
  48. package/tsconfig.json +14 -0
  49. package/umd/OsmdAudioPlayer.min.js +15 -0
  50. package/vitest.config.ts +7 -0
@@ -0,0 +1,130 @@
1
+ export const midiInstruments: [number, string][] = [
2
+ [0, "Acoustic Grand Piano"],
3
+ [1, "Bright Acoustic Piano"],
4
+ [2, "Electric Grand Piano"],
5
+ [3, "Honky-tonk Piano"],
6
+ [4, "Electric Piano 1"],
7
+ [5, "Electric Piano 2"],
8
+ [6, "Harpsichord"],
9
+ [7, "Clavi"],
10
+ [8, "Celesta"],
11
+ [9, "Glockenspiel"],
12
+ [10, "Music Box"],
13
+ [11, "Vibraphone"],
14
+ [12, "Marimba"],
15
+ [13, "Xylophone"],
16
+ [14, "Tubular Bells"],
17
+ [15, "Dulcimer"],
18
+ [16, "Drawbar Organ"],
19
+ [17, "Percussive Organ"],
20
+ [18, "Rock Organ"],
21
+ [19, "Church Organ"],
22
+ [20, "Reed Organ"],
23
+ [21, "Accordion"],
24
+ [22, "Harmonica"],
25
+ [23, "Tango Accordion"],
26
+ [24, "Acoustic Guitar (nylon)"],
27
+ [25, "Acoustic Guitar (steel)"],
28
+ [26, "Electric Guitar (jazz)"],
29
+ [27, "Electric Guitar (clean)"],
30
+ [28, "Electric Guitar (muted)"],
31
+ [29, "Overdriven Guitar"],
32
+ [30, "Distortion Guitar"],
33
+ [31, "Guitar harmonics"],
34
+ [32, "Acoustic Bass"],
35
+ [33, "Electric Bass (finger)"],
36
+ [34, "Electric Bass (pick)"],
37
+ [35, "Fretless Bass"],
38
+ [36, "Slap Bass 1"],
39
+ [37, "Slap Bass 2"],
40
+ [38, "Synth Bass 1"],
41
+ [39, "Synth Bass 2"],
42
+ [40, "Violin"],
43
+ [41, "Viola"],
44
+ [42, "Cello"],
45
+ [43, "Contrabass"],
46
+ [44, "Tremolo Strings"],
47
+ [45, "Pizzicato Strings"],
48
+ [46, "Orchestral Harp"],
49
+ [47, "Timpani"],
50
+ [48, "String Ensemble 1"],
51
+ [49, "String Ensemble 2"],
52
+ [50, "SynthStrings 1"],
53
+ [51, "SynthStrings 2"],
54
+ [52, "Choir Aahs"],
55
+ [53, "Voice Oohs"],
56
+ [54, "Synth Choir"],
57
+ [55, "Orchestra Hit"],
58
+ [56, "Trumpet"],
59
+ [57, "Trombone"],
60
+ [58, "Tuba"],
61
+ [59, "Muted Trumpet"],
62
+ [60, "French Horn"],
63
+ [61, "Brass Section"],
64
+ [62, "SynthBrass 1"],
65
+ [63, "SynthBrass 2"],
66
+ [64, "Soprano Sax"],
67
+ [65, "Alto Sax"],
68
+ [66, "Tenor Sax"],
69
+ [67, "Baritone Sax"],
70
+ [68, "Oboe"],
71
+ [69, "English Horn"],
72
+ [70, "Bassoon"],
73
+ [71, "Clarinet"],
74
+ [72, "Piccolo"],
75
+ [73, "Flute"],
76
+ [74, "Recorder"],
77
+ [75, "Pan Flute"],
78
+ [76, "Blown Bottle"],
79
+ [77, "Shakuhachi"],
80
+ [78, "Whistle"],
81
+ [79, "Ocarina"],
82
+ [80, "Lead 1 (square)"],
83
+ [81, "Lead 2 (sawtooth)"],
84
+ [82, "Lead 3 (calliope)"],
85
+ [83, "Lead 4 (chiff)"],
86
+ [84, "Lead 5 (charang)"],
87
+ [85, "Lead 6 (voice)"],
88
+ [86, "Lead 7 (fifths)"],
89
+ [87, "Lead 8 (bass + lead)"],
90
+ [88, "Pad 1 (new age)"],
91
+ [89, "Pad 2 (warm)"],
92
+ [90, "Pad 3 (polysynth)"],
93
+ [91, "Pad 4 (choir)"],
94
+ [92, "Pad 5 (bowed)"],
95
+ [93, "Pad 6 (metallic)"],
96
+ [94, "Pad 7 (halo)"],
97
+ [95, "Pad 8 (sweep)"],
98
+ [96, "FX 1 (rain)"],
99
+ [97, "FX 2 (soundtrack)"],
100
+ [98, "FX 3 (crystal)"],
101
+ [99, "FX 4 (atmosphere)"],
102
+ [100, "FX 5 (brightness)"],
103
+ [101, "FX 6 (goblins)"],
104
+ [102, "FX 7 (echoes)"],
105
+ [103, "FX 8 (sci-fi)"],
106
+ [104, "Sitar"],
107
+ [105, "Banjo"],
108
+ [106, "Shamisen"],
109
+ [107, "Koto"],
110
+ [108, "Kalimba"],
111
+ [109, "Bag pipe"],
112
+ [110, "Fiddle"],
113
+ [111, "Shanai"],
114
+ [112, "Tinkle Bell"],
115
+ [113, "Agogo"],
116
+ [114, "Steel Drums"],
117
+ [115, "Woodblock"],
118
+ [116, "Taiko Drum"],
119
+ [117, "Melodic Tom"],
120
+ [118, "Synth Drum"],
121
+ [119, "Reverse Cymbal"],
122
+ [120, "Guitar Fret Noise"],
123
+ [121, "Breath Noise"],
124
+ [122, "Seashore"],
125
+ [123, "Bird Tweet"],
126
+ [124, "Telephone Ring"],
127
+ [125, "Helicopter"],
128
+ [126, "Applause"],
129
+ [127, "Gunshot"],
130
+ ];
@@ -0,0 +1,17 @@
1
+ import { NotePlaybackInstruction } from "./NotePlaybackOptions";
2
+ import { IAudioContext } from "standardized-audio-context";
3
+
4
+ export interface PlaybackInstrument {
5
+ midiId: number;
6
+ name: string;
7
+ loaded: boolean;
8
+ }
9
+
10
+ export interface InstrumentPlayer {
11
+ instruments: PlaybackInstrument[];
12
+ init: (audioContext: IAudioContext) => void;
13
+ load: (midiId: number) => Promise<void>;
14
+ schedule: (midiId: number, time: number, notes: NotePlaybackInstruction[]) => void;
15
+ play: (midiId: number, options: NotePlaybackInstruction) => void;
16
+ stop: (midiId: number) => void;
17
+ }
@@ -0,0 +1,15 @@
1
+ export enum ArticulationStyle {
2
+ None,
3
+ Staccato,
4
+ Legato,
5
+ }
6
+
7
+ export interface NotePlaybackStyle {
8
+ articulation: ArticulationStyle;
9
+ }
10
+
11
+ export interface NotePlaybackInstruction extends NotePlaybackStyle {
12
+ note: number;
13
+ gain: number;
14
+ duration: number;
15
+ }
@@ -0,0 +1,76 @@
1
+ import { InstrumentPlayer, PlaybackInstrument } from "./InstrumentPlayer";
2
+ import {
3
+ NotePlaybackStyle,
4
+ NotePlaybackInstruction,
5
+ ArticulationStyle,
6
+ } from "./NotePlaybackOptions";
7
+ import { midiInstruments } from "../midi/midiInstruments";
8
+ import { IAudioContext } from "standardized-audio-context";
9
+ import supportedSoundfontInstruments from "./musyngkiteInstruments";
10
+ import * as Soundfont from "soundfont-player";
11
+
12
+ export class SoundfontPlayer implements InstrumentPlayer {
13
+ public instruments: PlaybackInstrument[];
14
+
15
+ private players: Map<number, Soundfont.Player> = new Map();
16
+ private audioContext: IAudioContext;
17
+
18
+ constructor() {
19
+ this.instruments = midiInstruments
20
+ .filter((i) => supportedSoundfontInstruments.includes(this.getSoundfontInstrumentName(i[1])))
21
+ .map((i) => ({
22
+ midiId: i[0],
23
+ name: i[1],
24
+ loaded: false,
25
+ }));
26
+ }
27
+
28
+ init(audioContext: IAudioContext) {
29
+ this.audioContext = audioContext;
30
+ }
31
+
32
+ async load(midiId: number) {
33
+ const instrument = this.instruments.find((i) => i.midiId === midiId);
34
+ if (!instrument) {
35
+ throw new Error("SoundfontPlayer does not support midi instrument ID " + midiId);
36
+ }
37
+ if (this.players.has(midiId)) return;
38
+
39
+ const player = await Soundfont.instrument(
40
+ this.audioContext as unknown as AudioContext,
41
+ this.getSoundfontInstrumentName(instrument.name) as Soundfont.InstrumentName,
42
+ );
43
+ this.players.set(midiId, player);
44
+ }
45
+
46
+ play: (midiId: string | number, options: NotePlaybackStyle) => void;
47
+
48
+ stop(midiId: number) {
49
+ if (!this.players.has(midiId)) return;
50
+ this.players.get(midiId).stop();
51
+ }
52
+
53
+ schedule(midiId: number, time: number, notes: NotePlaybackInstruction[]) {
54
+ this.verifyPlayerLoaded(midiId);
55
+ this.applyDynamics(notes);
56
+ this.players.get(midiId).schedule(time, notes);
57
+ }
58
+
59
+ private applyDynamics(notes: NotePlaybackInstruction[]): void {
60
+ for (const note of notes) {
61
+ if (note.articulation === ArticulationStyle.Staccato) {
62
+ note.gain = Math.max(note.gain + 0.3, note.gain * 1.3);
63
+ note.duration = Math.min(note.duration * 0.4, 0.4);
64
+ }
65
+ }
66
+ }
67
+
68
+ private verifyPlayerLoaded(midiId: number) {
69
+ if (!this.players.has(midiId))
70
+ throw new Error("No soundfont player loaded for midi instrument " + midiId);
71
+ }
72
+
73
+ private getSoundfontInstrumentName(midiName: string): string {
74
+ return midiName.toLowerCase().replace(/\s+/g, "_");
75
+ }
76
+ }
@@ -0,0 +1,130 @@
1
+ export default [
2
+ "accordion",
3
+ "acoustic_bass",
4
+ "acoustic_grand_piano",
5
+ "acoustic_guitar_nylon",
6
+ "acoustic_guitar_steel",
7
+ "agogo",
8
+ "alto_sax",
9
+ "applause",
10
+ "bagpipe",
11
+ "banjo",
12
+ "baritone_sax",
13
+ "bassoon",
14
+ "bird_tweet",
15
+ "blown_bottle",
16
+ "brass_section",
17
+ "breath_noise",
18
+ "bright_acoustic_piano",
19
+ "celesta",
20
+ "cello",
21
+ "choir_aahs",
22
+ "church_organ",
23
+ "clarinet",
24
+ "clavinet",
25
+ "contrabass",
26
+ "distortion_guitar",
27
+ "drawbar_organ",
28
+ "dulcimer",
29
+ "electric_bass_finger",
30
+ "electric_bass_pick",
31
+ "electric_grand_piano",
32
+ "electric_guitar_clean",
33
+ "electric_guitar_jazz",
34
+ "electric_guitar_muted",
35
+ "electric_piano_1",
36
+ "electric_piano_2",
37
+ "english_horn",
38
+ "fiddle",
39
+ "flute",
40
+ "french_horn",
41
+ "fretless_bass",
42
+ "fx_1_rain",
43
+ "fx_2_soundtrack",
44
+ "fx_3_crystal",
45
+ "fx_4_atmosphere",
46
+ "fx_5_brightness",
47
+ "fx_6_goblins",
48
+ "fx_7_echoes",
49
+ "fx_8_scifi",
50
+ "glockenspiel",
51
+ "guitar_fret_noise",
52
+ "guitar_harmonics",
53
+ "gunshot",
54
+ "harmonica",
55
+ "harpsichord",
56
+ "helicopter",
57
+ "honkytonk_piano",
58
+ "kalimba",
59
+ "koto",
60
+ "lead_1_square",
61
+ "lead_2_sawtooth",
62
+ "lead_3_calliope",
63
+ "lead_4_chiff",
64
+ "lead_5_charang",
65
+ "lead_6_voice",
66
+ "lead_7_fifths",
67
+ "lead_8_bass__lead",
68
+ "marimba",
69
+ "melodic_tom",
70
+ "music_box",
71
+ "muted_trumpet",
72
+ "oboe",
73
+ "ocarina",
74
+ "orchestra_hit",
75
+ "orchestral_harp",
76
+ "overdriven_guitar",
77
+ "pad_1_new_age",
78
+ "pad_2_warm",
79
+ "pad_3_polysynth",
80
+ "pad_4_choir",
81
+ "pad_5_bowed",
82
+ "pad_6_metallic",
83
+ "pad_7_halo",
84
+ "pad_8_sweep",
85
+ "pan_flute",
86
+ "percussive_organ",
87
+ "piccolo",
88
+ "pizzicato_strings",
89
+ "recorder",
90
+ "reed_organ",
91
+ "reverse_cymbal",
92
+ "rock_organ",
93
+ "seashore",
94
+ "shakuhachi",
95
+ "shamisen",
96
+ "shanai",
97
+ "sitar",
98
+ "slap_bass_1",
99
+ "slap_bass_2",
100
+ "soprano_sax",
101
+ "steel_drums",
102
+ "string_ensemble_1",
103
+ "string_ensemble_2",
104
+ "synth_bass_1",
105
+ "synth_bass_2",
106
+ "synth_brass_1",
107
+ "synth_brass_2",
108
+ "synth_choir",
109
+ "synth_drum",
110
+ "synth_strings_1",
111
+ "synth_strings_2",
112
+ "taiko_drum",
113
+ "tango_accordion",
114
+ "telephone_ring",
115
+ "tenor_sax",
116
+ "timpani",
117
+ "tinkle_bell",
118
+ "tremolo_strings",
119
+ "trombone",
120
+ "trumpet",
121
+ "tuba",
122
+ "tubular_bells",
123
+ "vibraphone",
124
+ "viola",
125
+ "violin",
126
+ "voice_oohs",
127
+ "whistle",
128
+ "woodblock",
129
+ "xylophone",
130
+ ];
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist"
5
+ },
6
+ "exclude": ["src/**/*.test.ts"]
7
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "allowSyntheticDefaultImports": true,
4
+ "rootDir": "src",
5
+ "lib": ["ES2020", "DOM"],
6
+ "target": "ES2020",
7
+ "module": "ES2020",
8
+ "moduleResolution": "Node",
9
+ "declaration": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true
12
+ },
13
+ "include": ["src/**/*"]
14
+ }