zds-pickers 4.0.25 → 4.1.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 +199 -2
- package/dist/index.cjs.js +40 -15
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +3099 -10462
- package/dist/index.es.js.map +1 -1
- package/dist/other/OctavePlayer.js +2 -1
- package/dist/other/SoundFontProvider.js +64 -4
- package/dist/pickers/KeyPicker.js +14 -2
- package/dist/pickers/PianoPicker.js +2 -1
- package/dist/pickers/knobSkin10.js +18 -0
- package/dist/rotaryKnob/knobdefaultskin.js +13 -0
- package/dist/types/other/OctavePlayer.d.ts +3 -2
- package/dist/types/other/SoundFontProvider.d.ts +13 -0
- package/dist/types/pickers/KeyPicker.d.ts +6 -1
- package/dist/types/pickers/PianoPicker.d.ts +2 -1
- package/lib/other/OctavePlayer.tsx +4 -1
- package/lib/other/SoundFontProvider.tsx +85 -6
- package/lib/pickers/KeyPicker.tsx +43 -3
- package/lib/pickers/PianoPicker.tsx +3 -0
- package/lib/pickers/knobSkin10.tsx +18 -0
- package/lib/rotaryKnob/knobdefaultskin.ts +13 -0
- package/lib/temp/acoustic_grand_piano-mp3.js +14 -1
- package/package.json +13 -13
- package/stories/assets/sampleStyles.css +16 -0
- package/stories/picker.stories.tsx +48 -22
- package/vite.config.ts +18 -9
|
@@ -30,7 +30,7 @@ const BaseOctavePlayer = ({ octave, height, width, ...rest }) => {
|
|
|
30
30
|
const lastNote = Note.midi(baseNotes[baseNotes.length - 1] + String(octave)) || 0;
|
|
31
31
|
return (_jsx("div", { children: _jsx(ReactPiano, { keyWidthToHeight: keyWidthToHeight, noteRange: { first: firstNote, last: lastNote }, width: width, ...rest }) }));
|
|
32
32
|
};
|
|
33
|
-
const WithProvider = ({ format, height, hostname, instrumentName, octave = 4, onChange, onClick, onDoubleClick, onKeyMouseEnter, onKeyMouseLeave, soundfont, Tooltip, width, }) => (_jsx(SoundfontProvider, { audioContext,
|
|
33
|
+
const WithProvider = ({ format, height, hostname, instrumentName, octave = 4, onChange, onClick, onDoubleClick, onKeyMouseEnter, onKeyMouseLeave, selectedNotes, soundfont, Tooltip, width, }) => (_jsx(SoundfontProvider, { audioContext,
|
|
34
34
|
format,
|
|
35
35
|
hostname,
|
|
36
36
|
instrumentName,
|
|
@@ -42,6 +42,7 @@ const WithProvider = ({ format, height, hostname, instrumentName, octave = 4, on
|
|
|
42
42
|
onKeyMouseEnter,
|
|
43
43
|
onKeyMouseLeave,
|
|
44
44
|
playNote,
|
|
45
|
+
selectedNotes,
|
|
45
46
|
stopNote,
|
|
46
47
|
Tooltip,
|
|
47
48
|
width })) }));
|
|
@@ -2,7 +2,30 @@
|
|
|
2
2
|
// for more documentation on prop options.
|
|
3
3
|
import { useCallback, useEffect, useState } from 'react';
|
|
4
4
|
import Soundfont from 'soundfont-player';
|
|
5
|
-
|
|
5
|
+
import { Note } from 'tonal';
|
|
6
|
+
// Ensure MIDI global exists before importing soundfont
|
|
7
|
+
if (typeof window !== 'undefined') {
|
|
8
|
+
if (!window.MIDI) {
|
|
9
|
+
window.MIDI = { Soundfont: {} };
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
// Also ensure it's available globally (not just on window)
|
|
13
|
+
if (typeof globalThis !== 'undefined') {
|
|
14
|
+
if (!globalThis.MIDI) {
|
|
15
|
+
globalThis.MIDI = window.MIDI || { Soundfont: {} };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
// Also set it on the global object for Node.js compatibility
|
|
19
|
+
if (typeof global !== 'undefined') {
|
|
20
|
+
if (!global.MIDI) {
|
|
21
|
+
global.MIDI = window.MIDI || { Soundfont: {} };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// Import bundled soundfont to avoid CSP issues in Electron apps
|
|
25
|
+
// NOTE: This adds ~2.2MB to the bundle size. Currently all consumers need piano,
|
|
26
|
+
// but if future web apps don't need audio, consider making this import conditional
|
|
27
|
+
// or implementing lazy loading to reduce bundle size for audio-free use cases.
|
|
28
|
+
import '../temp/acoustic_grand_piano-mp3';
|
|
6
29
|
const isDefined = (item) => item !== undefined && item !== null && !Number.isNaN(item);
|
|
7
30
|
const SoundfontProvider = (props) => {
|
|
8
31
|
const { audioContext, format, hostname, instrumentName, onChange, render, soundfont, } = props;
|
|
@@ -24,9 +47,46 @@ const SoundfontProvider = (props) => {
|
|
|
24
47
|
}
|
|
25
48
|
else if (instrumentName) {
|
|
26
49
|
// the host app has already injected the soundfont into global space
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
50
|
+
// Try to create a player directly from our bundled soundfont data
|
|
51
|
+
const soundfontData = window.MIDI?.Soundfont?.[instrumentName];
|
|
52
|
+
if (soundfontData) {
|
|
53
|
+
// MIDI note number to note name conversion using Tonal
|
|
54
|
+
const midiToNoteName = (midiNumber) => {
|
|
55
|
+
const num = typeof midiNumber === 'string'
|
|
56
|
+
? Number.parseInt(midiNumber, 10)
|
|
57
|
+
: midiNumber;
|
|
58
|
+
return Note.fromMidi(num);
|
|
59
|
+
};
|
|
60
|
+
// Create a simple player that can play notes from our bundled data
|
|
61
|
+
const player = {
|
|
62
|
+
play: (note, velocity) => {
|
|
63
|
+
// Convert MIDI note number to note name if needed
|
|
64
|
+
const noteName = midiToNoteName(note);
|
|
65
|
+
const noteData = soundfontData[noteName];
|
|
66
|
+
if (noteData) {
|
|
67
|
+
// Create audio from the data URL
|
|
68
|
+
const audio = new Audio(noteData);
|
|
69
|
+
audio.volume = (velocity || 127) / 127;
|
|
70
|
+
audio.play();
|
|
71
|
+
// Return a compatible object with stop method
|
|
72
|
+
return {
|
|
73
|
+
stop: () => {
|
|
74
|
+
audio.pause();
|
|
75
|
+
audio.currentTime = 0;
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
},
|
|
81
|
+
stop: () => {
|
|
82
|
+
// Simple stop implementation
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
setInstrument(player);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
setIsLoading(false);
|
|
89
|
+
}
|
|
30
90
|
}
|
|
31
91
|
else {
|
|
32
92
|
// sound must be disabled
|
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback } from 'react';
|
|
2
3
|
import { OctavePlayer } from '../other/OctavePlayer';
|
|
4
|
+
import { noSelection } from './Select';
|
|
3
5
|
const KeyPicker = (props) => {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
const { value, onChange, disabled = false, height = 100, width = 300, octave = 4, ...rest } = props;
|
|
7
|
+
const handleKeyClick = useCallback((note) => {
|
|
8
|
+
if (disabled)
|
|
9
|
+
return;
|
|
10
|
+
onChange(note);
|
|
11
|
+
}, [disabled, onChange]);
|
|
12
|
+
// Only highlight the note if it's a valid selection and within the octave range
|
|
13
|
+
// Octave 4 = C4 (60) to B4 (71)
|
|
14
|
+
const octaveStart = 60 + (octave - 4) * 12;
|
|
15
|
+
const octaveEnd = octaveStart + 11;
|
|
16
|
+
const shouldHighlight = value !== noSelection && value >= octaveStart && value <= octaveEnd;
|
|
17
|
+
return (_jsx(OctavePlayer, { ...rest, selectedNotes: shouldHighlight ? [value] : [], disabled: disabled, height: height, width: width, octave: octave, onClick: handleKeyClick }));
|
|
6
18
|
};
|
|
7
19
|
KeyPicker.propTypes = {};
|
|
8
20
|
export { KeyPicker };
|
|
@@ -11,7 +11,7 @@ const BasePianoPicker = ({ height, width, ...rest }) => {
|
|
|
11
11
|
const keyWidthToHeight = keyWidth / height;
|
|
12
12
|
return (_jsx("div", { children: _jsx(ReactPiano, { keyWidthToHeight: keyWidthToHeight, noteRange: { first: firstNote, last: lastNote }, width: width, ...rest }) }));
|
|
13
13
|
};
|
|
14
|
-
const WithProvider = ({ format, height, hostname, instrumentName, onChange, onClick, onDoubleClick, onKeyMouseEnter, onKeyMouseLeave, soundfont, width, }) => (_jsx(SoundfontProvider, { audioContext,
|
|
14
|
+
const WithProvider = ({ format, height, hostname, instrumentName, onChange, onClick, onDoubleClick, onKeyMouseEnter, onKeyMouseLeave, selectedNotes, soundfont, width, }) => (_jsx(SoundfontProvider, { audioContext,
|
|
15
15
|
format,
|
|
16
16
|
hostname,
|
|
17
17
|
instrumentName,
|
|
@@ -22,6 +22,7 @@ const WithProvider = ({ format, height, hostname, instrumentName, onChange, onCl
|
|
|
22
22
|
onKeyMouseEnter,
|
|
23
23
|
onKeyMouseLeave,
|
|
24
24
|
playNote,
|
|
25
|
+
selectedNotes,
|
|
25
26
|
stopNote,
|
|
26
27
|
width })) }));
|
|
27
28
|
export { WithProvider as PianoPicker };
|
|
@@ -55,8 +55,26 @@ const knobSkin10 = {
|
|
|
55
55
|
</g>
|
|
56
56
|
<g id="knob" transform="translate(28.574244, 28.574244)">
|
|
57
57
|
<circle id="Oval-5" fill="#322E2E" cx="71.4356094" cy="71.4356094" r="71.4356094"/>
|
|
58
|
+
|
|
59
|
+
<!-- Rotating indicator (existing) -->
|
|
58
60
|
<path d="M72.4209282,3.63066376 L88.6786876,16.869789 C81.585496,18.1548295 76.1662428,18.7973498 72.4209282,18.7973498 C68.6756135,18.7973498 63.2563604,18.1548295 56.1631688,16.869789 L72.4209282,3.63066376 Z" id="Rectangle" fill="#E6D7D7" transform="translate(72.420928, 11.214007) scale(1, -1) translate(-72.420928, -11.214007) "/>
|
|
59
61
|
</g>
|
|
62
|
+
|
|
63
|
+
<!-- Fixed position marks (outside the rotating knob group) -->
|
|
64
|
+
<!-- 12:00 position mark (more prominent) -->
|
|
65
|
+
<line x1="100.0098534" y1="15" x2="100.0098534" y2="35" stroke="#CCCCCC" stroke-width="4" opacity="0.9"/>
|
|
66
|
+
|
|
67
|
+
<!-- Arc fill between end position marks -->
|
|
68
|
+
<!-- <path d="M 35 165 A 65 65 0 0 1 165 165" stroke="#666666" stroke-width="8" fill="none" opacity="0.3"/> -->
|
|
69
|
+
|
|
70
|
+
<!-- End position marks (0 and 127) -->
|
|
71
|
+
<!-- 0 position mark (40 degrees from top, clockwise) - at outer radius beyond knob edge -->
|
|
72
|
+
<!-- This should be at the bottom-left where the value indicator points when at 0 -->
|
|
73
|
+
<line x1="36" y1="164" x2="44" y2="156" stroke="#DDDDDD" stroke-width="4"/>
|
|
74
|
+
|
|
75
|
+
<!-- 127 position mark (320 degrees from top, clockwise) - at outer radius beyond knob edge -->
|
|
76
|
+
<!-- This should be at the bottom-right where the value indicator points when at 127 -->
|
|
77
|
+
<line x1="164" y1="164" x2="156" y2="156" stroke="#DDDDDD" stroke-width="4"/>
|
|
60
78
|
<g id="label">
|
|
61
79
|
<g id="labeltext" fill-opacity="1" fill="#E6D7D7">
|
|
62
80
|
<text id="534f4e02-5541-3607-0966-af0b025d80e0" font-family="Helvetica" font-size="44" font-weight="normal" fill="#E6D7D7" class="text-7" filter="url(#12f618f7-e258-b597-3ed6-242484ddad8f)">
|
|
@@ -46,6 +46,19 @@ export default {
|
|
|
46
46
|
</g>
|
|
47
47
|
<g id="knob" transform="translate(27.431373, 27.431373)">
|
|
48
48
|
<circle id="Oval-5" fill="#322E2E" cx="71" cy="71" r="71"></circle>
|
|
49
|
+
|
|
50
|
+
<!-- Fixed position marks -->
|
|
51
|
+
<!-- 12:00 position mark (less prominent) -->
|
|
52
|
+
<line x1="71" y1="15" x2="71" y2="25" stroke="#666666" stroke-width="1" opacity="0.6"/>
|
|
53
|
+
|
|
54
|
+
<!-- End position marks (0 and 127) -->
|
|
55
|
+
<!-- 0 position mark (40 degrees from top, clockwise) -->
|
|
56
|
+
<line x1="95.5" y1="45.5" x2="88.5" y2="52.5" stroke="#CCCCCC" stroke-width="2"/>
|
|
57
|
+
|
|
58
|
+
<!-- 127 position mark (320 degrees from top, clockwise) -->
|
|
59
|
+
<line x1="46.5" y1="45.5" x2="53.5" y2="52.5" stroke="#CCCCCC" stroke-width="2"/>
|
|
60
|
+
|
|
61
|
+
<!-- Rotating indicator (existing) -->
|
|
49
62
|
<path d="M70.0686275,4.17063061 L86.5686275,65.2689929 C81.3642501,66.811221 75.8642501,67.5735686 70.0686275,67.5560356 C64.2730048,67.5385027 58.7730048,66.7761551 53.5686275,65.2689929 L70.0686275,4.17063061 Z" id="Rectangle-Copy" fill="#E6D7D7" transform="translate(70.068627, 35.863481) scale(1, -1) translate(-70.068627, -35.863481) "></path>
|
|
50
63
|
</g>
|
|
51
64
|
</g>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import type { PianoProps } from 'lib/pickers/PianoPicker';
|
|
1
2
|
import type Soundfont from 'soundfont-player';
|
|
2
3
|
import type { TooltipProps } from './DefaultTooltip';
|
|
3
|
-
import type { PianoProps } from 'lib/pickers/PianoPicker';
|
|
4
4
|
type PianoMidiNote = {
|
|
5
5
|
accidental: boolean;
|
|
6
6
|
left: number;
|
|
@@ -24,10 +24,11 @@ type WithProviderProps = PianoProps & {
|
|
|
24
24
|
onDoubleClick?: (note: number) => void;
|
|
25
25
|
onKeyMouseEnter?: (note: PianoMidiNote) => void;
|
|
26
26
|
onKeyMouseLeave?: (note: number) => void;
|
|
27
|
+
selectedNotes?: number[];
|
|
27
28
|
soundfont?: 'MusyngKite' | 'FluidR3_GM';
|
|
28
29
|
Tooltip?: TooltipProps;
|
|
29
30
|
width: number;
|
|
30
31
|
};
|
|
31
|
-
declare const WithProvider: ({ format, height, hostname, instrumentName, octave, onChange, onClick, onDoubleClick, onKeyMouseEnter, onKeyMouseLeave, soundfont, Tooltip, width, }: WithProviderProps) => import("react/jsx-runtime").JSX.Element;
|
|
32
|
+
declare const WithProvider: ({ format, height, hostname, instrumentName, octave, onChange, onClick, onDoubleClick, onKeyMouseEnter, onKeyMouseLeave, selectedNotes, soundfont, Tooltip, width, }: WithProviderProps) => import("react/jsx-runtime").JSX.Element;
|
|
32
33
|
export { WithProvider as OctavePlayer };
|
|
33
34
|
export type { OctavePlayerProps, PianoMidiNote, WithProviderProps };
|
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import Soundfont from 'soundfont-player';
|
|
2
|
+
interface MIDIGlobal {
|
|
3
|
+
Soundfont: Record<string, Record<string, string>>;
|
|
4
|
+
}
|
|
5
|
+
declare global {
|
|
6
|
+
interface Window {
|
|
7
|
+
MIDI?: MIDIGlobal;
|
|
8
|
+
}
|
|
9
|
+
var MIDI: MIDIGlobal | undefined;
|
|
10
|
+
namespace globalThis {
|
|
11
|
+
var MIDI: MIDIGlobal | undefined;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
import '../temp/acoustic_grand_piano-mp3';
|
|
2
15
|
type SoundfontProviderProps = {
|
|
3
16
|
audioContext: AudioContext;
|
|
4
17
|
format?: 'mp3' | 'ogg';
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { type OctavePlayerProps } from '../other/OctavePlayer';
|
|
2
|
-
type KeyPickerProps = OctavePlayerProps & {
|
|
2
|
+
type KeyPickerProps = Omit<OctavePlayerProps, 'selectedNotes' | 'onClick'> & {
|
|
3
3
|
value: number;
|
|
4
4
|
onChange: (value: number) => void;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
height?: number;
|
|
7
|
+
width?: number;
|
|
8
|
+
octave?: number;
|
|
5
9
|
};
|
|
6
10
|
declare const KeyPicker: {
|
|
7
11
|
(props: KeyPickerProps): import("react/jsx-runtime").JSX.Element;
|
|
8
12
|
propTypes: {};
|
|
9
13
|
};
|
|
10
14
|
export { KeyPicker };
|
|
15
|
+
export type { KeyPickerProps };
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { WithProviderProps } from '../other/OctavePlayer';
|
|
2
2
|
type PianoProps = {
|
|
3
3
|
activeNotes?: number[];
|
|
4
|
+
selectedNotes?: number[];
|
|
4
5
|
disabled: boolean;
|
|
5
6
|
height: number;
|
|
6
7
|
width: number;
|
|
7
8
|
};
|
|
8
|
-
declare const WithProvider: ({ format, height, hostname, instrumentName, onChange, onClick, onDoubleClick, onKeyMouseEnter, onKeyMouseLeave, soundfont, width, }: WithProviderProps) => import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
declare const WithProvider: ({ format, height, hostname, instrumentName, onChange, onClick, onDoubleClick, onKeyMouseEnter, onKeyMouseLeave, selectedNotes, soundfont, width, }: WithProviderProps) => import("react/jsx-runtime").JSX.Element;
|
|
9
10
|
export { WithProvider as PianoPicker };
|
|
10
11
|
export type { PianoProps };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import type { PianoProps } from 'lib/pickers/PianoPicker'
|
|
1
2
|
import type Soundfont from 'soundfont-player'
|
|
2
3
|
import { /* Midi, */ Note } from 'tonal'
|
|
3
4
|
import { Piano as ReactPiano } from 'zds-react-piano'
|
|
4
5
|
import type { TooltipProps } from './DefaultTooltip'
|
|
5
6
|
import { SoundfontProvider } from './SoundFontProvider'
|
|
6
|
-
import type { PianoProps } from 'lib/pickers/PianoPicker'
|
|
7
7
|
|
|
8
8
|
type PianoMidiNote = {
|
|
9
9
|
accidental: boolean
|
|
@@ -82,6 +82,7 @@ type WithProviderProps = PianoProps & {
|
|
|
82
82
|
onDoubleClick?: (note: number) => void
|
|
83
83
|
onKeyMouseEnter?: (note: PianoMidiNote) => void
|
|
84
84
|
onKeyMouseLeave?: (note: number) => void
|
|
85
|
+
selectedNotes?: number[]
|
|
85
86
|
soundfont?: 'MusyngKite' | 'FluidR3_GM'
|
|
86
87
|
Tooltip?: TooltipProps
|
|
87
88
|
width: number
|
|
@@ -98,6 +99,7 @@ const WithProvider = ({
|
|
|
98
99
|
onDoubleClick,
|
|
99
100
|
onKeyMouseEnter,
|
|
100
101
|
onKeyMouseLeave,
|
|
102
|
+
selectedNotes,
|
|
101
103
|
soundfont,
|
|
102
104
|
Tooltip,
|
|
103
105
|
width,
|
|
@@ -122,6 +124,7 @@ const WithProvider = ({
|
|
|
122
124
|
onKeyMouseEnter,
|
|
123
125
|
onKeyMouseLeave,
|
|
124
126
|
playNote,
|
|
127
|
+
selectedNotes,
|
|
125
128
|
stopNote,
|
|
126
129
|
Tooltip,
|
|
127
130
|
width,
|
|
@@ -2,7 +2,49 @@
|
|
|
2
2
|
// for more documentation on prop options.
|
|
3
3
|
import { useCallback, useEffect, useState } from 'react'
|
|
4
4
|
import Soundfont from 'soundfont-player'
|
|
5
|
-
|
|
5
|
+
import { Note } from 'tonal'
|
|
6
|
+
|
|
7
|
+
// Type for the MIDI global object
|
|
8
|
+
interface MIDIGlobal {
|
|
9
|
+
Soundfont: Record<string, Record<string, string>>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Extend global interfaces to include MIDI
|
|
13
|
+
declare global {
|
|
14
|
+
interface Window {
|
|
15
|
+
MIDI?: MIDIGlobal
|
|
16
|
+
}
|
|
17
|
+
var MIDI: MIDIGlobal | undefined
|
|
18
|
+
namespace globalThis {
|
|
19
|
+
var MIDI: MIDIGlobal | undefined
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// Ensure MIDI global exists before importing soundfont
|
|
23
|
+
if (typeof window !== 'undefined') {
|
|
24
|
+
if (!window.MIDI) {
|
|
25
|
+
window.MIDI = { Soundfont: {} }
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Also ensure it's available globally (not just on window)
|
|
30
|
+
if (typeof globalThis !== 'undefined') {
|
|
31
|
+
if (!globalThis.MIDI) {
|
|
32
|
+
globalThis.MIDI = window.MIDI || { Soundfont: {} }
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Also set it on the global object for Node.js compatibility
|
|
37
|
+
if (typeof global !== 'undefined') {
|
|
38
|
+
if (!global.MIDI) {
|
|
39
|
+
global.MIDI = window.MIDI || { Soundfont: {} }
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Import bundled soundfont to avoid CSP issues in Electron apps
|
|
44
|
+
// NOTE: This adds ~2.2MB to the bundle size. Currently all consumers need piano,
|
|
45
|
+
// but if future web apps don't need audio, consider making this import conditional
|
|
46
|
+
// or implementing lazy loading to reduce bundle size for audio-free use cases.
|
|
47
|
+
import '../temp/acoustic_grand_piano-mp3'
|
|
6
48
|
|
|
7
49
|
const isDefined = <T,>(item: T) =>
|
|
8
50
|
item !== undefined && item !== null && !Number.isNaN(item)
|
|
@@ -56,11 +98,48 @@ const SoundfontProvider = (props: SoundfontProviderProps) => {
|
|
|
56
98
|
})
|
|
57
99
|
} else if (instrumentName) {
|
|
58
100
|
// the host app has already injected the soundfont into global space
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
101
|
+
// Try to create a player directly from our bundled soundfont data
|
|
102
|
+
const soundfontData = window.MIDI?.Soundfont?.[instrumentName]
|
|
103
|
+
if (soundfontData) {
|
|
104
|
+
// MIDI note number to note name conversion using Tonal
|
|
105
|
+
const midiToNoteName = (midiNumber: number | string): string => {
|
|
106
|
+
const num =
|
|
107
|
+
typeof midiNumber === 'string'
|
|
108
|
+
? Number.parseInt(midiNumber, 10)
|
|
109
|
+
: midiNumber
|
|
110
|
+
return Note.fromMidi(num)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Create a simple player that can play notes from our bundled data
|
|
114
|
+
const player = {
|
|
115
|
+
play: (note: string | number, velocity?: number) => {
|
|
116
|
+
// Convert MIDI note number to note name if needed
|
|
117
|
+
const noteName = midiToNoteName(note)
|
|
118
|
+
const noteData = soundfontData[noteName]
|
|
119
|
+
if (noteData) {
|
|
120
|
+
// Create audio from the data URL
|
|
121
|
+
const audio = new Audio(noteData)
|
|
122
|
+
audio.volume = (velocity || 127) / 127
|
|
123
|
+
audio.play()
|
|
124
|
+
|
|
125
|
+
// Return a compatible object with stop method
|
|
126
|
+
return {
|
|
127
|
+
stop: () => {
|
|
128
|
+
audio.pause()
|
|
129
|
+
audio.currentTime = 0
|
|
130
|
+
},
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return null
|
|
134
|
+
},
|
|
135
|
+
stop: () => {
|
|
136
|
+
// Simple stop implementation
|
|
137
|
+
},
|
|
138
|
+
}
|
|
139
|
+
setInstrument(player as Soundfont.Player)
|
|
140
|
+
} else {
|
|
141
|
+
setIsLoading(false)
|
|
142
|
+
}
|
|
64
143
|
} else {
|
|
65
144
|
// sound must be disabled
|
|
66
145
|
setIsLoading(false)
|
|
@@ -1,16 +1,56 @@
|
|
|
1
|
+
import { useCallback } from 'react'
|
|
1
2
|
import { OctavePlayer, type OctavePlayerProps } from '../other/OctavePlayer'
|
|
3
|
+
import { noSelection } from './Select'
|
|
2
4
|
|
|
3
|
-
type KeyPickerProps = OctavePlayerProps & {
|
|
5
|
+
type KeyPickerProps = Omit<OctavePlayerProps, 'selectedNotes' | 'onClick'> & {
|
|
4
6
|
value: number
|
|
5
7
|
onChange: (value: number) => void
|
|
8
|
+
disabled?: boolean
|
|
9
|
+
height?: number
|
|
10
|
+
width?: number
|
|
11
|
+
octave?: number
|
|
6
12
|
}
|
|
7
13
|
|
|
8
14
|
const KeyPicker = (props: KeyPickerProps) => {
|
|
9
|
-
|
|
15
|
+
const {
|
|
16
|
+
value,
|
|
17
|
+
onChange,
|
|
18
|
+
disabled = false,
|
|
19
|
+
height = 100,
|
|
20
|
+
width = 300,
|
|
21
|
+
octave = 4,
|
|
22
|
+
...rest
|
|
23
|
+
} = props
|
|
10
24
|
|
|
11
|
-
|
|
25
|
+
const handleKeyClick = useCallback(
|
|
26
|
+
(note: number) => {
|
|
27
|
+
if (disabled) return
|
|
28
|
+
onChange(note)
|
|
29
|
+
},
|
|
30
|
+
[disabled, onChange],
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
// Only highlight the note if it's a valid selection and within the octave range
|
|
34
|
+
// Octave 4 = C4 (60) to B4 (71)
|
|
35
|
+
const octaveStart = 60 + (octave - 4) * 12
|
|
36
|
+
const octaveEnd = octaveStart + 11
|
|
37
|
+
const shouldHighlight =
|
|
38
|
+
value !== noSelection && value >= octaveStart && value <= octaveEnd
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<OctavePlayer
|
|
42
|
+
{...rest}
|
|
43
|
+
selectedNotes={shouldHighlight ? [value] : []}
|
|
44
|
+
disabled={disabled}
|
|
45
|
+
height={height}
|
|
46
|
+
width={width}
|
|
47
|
+
octave={octave}
|
|
48
|
+
onClick={handleKeyClick}
|
|
49
|
+
/>
|
|
50
|
+
)
|
|
12
51
|
}
|
|
13
52
|
|
|
14
53
|
KeyPicker.propTypes = {}
|
|
15
54
|
|
|
16
55
|
export { KeyPicker }
|
|
56
|
+
export type { KeyPickerProps }
|
|
@@ -12,6 +12,7 @@ const audioContext = new window.AudioContext()
|
|
|
12
12
|
|
|
13
13
|
type PianoProps = {
|
|
14
14
|
activeNotes?: number[]
|
|
15
|
+
selectedNotes?: number[]
|
|
15
16
|
disabled: boolean
|
|
16
17
|
height: number
|
|
17
18
|
width: number
|
|
@@ -43,6 +44,7 @@ const WithProvider = ({
|
|
|
43
44
|
onDoubleClick,
|
|
44
45
|
onKeyMouseEnter,
|
|
45
46
|
onKeyMouseLeave,
|
|
47
|
+
selectedNotes,
|
|
46
48
|
soundfont,
|
|
47
49
|
width,
|
|
48
50
|
}: WithProviderProps) => (
|
|
@@ -65,6 +67,7 @@ const WithProvider = ({
|
|
|
65
67
|
onKeyMouseEnter,
|
|
66
68
|
onKeyMouseLeave,
|
|
67
69
|
playNote,
|
|
70
|
+
selectedNotes,
|
|
68
71
|
stopNote,
|
|
69
72
|
width,
|
|
70
73
|
}}
|
|
@@ -55,8 +55,26 @@ const knobSkin10 = {
|
|
|
55
55
|
</g>
|
|
56
56
|
<g id="knob" transform="translate(28.574244, 28.574244)">
|
|
57
57
|
<circle id="Oval-5" fill="#322E2E" cx="71.4356094" cy="71.4356094" r="71.4356094"/>
|
|
58
|
+
|
|
59
|
+
<!-- Rotating indicator (existing) -->
|
|
58
60
|
<path d="M72.4209282,3.63066376 L88.6786876,16.869789 C81.585496,18.1548295 76.1662428,18.7973498 72.4209282,18.7973498 C68.6756135,18.7973498 63.2563604,18.1548295 56.1631688,16.869789 L72.4209282,3.63066376 Z" id="Rectangle" fill="#E6D7D7" transform="translate(72.420928, 11.214007) scale(1, -1) translate(-72.420928, -11.214007) "/>
|
|
59
61
|
</g>
|
|
62
|
+
|
|
63
|
+
<!-- Fixed position marks (outside the rotating knob group) -->
|
|
64
|
+
<!-- 12:00 position mark (more prominent) -->
|
|
65
|
+
<line x1="100.0098534" y1="15" x2="100.0098534" y2="35" stroke="#CCCCCC" stroke-width="4" opacity="0.9"/>
|
|
66
|
+
|
|
67
|
+
<!-- Arc fill between end position marks -->
|
|
68
|
+
<!-- <path d="M 35 165 A 65 65 0 0 1 165 165" stroke="#666666" stroke-width="8" fill="none" opacity="0.3"/> -->
|
|
69
|
+
|
|
70
|
+
<!-- End position marks (0 and 127) -->
|
|
71
|
+
<!-- 0 position mark (40 degrees from top, clockwise) - at outer radius beyond knob edge -->
|
|
72
|
+
<!-- This should be at the bottom-left where the value indicator points when at 0 -->
|
|
73
|
+
<line x1="36" y1="164" x2="44" y2="156" stroke="#DDDDDD" stroke-width="4"/>
|
|
74
|
+
|
|
75
|
+
<!-- 127 position mark (320 degrees from top, clockwise) - at outer radius beyond knob edge -->
|
|
76
|
+
<!-- This should be at the bottom-right where the value indicator points when at 127 -->
|
|
77
|
+
<line x1="164" y1="164" x2="156" y2="156" stroke="#DDDDDD" stroke-width="4"/>
|
|
60
78
|
<g id="label">
|
|
61
79
|
<g id="labeltext" fill-opacity="1" fill="#E6D7D7">
|
|
62
80
|
<text id="534f4e02-5541-3607-0966-af0b025d80e0" font-family="Helvetica" font-size="44" font-weight="normal" fill="#E6D7D7" class="text-7" filter="url(#12f618f7-e258-b597-3ed6-242484ddad8f)">
|
|
@@ -46,6 +46,19 @@ export default {
|
|
|
46
46
|
</g>
|
|
47
47
|
<g id="knob" transform="translate(27.431373, 27.431373)">
|
|
48
48
|
<circle id="Oval-5" fill="#322E2E" cx="71" cy="71" r="71"></circle>
|
|
49
|
+
|
|
50
|
+
<!-- Fixed position marks -->
|
|
51
|
+
<!-- 12:00 position mark (less prominent) -->
|
|
52
|
+
<line x1="71" y1="15" x2="71" y2="25" stroke="#666666" stroke-width="1" opacity="0.6"/>
|
|
53
|
+
|
|
54
|
+
<!-- End position marks (0 and 127) -->
|
|
55
|
+
<!-- 0 position mark (40 degrees from top, clockwise) -->
|
|
56
|
+
<line x1="95.5" y1="45.5" x2="88.5" y2="52.5" stroke="#CCCCCC" stroke-width="2"/>
|
|
57
|
+
|
|
58
|
+
<!-- 127 position mark (320 degrees from top, clockwise) -->
|
|
59
|
+
<line x1="46.5" y1="45.5" x2="53.5" y2="52.5" stroke="#CCCCCC" stroke-width="2"/>
|
|
60
|
+
|
|
61
|
+
<!-- Rotating indicator (existing) -->
|
|
49
62
|
<path d="M70.0686275,4.17063061 L86.5686275,65.2689929 C81.3642501,66.811221 75.8642501,67.5735686 70.0686275,67.5560356 C64.2730048,67.5385027 58.7730048,66.7761551 53.5686275,65.2689929 L70.0686275,4.17063061 Z" id="Rectangle-Copy" fill="#E6D7D7" transform="translate(70.068627, 35.863481) scale(1, -1) translate(-70.068627, -35.863481) "></path>
|
|
50
63
|
</g>
|
|
51
64
|
</g>
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
|
|
2
|
-
|
|
2
|
+
// Use global MIDI object instead of creating local variable
|
|
3
|
+
if (typeof window !== 'undefined' && window.MIDI) {
|
|
4
|
+
var MIDI = window.MIDI
|
|
5
|
+
} else if (typeof globalThis !== 'undefined' && globalThis.MIDI) {
|
|
6
|
+
var MIDI = globalThis.MIDI
|
|
7
|
+
} else if (typeof global !== 'undefined' && global.MIDI) {
|
|
8
|
+
var MIDI = global.MIDI
|
|
9
|
+
} else {
|
|
10
|
+
// Create global MIDI object if none exists
|
|
11
|
+
var MIDI = { Soundfont: {} }
|
|
12
|
+
if (typeof window !== 'undefined') window.MIDI = MIDI
|
|
13
|
+
if (typeof globalThis !== 'undefined') globalThis.MIDI = MIDI
|
|
14
|
+
if (typeof global !== 'undefined') global.MIDI = MIDI
|
|
15
|
+
}
|
|
3
16
|
if (typeof(MIDI.Soundfont) === 'undefined') MIDI.Soundfont = {};
|
|
4
17
|
MIDI.Soundfont.acoustic_grand_piano = {
|
|
5
18
|
"A0": "data:audio/mp3;base64,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",
|