Musical keyboard component written in React.
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 

384 řádky
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. )