Musical keyboard component written in React.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

WaveGenerator.ts 1.8 KiB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. import SoundGenerator from '../SoundGenerator'
  2. export default class WaveGenerator implements SoundGenerator {
  3. private output: AudioContext
  4. private sounds = 'sine triangle sawtooth square'.split(' ')
  5. private oscillators = new Array(16).fill({})
  6. private channels = new Array(16).fill(0)
  7. private baseFrequency = 440
  8. constructor() {
  9. const tryWindow = window as any
  10. const AudioContext = tryWindow.AudioContext || tryWindow['webkitAudioContext']
  11. this.output = new AudioContext()
  12. }
  13. private getKeyFrequency = (keyNumber: number, baseKeyNumber: number, baseKeyFrequency: number) => (
  14. baseKeyFrequency * Math.pow(
  15. Math.pow(2, 1 / 12),
  16. (keyNumber - baseKeyNumber),
  17. )
  18. )
  19. noteOn(channel: number, key: number, velocity: number) {
  20. if (this.oscillators[channel][key]) {
  21. this.oscillators[channel][key].stop()
  22. delete this.oscillators[channel][key]
  23. }
  24. this.oscillators[channel][key] = this.output.createOscillator()
  25. const gainNode = this.output.createGain()
  26. this.oscillators[channel][key].type = this.sounds[this.channels[channel]]
  27. this.oscillators[channel][key].connect(gainNode)
  28. gainNode.connect(this.output.destination)
  29. gainNode.gain.value = velocity * 0.001
  30. this.oscillators[channel][key].frequency.value = this.getKeyFrequency(key, 69, this.baseFrequency)
  31. this.oscillators[channel][key].start()
  32. }
  33. noteOff(channel: number, key: number, _velocity: number) {
  34. if (this.oscillators[channel][key]) {
  35. try {
  36. this.oscillators[channel][key].stop()
  37. } catch (err) {
  38. }
  39. delete this.oscillators[channel][key]
  40. }
  41. }
  42. changeInstrument(channel: number, patch: number) {
  43. this.channels[channel] = patch
  44. }
  45. getInstrumentNames(): string[] {
  46. return this.sounds
  47. }
  48. }