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.

index.tsx 3.9 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import * as React from 'react'
  2. import ReactDOM from 'react-dom'
  3. import Keyboard from '../src'
  4. import * as Channel from './controllers/Channel'
  5. import * as Instrument from './controllers/Instrument'
  6. import * as Generator from './controllers/Generator'
  7. import keyboardMapping from './services/keyboardMapping'
  8. import SoundGenerator from './services/SoundGenerator'
  9. const App = () => {
  10. const [channel, setChannel] = React.useState(0)
  11. const [keyChannels, setKeyChannels] = React.useState<{ key: number; velocity: number; channel: number }[]>([])
  12. const [instruments, setInstruments, ] = React.useState<string[]>([])
  13. const [instrument, setInstrument] = React.useState(0)
  14. const [inputs, setInputs] = React.useState<any[]>([])
  15. const [input, setInput] = React.useState<number>()
  16. const generator = React.useRef<SoundGenerator | undefined>(undefined)
  17. const scrollRef = React.useRef<HTMLDivElement>(null)
  18. const midiInputRef = React.useRef<any>(null)
  19. React.useEffect(() => {
  20. if (!generator.current) {
  21. return
  22. }
  23. Instrument.reflect({ generator: generator.current, channel, instrument })
  24. }, [channel, instrument])
  25. React.useEffect(() => {
  26. Generator
  27. .load()
  28. .then(g => {
  29. Instrument.initialize({ setInstruments, generator: generator.current = g, })
  30. })
  31. }, [])
  32. React.useEffect(() => {
  33. const { current } = scrollRef
  34. if (current) {
  35. current.scrollLeft = current.scrollWidth * 0.4668
  36. }
  37. }, [scrollRef])
  38. React.useEffect(() => {
  39. const loadMIDIInputs = async () => {
  40. const access = await navigator.requestMIDIAccess()
  41. const inputs = Array.from(access.inputs.entries()).map(([handle, input]) => ({
  42. handle,
  43. input,
  44. }))
  45. midiInputRef.current = inputs[0].input
  46. setInputs(inputs)
  47. if (inputs.length > 0) {
  48. setInput(0)
  49. }
  50. }
  51. loadMIDIInputs()
  52. }, [])
  53. React.useEffect(() => {
  54. const theInput = inputs[input]
  55. const handleMidiMessage = (e: any) => {
  56. const arg0 = e.data[0]
  57. const arg1 = e.data[1]
  58. const arg2 = e.data[2]
  59. const type = arg0 & 0b11110000
  60. if (type === 0b10010000 || type === 0b10000000) {
  61. return
  62. }
  63. if (generator.current! && 'sendMessage' in generator.current!) {
  64. generator.current!.sendMessage!(arg0 & 0b00001111, arg0 & 0b11110000, arg1, arg2)
  65. }
  66. }
  67. if (theInput) {
  68. theInput.input.addEventListener('midimessage', handleMidiMessage)
  69. }
  70. return () => {
  71. if (theInput) {
  72. theInput.input.removeEventListener('midimessage', handleMidiMessage)
  73. }
  74. }
  75. }, [inputs, input])
  76. return (
  77. <React.Fragment>
  78. <input
  79. type="number"
  80. id="channel"
  81. min={0}
  82. max={15}
  83. onChange={Channel.change({ setChannel, })}
  84. defaultValue={0}
  85. />
  86. <select
  87. id="instrument"
  88. onChange={Instrument.change({ setInstrument, })}
  89. defaultValue={0}
  90. >
  91. {Array.isArray(instruments) && instruments.map((name, i) => (
  92. <option
  93. key={i}
  94. value={i}
  95. >
  96. {name}
  97. </option>
  98. ))}
  99. </select>
  100. <div
  101. id="keyboard"
  102. ref={scrollRef}
  103. >
  104. <div
  105. id="keyboard-scroll"
  106. >
  107. <Keyboard
  108. startKey={0}
  109. endKey={127}
  110. keyChannels={keyChannels}
  111. height="100%"
  112. keyboardVelocity={0.75}
  113. onChange={Channel.handle({ setKeyChannels, generator: generator.current!, channel, })}
  114. keyboardMapping={keyboardMapping}
  115. midiInput={inputs.length > 0 && typeof input! === 'number' ? inputs[input].input : undefined}
  116. />
  117. </div>
  118. </div>
  119. </React.Fragment>
  120. )
  121. }
  122. const container = window.document.createElement('div')
  123. container.style.display = 'contents'
  124. window.document.body.appendChild(container)
  125. ReactDOM.render(<App />, container)