minikeys 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  A tiny javascript library that plays the piano. [Try it out here!](http://liamp.uk/minikeys)
6
6
 
7
- To use MiniKeys in a React app, you can use components from [@minikeys/react](https://npmjs.com/)
7
+ To use MiniKeys in a React app, you can use components from [@minikeys/react](https://www.npmjs.com/package/@minikeys/react)
8
8
 
9
9
  <img width="1269" height="486" alt="Screenshot 2025-07-31 at 21 23 08" src="https://github.com/user-attachments/assets/1ff24de7-9222-42f0-ba74-9a3618ef9cd6" />
10
10
 
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var f=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var b=Object.prototype.hasOwnProperty;var F=(s,e)=>{for(var o in e)f(s,o,{get:e[o],enumerable:!0})},w=(s,e,o,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of D(e))!b.call(s,t)&&t!==o&&f(s,t,{get:()=>e[t],enumerable:!(i=g(e,t))||i.enumerable});return s};var G=s=>w(f({},"__esModule",{value:!0}),s);var K={};F(K,{MiniKeys:()=>y,MiniKeysKeyboard:()=>A,keyboardBlackNotes:()=>h,keyboardWhiteNotes:()=>p,midiToNote:()=>u,noteToMidi:()=>n});module.exports=G(K);var n={"C-1":0,"C#-1":1,"D-1":2,"D#-1":3,"E-1":4,"F-1":5,"F#-1":6,"G-1":7,"G#-1":8,"A-1":9,"A#-1":10,"B-1":11,C0:12,"C#0":13,D0:14,"D#0":15,E0:16,F0:17,"F#0":18,G0:19,"G#0":20,A0:21,"A#0":22,B0:23,C1:24,"C#1":25,D1:26,"D#1":27,E1:28,F1:29,"F#1":30,G1:31,"G#1":32,A1:33,"A#1":34,B1:35,C2:36,"C#2":37,D2:38,"D#2":39,E2:40,F2:41,"F#2":42,G2:43,"G#2":44,A2:45,"A#2":46,B2:47,C3:48,"C#3":49,D3:50,"D#3":51,E3:52,F3:53,"F#3":54,G3:55,"G#3":56,A3:57,"A#3":58,B3:59,C4:60,"C#4":61,D4:62,"D#4":63,E4:64,F4:65,"F#4":66,G4:67,"G#4":68,A4:69,"A#4":70,B4:71,C5:72,"C#5":73,D5:74,"D#5":75,E5:76,F5:77,"F#5":78,G5:79,"G#5":80,A5:81,"A#5":82,B5:83,C6:84,"C#6":85,D6:86,"D#6":87,E6:88,F6:89,"F#6":90,G6:91,"G#6":92,A6:93,"A#6":94,B6:95,C7:96,"C#7":97,D7:98,"D#7":99,E7:100,F7:101,"F#7":102,G7:103,"G#7":104,A7:105,"A#7":106,B7:107,C8:108,"C#8":109,D8:110,"D#8":111,E8:112,F8:113,"F#8":114,G8:115,"G#8":116,A8:117,"A#8":118,B8:119,C9:120,"C#9":121,D9:122,"D#9":123,E9:124,F9:125,"F#9":126,G9:127},u=Object.fromEntries(Object.entries(n).map(([s,e])=>[e,s])),h=[null,"A#0",null,"C#1","D#1",null,"F#1","G#1","A#1",null,"C#2","D#2",null,"F#2","G#2","A#2",null,"C#3","D#3",null,"F#3","G#3","A#3",null,"C#4","D#4",null,"F#4","G#4","A#4",null,"C#5","D#5",null,"F#5","G#5","A#5",null,"C#6","D#6",null,"F#6","G#6","A#6",null,"C#7","D#7",null,"F#7","G#7","A#7",null,null,null],p=["A0","B0","C1","D1","E1","F1","G1","A1","B1","C2","D2","E2","F2","G2","A2","B2","C3","D3","E3","F3","G3","A3","B3","C4","D4","E4","F4","G4","A4","B4","C5","D5","E5","F5","G5","A5","B5","C6","D6","E6","F6","G6","A6","B6","C7","D7","E7","F7","G7","A7","B7","C8"],m=[["Digit1","Digit2","Digit3","Digit4","Digit5","Digit6","Digit7","Digit8","Digit9","Digit0","Minus","Equal"],["KeyQ","KeyW","KeyE","KeyR","KeyT","KeyY","KeyU","KeyI","KeyO","KeyP","BracketLeft","BracketRight"],["KeyA","KeyS","KeyD","KeyF","KeyG","KeyH","KeyJ","KeyK","KeyL","Semicolon","Quote"],["KeyZ","KeyX","KeyC","KeyV","KeyB","KeyN","KeyM","Comma","Period","Slash"]],C=(s,e,o,i)=>{let t;if(i&&i<64&&e.size>0)t=e;else if(s.size>0)t=s;else throw new Error("No notes loaded");let r=Array.from(t.keys()).reduce((l,a)=>Math.abs(a-o)<Math.abs(l-o)?a:l);return{closestNoteMidi:r,closestNote:t.get(r)}};var y=class{audioContext;compressorNode;forteNotes=new Map;pianoNotes=new Map;sustain=!1;playingNotes=new Map;progress=0;constructor(){this.audioContext=new AudioContext,this.compressorNode=this.audioContext.createDynamicsCompressor(),this.compressorNode.threshold.value=-24,this.compressorNode.knee.value=0,this.compressorNode.ratio.value=2,this.compressorNode.attack.value=.001,this.compressorNode.release.value=.5,this.compressorNode.connect(this.audioContext.destination)}setSustain=e=>{this.sustain=e,e?this.playingNotes.forEach(o=>{let i=this.audioContext.currentTime;o.gain.cancelScheduledValues(i),o.gain.setValueAtTime(o.gain.value,i)}):this.playingNotes.forEach(o=>{let i=this.audioContext.currentTime;o.gain.cancelScheduledValues(i),o.gain.setValueAtTime(o.gain.value,i),o.gain.linearRampToValueAtTime(.001,i+.5)})};loadNotes=async(e,o)=>{this.forteNotes.clear(),this.pianoNotes.clear();let i=e.map(async t=>{let l=await(await fetch(t.url)).arrayBuffer(),a=await this.audioContext.decodeAudioData(l);if(t.velocity==="piano"?this.pianoNotes.set(n[t.note],a):this.forteNotes.set(n[t.note],a),o){let d=(this.pianoNotes.size+this.forteNotes.size)/e.length;d>this.progress&&(this.progress=d,o(this.progress))}});return await Promise.all(i)};playNoteFromMidi=(e,o)=>{if(e<0||e>127)throw new Error("Invalid midi note");if(o&&(o<0||o>127))throw new Error("Invalid velocity value");if(!this.audioContext)throw new Error("Audio context not initialized");let t=this.audioContext.createBufferSource(),{closestNoteMidi:r,closestNote:l}=C(this.forteNotes,this.pianoNotes,e,o);if(l){t.buffer=l;let a=this.audioContext.createGain(),d=Math.min(127,o??127);if(a.gain.value=(d/127*.9+.1)*.5,t.connect(a),a.connect(this.compressorNode),t.playbackRate.value=2**((e-r)/12),t.start(),this.playingNotes.set(t,a),!this.sustain){let c=this.audioContext.currentTime;a.gain.cancelScheduledValues(c),a.gain.setValueAtTime(a.gain.value,c),a.gain.exponentialRampToValueAtTime(Number.EPSILON,c+8)}t.onended=()=>{t.disconnect(),this.playingNotes.delete(t)}}else throw new Error("Note not found")};playNoteFromName=(e,o)=>{this.playNoteFromMidi(n[e],o)}};var A=class{mode;keyMap=new Map;offset;constructor(e){this.mode=e,e==="single"?this.offset=23:this.offset=16,this.buildNoteMap()}getNoteMap=()=>this.keyMap;getMidiRange=()=>{let e=Array.from(this.keyMap.values()).reduce((i,t)=>t.midiNote&&t.midiNote<i?t.midiNote:i,127),o=Array.from(this.keyMap.values()).reduce((i,t)=>t.midiNote&&t.midiNote>i?t.midiNote:i,0);return{low:e,high:o}};getNoteRange=()=>{let e=this.getMidiRange();return{low:u[e.low],high:u[e.high]}};shiftLeft=()=>{this.offset=Math.max(this.offset-1,0),this.keyMap=new Map,this.buildNoteMap()};shiftLeftOctave=()=>{this.offset=Math.max(this.offset-7,0),this.keyMap=new Map,this.buildNoteMap()};shiftRight=()=>{this.offset=Math.min(this.offset+1,this.mode==="dual"?30:41),this.keyMap=new Map,this.buildNoteMap()};shiftRightOctave=()=>{this.offset=Math.min(this.offset+7,this.mode==="dual"?30:41),this.keyMap=new Map,this.buildNoteMap()};buildNoteMap=()=>{this.mode==="single"?(N(this.keyMap,1,this.offset),M(this.keyMap,2,this.offset)):(N(this.keyMap,0,this.offset),M(this.keyMap,1,this.offset),N(this.keyMap,2,this.offset+12),M(this.keyMap,3,this.offset+12))}},N=(s,e,o)=>{m[e].forEach((i,t)=>{let r=h[o+t];if(r===null)s.set(i,{midiNote:null,type:"disabled"});else if(r!==void 0)s.set(i,{midiNote:n[r],type:"black"});else throw new Error("Invalid note")})},M=(s,e,o)=>{m[e].forEach((i,t)=>{let r=p[o+t];if(r)s.set(i,{midiNote:n[r],type:"white"});else throw new Error("Invalid note")})};0&&(module.exports={MiniKeys,MiniKeysKeyboard,keyboardBlackNotes,keyboardWhiteNotes,midiToNote,noteToMidi});
1
+ "use strict";var c=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var g=Object.getOwnPropertyNames;var D=Object.prototype.hasOwnProperty;var b=(s,e)=>{for(var t in e)c(s,t,{get:e[t],enumerable:!0})},F=(s,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of g(e))!D.call(s,o)&&o!==t&&c(s,o,{get:()=>e[o],enumerable:!(i=C(e,o))||i.enumerable});return s};var w=s=>F(c({},"__esModule",{value:!0}),s);var G={};b(G,{MiniKeys:()=>m,MiniKeysKeyboard:()=>M,keyboardBlackNotes:()=>h,keyboardWhiteNotes:()=>p,midiToNote:()=>d,noteToMidi:()=>n});module.exports=w(G);var n={"C-1":0,"C#-1":1,"D-1":2,"D#-1":3,"E-1":4,"F-1":5,"F#-1":6,"G-1":7,"G#-1":8,"A-1":9,"A#-1":10,"B-1":11,C0:12,"C#0":13,D0:14,"D#0":15,E0:16,F0:17,"F#0":18,G0:19,"G#0":20,A0:21,"A#0":22,B0:23,C1:24,"C#1":25,D1:26,"D#1":27,E1:28,F1:29,"F#1":30,G1:31,"G#1":32,A1:33,"A#1":34,B1:35,C2:36,"C#2":37,D2:38,"D#2":39,E2:40,F2:41,"F#2":42,G2:43,"G#2":44,A2:45,"A#2":46,B2:47,C3:48,"C#3":49,D3:50,"D#3":51,E3:52,F3:53,"F#3":54,G3:55,"G#3":56,A3:57,"A#3":58,B3:59,C4:60,"C#4":61,D4:62,"D#4":63,E4:64,F4:65,"F#4":66,G4:67,"G#4":68,A4:69,"A#4":70,B4:71,C5:72,"C#5":73,D5:74,"D#5":75,E5:76,F5:77,"F#5":78,G5:79,"G#5":80,A5:81,"A#5":82,B5:83,C6:84,"C#6":85,D6:86,"D#6":87,E6:88,F6:89,"F#6":90,G6:91,"G#6":92,A6:93,"A#6":94,B6:95,C7:96,"C#7":97,D7:98,"D#7":99,E7:100,F7:101,"F#7":102,G7:103,"G#7":104,A7:105,"A#7":106,B7:107,C8:108,"C#8":109,D8:110,"D#8":111,E8:112,F8:113,"F#8":114,G8:115,"G#8":116,A8:117,"A#8":118,B8:119,C9:120,"C#9":121,D9:122,"D#9":123,E9:124,F9:125,"F#9":126,G9:127},d=Object.fromEntries(Object.entries(n).map(([s,e])=>[e,s])),h=[null,"A#0",null,"C#1","D#1",null,"F#1","G#1","A#1",null,"C#2","D#2",null,"F#2","G#2","A#2",null,"C#3","D#3",null,"F#3","G#3","A#3",null,"C#4","D#4",null,"F#4","G#4","A#4",null,"C#5","D#5",null,"F#5","G#5","A#5",null,"C#6","D#6",null,"F#6","G#6","A#6",null,"C#7","D#7",null,"F#7","G#7","A#7",null,null,null],p=["A0","B0","C1","D1","E1","F1","G1","A1","B1","C2","D2","E2","F2","G2","A2","B2","C3","D3","E3","F3","G3","A3","B3","C4","D4","E4","F4","G4","A4","B4","C5","D5","E5","F5","G5","A5","B5","C6","D6","E6","F6","G6","A6","B6","C7","D7","E7","F7","G7","A7","B7","C8"],f=[["Digit1","Digit2","Digit3","Digit4","Digit5","Digit6","Digit7","Digit8","Digit9","Digit0","Minus","Equal"],["KeyQ","KeyW","KeyE","KeyR","KeyT","KeyY","KeyU","KeyI","KeyO","KeyP","BracketLeft","BracketRight"],["KeyA","KeyS","KeyD","KeyF","KeyG","KeyH","KeyJ","KeyK","KeyL","Semicolon","Quote"],["KeyZ","KeyX","KeyC","KeyV","KeyB","KeyN","KeyM","Comma","Period","Slash"]],A=(s,e,t,i)=>{let o;if(i&&i<64&&e.size>0)o=e;else if(s.size>0)o=s;else throw new Error("No notes loaded");let a=Array.from(o.keys()).reduce((l,r)=>Math.abs(r-t)<Math.abs(l-t)?r:l);return{closestNoteMidi:a,closestNote:o.get(a)}};var m=class{audioContext;compressorNode;forteNotes=new Map;pianoNotes=new Map;sustain=!1;playingNotes=new Map;progress=0;constructor(){this.audioContext=new AudioContext,this.compressorNode=this.audioContext.createDynamicsCompressor(),this.compressorNode.threshold.value=-24,this.compressorNode.knee.value=0,this.compressorNode.ratio.value=2,this.compressorNode.attack.value=.001,this.compressorNode.release.value=.5,this.compressorNode.connect(this.audioContext.destination)}setSustain=e=>{this.sustain=e,e?this.playingNotes.forEach(t=>{let i=this.audioContext.currentTime;t.node.gain.cancelScheduledValues(i),t.node.gain.setValueAtTime(t.node.gain.value,i)}):this.playingNotes.forEach(t=>{let i=this.audioContext.currentTime;t.node.gain.cancelScheduledValues(i),t.node.gain.setValueAtTime(t.node.gain.value,i),t.node.gain.linearRampToValueAtTime(.001,i+.5)})};loadNotes=async(e,t)=>{this.forteNotes.clear(),this.pianoNotes.clear();let i=e.map(async o=>{let l=await(await fetch(o.url)).arrayBuffer(),r=await this.audioContext.decodeAudioData(l);if(o.velocity==="piano"?this.pianoNotes.set(n[o.note],r):this.forteNotes.set(n[o.note],r),t){let u=(this.pianoNotes.size+this.forteNotes.size)/e.length;u>this.progress&&(this.progress=u,t(this.progress))}});return await Promise.all(i)};playNoteFromMidi=(e,t)=>{if(e<0||e>127)throw new Error("Invalid midi note");if(t&&(t<0||t>127))throw new Error("Invalid velocity value");if(!this.audioContext)throw new Error("Audio context not initialized");let o=this.audioContext.createBufferSource(),{closestNoteMidi:a,closestNote:l}=A(this.forteNotes,this.pianoNotes,e,t);if(l){o.buffer=l;let r=this.audioContext.createGain(),u=Math.min(127,t??127);r.gain.value=(u/127*.9+.1)*.5,o.connect(r),r.connect(this.compressorNode),o.playbackRate.value=2**((e-a)/12),o.start(),this.playingNotes.set(o,{node:r,note:e}),o.onended=()=>{o.disconnect(),this.playingNotes.delete(o)}}else throw new Error("Note not found")};playNoteFromName=(e,t)=>{this.playNoteFromMidi(n[e],t)};liftNoteFromMidi=e=>{this.playingNotes.forEach(t=>{if(t.note===e&&!this.sustain){let i=this.audioContext.currentTime;t.node.gain.cancelScheduledValues(i),t.node.gain.setValueAtTime(t.node.gain.value,i),t.node.gain.exponentialRampToValueAtTime(Number.EPSILON,i+8)}})};liftNoteFromName=(e,t)=>{this.liftNoteFromMidi(n[e])}};var M=class{mode;keyMap=new Map;offset;constructor(e){this.mode=e,e==="single"?this.offset=23:this.offset=16,this.buildNoteMap()}getNoteMap=()=>this.keyMap;getMidiRange=()=>{let e=Array.from(this.keyMap.values()).reduce((i,o)=>o.midiNote&&o.midiNote<i?o.midiNote:i,127),t=Array.from(this.keyMap.values()).reduce((i,o)=>o.midiNote&&o.midiNote>i?o.midiNote:i,0);return{low:e,high:t}};getNoteRange=()=>{let e=this.getMidiRange();return{low:d[e.low],high:d[e.high]}};shiftLeft=()=>{this.offset=Math.max(this.offset-1,0),this.keyMap=new Map,this.buildNoteMap()};shiftLeftOctave=()=>{this.offset=Math.max(this.offset-7,0),this.keyMap=new Map,this.buildNoteMap()};shiftRight=()=>{this.offset=Math.min(this.offset+1,this.mode==="dual"?30:41),this.keyMap=new Map,this.buildNoteMap()};shiftRightOctave=()=>{this.offset=Math.min(this.offset+7,this.mode==="dual"?30:41),this.keyMap=new Map,this.buildNoteMap()};buildNoteMap=()=>{this.mode==="single"?(y(this.keyMap,1,this.offset),N(this.keyMap,2,this.offset)):(y(this.keyMap,0,this.offset),N(this.keyMap,1,this.offset),y(this.keyMap,2,this.offset+12),N(this.keyMap,3,this.offset+12))}},y=(s,e,t)=>{f[e].forEach((i,o)=>{let a=h[t+o];if(a===null)s.set(i,{midiNote:null,type:"disabled"});else if(a!==void 0)s.set(i,{midiNote:n[a],type:"black"});else throw new Error("Invalid note")})},N=(s,e,t)=>{f[e].forEach((i,o)=>{let a=p[t+o];if(a)s.set(i,{midiNote:n[a],type:"white"});else throw new Error("Invalid note")})};0&&(module.exports={MiniKeys,MiniKeysKeyboard,keyboardBlackNotes,keyboardWhiteNotes,midiToNote,noteToMidi});
package/dist/index.d.cts CHANGED
@@ -160,6 +160,8 @@ declare class MiniKeys {
160
160
  loadNotes: (samples: Sample[], handleSampleLoaded?: (progress: number) => void) => Promise<void[]>;
161
161
  playNoteFromMidi: (midiNote: number, velocity?: number) => void;
162
162
  playNoteFromName: (noteName: NoteName, velocity?: number) => void;
163
+ liftNoteFromMidi: (midiNote: number) => void;
164
+ liftNoteFromName: (noteName: NoteName, velocity?: number) => void;
163
165
  }
164
166
 
165
167
  declare class MiniKeysKeyboard {
package/dist/index.d.ts CHANGED
@@ -160,6 +160,8 @@ declare class MiniKeys {
160
160
  loadNotes: (samples: Sample[], handleSampleLoaded?: (progress: number) => void) => Promise<void[]>;
161
161
  playNoteFromMidi: (midiNote: number, velocity?: number) => void;
162
162
  playNoteFromName: (noteName: NoteName, velocity?: number) => void;
163
+ liftNoteFromMidi: (midiNote: number) => void;
164
+ liftNoteFromName: (noteName: NoteName, velocity?: number) => void;
163
165
  }
164
166
 
165
167
  declare class MiniKeysKeyboard {
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- var n={"C-1":0,"C#-1":1,"D-1":2,"D#-1":3,"E-1":4,"F-1":5,"F#-1":6,"G-1":7,"G#-1":8,"A-1":9,"A#-1":10,"B-1":11,C0:12,"C#0":13,D0:14,"D#0":15,E0:16,F0:17,"F#0":18,G0:19,"G#0":20,A0:21,"A#0":22,B0:23,C1:24,"C#1":25,D1:26,"D#1":27,E1:28,F1:29,"F#1":30,G1:31,"G#1":32,A1:33,"A#1":34,B1:35,C2:36,"C#2":37,D2:38,"D#2":39,E2:40,F2:41,"F#2":42,G2:43,"G#2":44,A2:45,"A#2":46,B2:47,C3:48,"C#3":49,D3:50,"D#3":51,E3:52,F3:53,"F#3":54,G3:55,"G#3":56,A3:57,"A#3":58,B3:59,C4:60,"C#4":61,D4:62,"D#4":63,E4:64,F4:65,"F#4":66,G4:67,"G#4":68,A4:69,"A#4":70,B4:71,C5:72,"C#5":73,D5:74,"D#5":75,E5:76,F5:77,"F#5":78,G5:79,"G#5":80,A5:81,"A#5":82,B5:83,C6:84,"C#6":85,D6:86,"D#6":87,E6:88,F6:89,"F#6":90,G6:91,"G#6":92,A6:93,"A#6":94,B6:95,C7:96,"C#7":97,D7:98,"D#7":99,E7:100,F7:101,"F#7":102,G7:103,"G#7":104,A7:105,"A#7":106,B7:107,C8:108,"C#8":109,D8:110,"D#8":111,E8:112,F8:113,"F#8":114,G8:115,"G#8":116,A8:117,"A#8":118,B8:119,C9:120,"C#9":121,D9:122,"D#9":123,E9:124,F9:125,"F#9":126,G9:127},d=Object.fromEntries(Object.entries(n).map(([r,t])=>[t,r])),p=[null,"A#0",null,"C#1","D#1",null,"F#1","G#1","A#1",null,"C#2","D#2",null,"F#2","G#2","A#2",null,"C#3","D#3",null,"F#3","G#3","A#3",null,"C#4","D#4",null,"F#4","G#4","A#4",null,"C#5","D#5",null,"F#5","G#5","A#5",null,"C#6","D#6",null,"F#6","G#6","A#6",null,"C#7","D#7",null,"F#7","G#7","A#7",null,null,null],c=["A0","B0","C1","D1","E1","F1","G1","A1","B1","C2","D2","E2","F2","G2","A2","B2","C3","D3","E3","F3","G3","A3","B3","C4","D4","E4","F4","G4","A4","B4","C5","D5","E5","F5","G5","A5","B5","C6","D6","E6","F6","G6","A6","B6","C7","D7","E7","F7","G7","A7","B7","C8"],f=[["Digit1","Digit2","Digit3","Digit4","Digit5","Digit6","Digit7","Digit8","Digit9","Digit0","Minus","Equal"],["KeyQ","KeyW","KeyE","KeyR","KeyT","KeyY","KeyU","KeyI","KeyO","KeyP","BracketLeft","BracketRight"],["KeyA","KeyS","KeyD","KeyF","KeyG","KeyH","KeyJ","KeyK","KeyL","Semicolon","Quote"],["KeyZ","KeyX","KeyC","KeyV","KeyB","KeyN","KeyM","Comma","Period","Slash"]],N=(r,t,o,i)=>{let e;if(i&&i<64&&t.size>0)e=t;else if(r.size>0)e=r;else throw new Error("No notes loaded");let a=Array.from(e.keys()).reduce((l,s)=>Math.abs(s-o)<Math.abs(l-o)?s:l);return{closestNoteMidi:a,closestNote:e.get(a)}};var M=class{audioContext;compressorNode;forteNotes=new Map;pianoNotes=new Map;sustain=!1;playingNotes=new Map;progress=0;constructor(){this.audioContext=new AudioContext,this.compressorNode=this.audioContext.createDynamicsCompressor(),this.compressorNode.threshold.value=-24,this.compressorNode.knee.value=0,this.compressorNode.ratio.value=2,this.compressorNode.attack.value=.001,this.compressorNode.release.value=.5,this.compressorNode.connect(this.audioContext.destination)}setSustain=t=>{this.sustain=t,t?this.playingNotes.forEach(o=>{let i=this.audioContext.currentTime;o.gain.cancelScheduledValues(i),o.gain.setValueAtTime(o.gain.value,i)}):this.playingNotes.forEach(o=>{let i=this.audioContext.currentTime;o.gain.cancelScheduledValues(i),o.gain.setValueAtTime(o.gain.value,i),o.gain.linearRampToValueAtTime(.001,i+.5)})};loadNotes=async(t,o)=>{this.forteNotes.clear(),this.pianoNotes.clear();let i=t.map(async e=>{let l=await(await fetch(e.url)).arrayBuffer(),s=await this.audioContext.decodeAudioData(l);if(e.velocity==="piano"?this.pianoNotes.set(n[e.note],s):this.forteNotes.set(n[e.note],s),o){let u=(this.pianoNotes.size+this.forteNotes.size)/t.length;u>this.progress&&(this.progress=u,o(this.progress))}});return await Promise.all(i)};playNoteFromMidi=(t,o)=>{if(t<0||t>127)throw new Error("Invalid midi note");if(o&&(o<0||o>127))throw new Error("Invalid velocity value");if(!this.audioContext)throw new Error("Audio context not initialized");let e=this.audioContext.createBufferSource(),{closestNoteMidi:a,closestNote:l}=N(this.forteNotes,this.pianoNotes,t,o);if(l){e.buffer=l;let s=this.audioContext.createGain(),u=Math.min(127,o??127);if(s.gain.value=(u/127*.9+.1)*.5,e.connect(s),s.connect(this.compressorNode),e.playbackRate.value=2**((t-a)/12),e.start(),this.playingNotes.set(e,s),!this.sustain){let h=this.audioContext.currentTime;s.gain.cancelScheduledValues(h),s.gain.setValueAtTime(s.gain.value,h),s.gain.exponentialRampToValueAtTime(Number.EPSILON,h+8)}e.onended=()=>{e.disconnect(),this.playingNotes.delete(e)}}else throw new Error("Note not found")};playNoteFromName=(t,o)=>{this.playNoteFromMidi(n[t],o)}};var A=class{mode;keyMap=new Map;offset;constructor(t){this.mode=t,t==="single"?this.offset=23:this.offset=16,this.buildNoteMap()}getNoteMap=()=>this.keyMap;getMidiRange=()=>{let t=Array.from(this.keyMap.values()).reduce((i,e)=>e.midiNote&&e.midiNote<i?e.midiNote:i,127),o=Array.from(this.keyMap.values()).reduce((i,e)=>e.midiNote&&e.midiNote>i?e.midiNote:i,0);return{low:t,high:o}};getNoteRange=()=>{let t=this.getMidiRange();return{low:d[t.low],high:d[t.high]}};shiftLeft=()=>{this.offset=Math.max(this.offset-1,0),this.keyMap=new Map,this.buildNoteMap()};shiftLeftOctave=()=>{this.offset=Math.max(this.offset-7,0),this.keyMap=new Map,this.buildNoteMap()};shiftRight=()=>{this.offset=Math.min(this.offset+1,this.mode==="dual"?30:41),this.keyMap=new Map,this.buildNoteMap()};shiftRightOctave=()=>{this.offset=Math.min(this.offset+7,this.mode==="dual"?30:41),this.keyMap=new Map,this.buildNoteMap()};buildNoteMap=()=>{this.mode==="single"?(m(this.keyMap,1,this.offset),y(this.keyMap,2,this.offset)):(m(this.keyMap,0,this.offset),y(this.keyMap,1,this.offset),m(this.keyMap,2,this.offset+12),y(this.keyMap,3,this.offset+12))}},m=(r,t,o)=>{f[t].forEach((i,e)=>{let a=p[o+e];if(a===null)r.set(i,{midiNote:null,type:"disabled"});else if(a!==void 0)r.set(i,{midiNote:n[a],type:"black"});else throw new Error("Invalid note")})},y=(r,t,o)=>{f[t].forEach((i,e)=>{let a=c[o+e];if(a)r.set(i,{midiNote:n[a],type:"white"});else throw new Error("Invalid note")})};export{M as MiniKeys,A as MiniKeysKeyboard,p as keyboardBlackNotes,c as keyboardWhiteNotes,d as midiToNote,n as noteToMidi};
1
+ var r={"C-1":0,"C#-1":1,"D-1":2,"D#-1":3,"E-1":4,"F-1":5,"F#-1":6,"G-1":7,"G#-1":8,"A-1":9,"A#-1":10,"B-1":11,C0:12,"C#0":13,D0:14,"D#0":15,E0:16,F0:17,"F#0":18,G0:19,"G#0":20,A0:21,"A#0":22,B0:23,C1:24,"C#1":25,D1:26,"D#1":27,E1:28,F1:29,"F#1":30,G1:31,"G#1":32,A1:33,"A#1":34,B1:35,C2:36,"C#2":37,D2:38,"D#2":39,E2:40,F2:41,"F#2":42,G2:43,"G#2":44,A2:45,"A#2":46,B2:47,C3:48,"C#3":49,D3:50,"D#3":51,E3:52,F3:53,"F#3":54,G3:55,"G#3":56,A3:57,"A#3":58,B3:59,C4:60,"C#4":61,D4:62,"D#4":63,E4:64,F4:65,"F#4":66,G4:67,"G#4":68,A4:69,"A#4":70,B4:71,C5:72,"C#5":73,D5:74,"D#5":75,E5:76,F5:77,"F#5":78,G5:79,"G#5":80,A5:81,"A#5":82,B5:83,C6:84,"C#6":85,D6:86,"D#6":87,E6:88,F6:89,"F#6":90,G6:91,"G#6":92,A6:93,"A#6":94,B6:95,C7:96,"C#7":97,D7:98,"D#7":99,E7:100,F7:101,"F#7":102,G7:103,"G#7":104,A7:105,"A#7":106,B7:107,C8:108,"C#8":109,D8:110,"D#8":111,E8:112,F8:113,"F#8":114,G8:115,"G#8":116,A8:117,"A#8":118,B8:119,C9:120,"C#9":121,D9:122,"D#9":123,E9:124,F9:125,"F#9":126,G9:127},u=Object.fromEntries(Object.entries(r).map(([a,t])=>[t,a])),h=[null,"A#0",null,"C#1","D#1",null,"F#1","G#1","A#1",null,"C#2","D#2",null,"F#2","G#2","A#2",null,"C#3","D#3",null,"F#3","G#3","A#3",null,"C#4","D#4",null,"F#4","G#4","A#4",null,"C#5","D#5",null,"F#5","G#5","A#5",null,"C#6","D#6",null,"F#6","G#6","A#6",null,"C#7","D#7",null,"F#7","G#7","A#7",null,null,null],p=["A0","B0","C1","D1","E1","F1","G1","A1","B1","C2","D2","E2","F2","G2","A2","B2","C3","D3","E3","F3","G3","A3","B3","C4","D4","E4","F4","G4","A4","B4","C5","D5","E5","F5","G5","A5","B5","C6","D6","E6","F6","G6","A6","B6","C7","D7","E7","F7","G7","A7","B7","C8"],c=[["Digit1","Digit2","Digit3","Digit4","Digit5","Digit6","Digit7","Digit8","Digit9","Digit0","Minus","Equal"],["KeyQ","KeyW","KeyE","KeyR","KeyT","KeyY","KeyU","KeyI","KeyO","KeyP","BracketLeft","BracketRight"],["KeyA","KeyS","KeyD","KeyF","KeyG","KeyH","KeyJ","KeyK","KeyL","Semicolon","Quote"],["KeyZ","KeyX","KeyC","KeyV","KeyB","KeyN","KeyM","Comma","Period","Slash"]],y=(a,t,e,i)=>{let o;if(i&&i<64&&t.size>0)o=t;else if(a.size>0)o=a;else throw new Error("No notes loaded");let s=Array.from(o.keys()).reduce((l,n)=>Math.abs(n-e)<Math.abs(l-e)?n:l);return{closestNoteMidi:s,closestNote:o.get(s)}};var N=class{audioContext;compressorNode;forteNotes=new Map;pianoNotes=new Map;sustain=!1;playingNotes=new Map;progress=0;constructor(){this.audioContext=new AudioContext,this.compressorNode=this.audioContext.createDynamicsCompressor(),this.compressorNode.threshold.value=-24,this.compressorNode.knee.value=0,this.compressorNode.ratio.value=2,this.compressorNode.attack.value=.001,this.compressorNode.release.value=.5,this.compressorNode.connect(this.audioContext.destination)}setSustain=t=>{this.sustain=t,t?this.playingNotes.forEach(e=>{let i=this.audioContext.currentTime;e.node.gain.cancelScheduledValues(i),e.node.gain.setValueAtTime(e.node.gain.value,i)}):this.playingNotes.forEach(e=>{let i=this.audioContext.currentTime;e.node.gain.cancelScheduledValues(i),e.node.gain.setValueAtTime(e.node.gain.value,i),e.node.gain.linearRampToValueAtTime(.001,i+.5)})};loadNotes=async(t,e)=>{this.forteNotes.clear(),this.pianoNotes.clear();let i=t.map(async o=>{let l=await(await fetch(o.url)).arrayBuffer(),n=await this.audioContext.decodeAudioData(l);if(o.velocity==="piano"?this.pianoNotes.set(r[o.note],n):this.forteNotes.set(r[o.note],n),e){let d=(this.pianoNotes.size+this.forteNotes.size)/t.length;d>this.progress&&(this.progress=d,e(this.progress))}});return await Promise.all(i)};playNoteFromMidi=(t,e)=>{if(t<0||t>127)throw new Error("Invalid midi note");if(e&&(e<0||e>127))throw new Error("Invalid velocity value");if(!this.audioContext)throw new Error("Audio context not initialized");let o=this.audioContext.createBufferSource(),{closestNoteMidi:s,closestNote:l}=y(this.forteNotes,this.pianoNotes,t,e);if(l){o.buffer=l;let n=this.audioContext.createGain(),d=Math.min(127,e??127);n.gain.value=(d/127*.9+.1)*.5,o.connect(n),n.connect(this.compressorNode),o.playbackRate.value=2**((t-s)/12),o.start(),this.playingNotes.set(o,{node:n,note:t}),o.onended=()=>{o.disconnect(),this.playingNotes.delete(o)}}else throw new Error("Note not found")};playNoteFromName=(t,e)=>{this.playNoteFromMidi(r[t],e)};liftNoteFromMidi=t=>{this.playingNotes.forEach(e=>{if(e.note===t&&!this.sustain){let i=this.audioContext.currentTime;e.node.gain.cancelScheduledValues(i),e.node.gain.setValueAtTime(e.node.gain.value,i),e.node.gain.exponentialRampToValueAtTime(Number.EPSILON,i+8)}})};liftNoteFromName=(t,e)=>{this.liftNoteFromMidi(r[t])}};var M=class{mode;keyMap=new Map;offset;constructor(t){this.mode=t,t==="single"?this.offset=23:this.offset=16,this.buildNoteMap()}getNoteMap=()=>this.keyMap;getMidiRange=()=>{let t=Array.from(this.keyMap.values()).reduce((i,o)=>o.midiNote&&o.midiNote<i?o.midiNote:i,127),e=Array.from(this.keyMap.values()).reduce((i,o)=>o.midiNote&&o.midiNote>i?o.midiNote:i,0);return{low:t,high:e}};getNoteRange=()=>{let t=this.getMidiRange();return{low:u[t.low],high:u[t.high]}};shiftLeft=()=>{this.offset=Math.max(this.offset-1,0),this.keyMap=new Map,this.buildNoteMap()};shiftLeftOctave=()=>{this.offset=Math.max(this.offset-7,0),this.keyMap=new Map,this.buildNoteMap()};shiftRight=()=>{this.offset=Math.min(this.offset+1,this.mode==="dual"?30:41),this.keyMap=new Map,this.buildNoteMap()};shiftRightOctave=()=>{this.offset=Math.min(this.offset+7,this.mode==="dual"?30:41),this.keyMap=new Map,this.buildNoteMap()};buildNoteMap=()=>{this.mode==="single"?(f(this.keyMap,1,this.offset),m(this.keyMap,2,this.offset)):(f(this.keyMap,0,this.offset),m(this.keyMap,1,this.offset),f(this.keyMap,2,this.offset+12),m(this.keyMap,3,this.offset+12))}},f=(a,t,e)=>{c[t].forEach((i,o)=>{let s=h[e+o];if(s===null)a.set(i,{midiNote:null,type:"disabled"});else if(s!==void 0)a.set(i,{midiNote:r[s],type:"black"});else throw new Error("Invalid note")})},m=(a,t,e)=>{c[t].forEach((i,o)=>{let s=p[e+o];if(s)a.set(i,{midiNote:r[s],type:"white"});else throw new Error("Invalid note")})};export{N as MiniKeys,M as MiniKeysKeyboard,h as keyboardBlackNotes,p as keyboardWhiteNotes,u as midiToNote,r as noteToMidi};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minikeys",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "A javascript library that plays the piano",
5
5
  "keywords": [
6
6
  "minikeys",
@@ -37,7 +37,7 @@
37
37
  "vitest": "^3.0.5"
38
38
  },
39
39
  "scripts": {
40
- "build": "tsup",
40
+ "build": "tsup src/index.ts --out-dir dist",
41
41
  "ci": "npm run build && npm run check-format && npm run lint && npm run test",
42
42
  "lint": "tsc",
43
43
  "test": "vitest run",