Musical keyboard component written in React.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 

384 lignes
8.9 KiB

  1. import * as React from 'react'
  2. import StyledAccidentalKey from '../StyledAccidentalKey/StyledAccidentalKey'
  3. import StyledNaturalKey from '../StyledNaturalKey/StyledNaturalKey'
  4. import Keyboard, { Props } from './Keyboard'
  5. const Wrapper: React.FC = (props) => (
  6. <div
  7. {...props}
  8. style={{
  9. // @ts-ignore
  10. '--color-channel-0': '#f55',
  11. '--color-channel-1': '#ff0',
  12. '--color-channel-2': '#0a0',
  13. '--color-channel-3': '#05a',
  14. '--color-channel-4': '#a0f',
  15. '--color-channel-5': '#a00',
  16. '--color-channel-6': '#a50',
  17. '--color-channel-7': '#fa0',
  18. '--color-channel-8': '#0f0',
  19. '--color-channel-9': '#0aa',
  20. '--color-channel-10': '#0ff',
  21. '--color-channel-11': '#f0a',
  22. '--color-channel-12': '#aa0',
  23. '--color-channel-13': '#550',
  24. '--color-channel-14': '#50a',
  25. '--color-channel-15': '#f5f',
  26. }}
  27. />
  28. )
  29. export default {
  30. title: 'Keyboard',
  31. }
  32. // By passing optional props to this story, you can control the props of the component when
  33. // you consume the story in a test.
  34. export const Default = (props?: Partial<Props>) => (
  35. <Wrapper>
  36. <Keyboard {...props} startKey={21} endKey={108} />
  37. </Wrapper>
  38. )
  39. export const WithActiveKeys = (props?: Partial<Props>) => (
  40. <Wrapper>
  41. <Keyboard
  42. {...props}
  43. startKey={21}
  44. endKey={108}
  45. keysOn={[
  46. {
  47. key: 60,
  48. velocity: 1,
  49. },
  50. {
  51. key: 63,
  52. velocity: 1,
  53. },
  54. {
  55. key: 67,
  56. velocity: 1,
  57. },
  58. ]}
  59. />
  60. </Wrapper>
  61. )
  62. export const WithDifferentKeyRange = (props?: Partial<Props>) => (
  63. <Wrapper>
  64. <Keyboard {...props} height={300} startKey={48} endKey={71} />
  65. </Wrapper>
  66. )
  67. export const Styled = (props?: Partial<Props>) => (
  68. <div
  69. style={{
  70. // @ts-ignore
  71. '--color-accidental-key': '#35313b',
  72. '--color-natural-key': '#e3e3e5',
  73. }}
  74. >
  75. <Wrapper>
  76. <Keyboard
  77. {...props}
  78. startKey={21}
  79. endKey={108}
  80. keysOn={[
  81. {
  82. key: 60,
  83. velocity: 1,
  84. },
  85. {
  86. key: 63,
  87. velocity: 1,
  88. },
  89. {
  90. key: 67,
  91. velocity: 1,
  92. },
  93. ]}
  94. keyComponents={{
  95. natural: StyledNaturalKey,
  96. accidental: StyledAccidentalKey,
  97. }}
  98. />
  99. </Wrapper>
  100. </div>
  101. )
  102. export const AnotherStyled = (props?: Partial<Props>) => (
  103. <div
  104. style={{
  105. // @ts-ignore
  106. '--size-scale-factor': 2,
  107. '--color-accidental-key': '#35313b',
  108. '--color-natural-key': '#e3e3e5',
  109. }}
  110. >
  111. <Wrapper>
  112. <Keyboard
  113. {...props}
  114. startKey={21}
  115. endKey={108}
  116. keysOn={[
  117. {
  118. key: 60,
  119. velocity: 1,
  120. },
  121. {
  122. key: 63,
  123. velocity: 1,
  124. },
  125. {
  126. key: 67,
  127. velocity: 1,
  128. },
  129. ]}
  130. keyComponents={{
  131. natural: StyledNaturalKey,
  132. accidental: StyledAccidentalKey,
  133. }}
  134. />
  135. </Wrapper>
  136. </div>
  137. )
  138. export const DarkStyled = (props?: Partial<Props>) => (
  139. <div
  140. style={{
  141. // @ts-ignore
  142. '--size-scale-factor': 2,
  143. '--color-accidental-key': '#666666',
  144. '--color-natural-key': '#35313b',
  145. '--color-active-key': 'red',
  146. }}
  147. >
  148. <Wrapper>
  149. <Keyboard
  150. {...props}
  151. startKey={21}
  152. endKey={108}
  153. keysOn={[
  154. {
  155. key: 60,
  156. velocity: 1,
  157. },
  158. {
  159. key: 63,
  160. velocity: 1,
  161. },
  162. {
  163. key: 67,
  164. velocity: 1,
  165. },
  166. ]}
  167. keyComponents={{
  168. natural: StyledNaturalKey,
  169. accidental: StyledAccidentalKey,
  170. }}
  171. />
  172. </Wrapper>
  173. </div>
  174. )
  175. const HasMapComponent = (props: any) => {
  176. const [keyChannels, setKeyChannels] = React.useState<{ key: number; velocity: number; channel: number }[]>([])
  177. const midiAccess = React.useRef<any>(undefined)
  178. const handleKeyOn = (newKeys: { key: number; velocity: number; channel: number; id: number }[]) => {
  179. setKeyChannels((oldKeys) => {
  180. const oldKeysKeys = oldKeys.map((k) => k.key)
  181. const newKeysKeys = newKeys.map((k) => k.key)
  182. const keysOff = oldKeys.filter((ok) => !newKeysKeys.includes(ok.key))
  183. const keysOn = newKeys.filter((nk) => !oldKeysKeys.includes(nk.key))
  184. const channel = 0
  185. keysOn.forEach((k) => {
  186. midiAccess.current?.send([0b10010000 + channel, k.key, Math.floor(k.velocity * 127)])
  187. })
  188. keysOff.forEach((k) => {
  189. midiAccess.current?.send([0b10000000 + channel, k.key, Math.floor(k.velocity * 127)])
  190. })
  191. return newKeys
  192. })
  193. }
  194. React.useEffect(() => {
  195. const { navigator: maybeNavigator } = window
  196. const navigator = maybeNavigator as Navigator & {
  197. requestMIDIAccess: () => Promise<{ outputs: Map<string, unknown> }>
  198. }
  199. if ('requestMIDIAccess' in navigator) {
  200. navigator.requestMIDIAccess().then((m) => {
  201. midiAccess.current = Array.from(m.outputs.values())[0]
  202. })
  203. }
  204. }, [])
  205. return (
  206. <Wrapper>
  207. <Keyboard
  208. {...props}
  209. startKey={21}
  210. endKey={108}
  211. keysOn={keyChannels}
  212. onChange={handleKeyOn}
  213. keyboardMapping={{
  214. KeyQ: 60,
  215. Digit2: 61,
  216. KeyW: 62,
  217. Digit3: 63,
  218. KeyE: 64,
  219. KeyR: 65,
  220. Digit5: 66,
  221. KeyT: 67,
  222. Digit6: 68,
  223. KeyY: 69,
  224. Digit7: 70,
  225. KeyU: 71,
  226. KeyI: 72,
  227. Digit9: 73,
  228. KeyO: 74,
  229. Digit0: 75,
  230. KeyP: 76,
  231. BracketLeft: 77,
  232. Equal: 78,
  233. BracketRight: 79,
  234. KeyZ: 48,
  235. KeyS: 49,
  236. KeyX: 50,
  237. KeyD: 51,
  238. KeyC: 52,
  239. KeyV: 53,
  240. KeyG: 54,
  241. KeyB: 55,
  242. KeyH: 56,
  243. KeyN: 57,
  244. KeyJ: 58,
  245. KeyM: 59,
  246. Comma: 60,
  247. KeyL: 61,
  248. Period: 62,
  249. Semicolon: 63,
  250. Slash: 64,
  251. }}
  252. />
  253. </Wrapper>
  254. )
  255. }
  256. export const HasMap = () => <HasMapComponent />
  257. export const Mirrored = () => <HasMapComponent mirrored />
  258. export const Checkbox = (props?: Partial<Props>) => (
  259. <Wrapper>
  260. <Keyboard {...props} startKey={21} endKey={108} fallbackBehavior="checkbox" name="checkbox" />
  261. </Wrapper>
  262. )
  263. export const Radio = (props?: Partial<Props>) => (
  264. <Wrapper>
  265. <Keyboard {...props} startKey={21} endKey={108} fallbackBehavior="radio" name="radio" />
  266. </Wrapper>
  267. )
  268. export const Link = (props?: Partial<Props>) => (
  269. <Wrapper>
  270. <Keyboard {...props} startKey={21} endKey={108} fallbackBehavior="link" href={(key) => `?key=${key}`} />
  271. </Wrapper>
  272. )
  273. export const Rotated90 = (props?: Partial<Props>) => (
  274. <HasMapComponent {...props} orientation={90} width={80} height={600} />
  275. )
  276. export const Rotated180 = (props?: Partial<Props>) => <HasMapComponent {...props} orientation={180} />
  277. export const Rotated270 = (props?: Partial<Props>) => (
  278. <HasMapComponent {...props} orientation={270} width={80} height={600} />
  279. )
  280. export const Rotated90Mirrored = (props?: Partial<Props>) => (
  281. <HasMapComponent {...props} orientation={90} width={80} height={600} mirrored />
  282. )
  283. export const Rotated180Mirrored = (props?: Partial<Props>) => <HasMapComponent {...props} orientation={180} mirrored />
  284. export const Rotated270Mirrored = (props?: Partial<Props>) => (
  285. <HasMapComponent {...props} orientation={270} width={80} height={600} mirrored />
  286. )
  287. export const LabelledKeyboard = (props?: Partial<Props>) => (
  288. <HasMapComponent {...props} startKey={21} endKey={108} keyLabels={(key: number) => key} />
  289. )
  290. export const LabelledOctave = (props?: Partial<Props>) => (
  291. <HasMapComponent
  292. {...props}
  293. startKey={21}
  294. endKey={108}
  295. keyLabels={(key: number) => {
  296. if (Math.floor(key / 12) === 5) {
  297. return key.toString()
  298. }
  299. return ''
  300. }}
  301. />
  302. )
  303. export const LabelledPitch = (props?: Partial<Props>) => (
  304. <HasMapComponent
  305. {...props}
  306. startKey={21}
  307. endKey={108}
  308. keyLabels={(key: number) => {
  309. if (key % 12 === 0) {
  310. return 'C'
  311. }
  312. return ''
  313. }}
  314. />
  315. )
  316. export const LabelledRotatedKeyboard = (props?: Partial<Props>) => (
  317. <HasMapComponent
  318. {...props}
  319. orientation={90}
  320. width={80}
  321. height={600}
  322. startKey={21}
  323. endKey={108}
  324. keyLabels={(key: number) => key}
  325. />
  326. )
  327. export const LabelledRotatedMirroredKeyboard = (props?: Partial<Props>) => (
  328. <HasMapComponent
  329. {...props}
  330. orientation={90}
  331. width={80}
  332. height={600}
  333. startKey={21}
  334. endKey={108}
  335. mirrored
  336. keyLabels={(key: number) => key}
  337. />
  338. )
  339. export const LabelledStyledKeyboard = (props?: Partial<Props>) => (
  340. <HasMapComponent
  341. {...props}
  342. startKey={21}
  343. endKey={108}
  344. keyComponents={{
  345. natural: StyledNaturalKey,
  346. accidental: StyledAccidentalKey,
  347. }}
  348. keyLabels={(key: number) => key}
  349. />
  350. )