aboutsummaryrefslogtreecommitdiff
path: root/src/sounds.ts
blob: f906a70e78261b264eaa039f7fe36346068b16b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import * as dict from 'lib/dict'

export type Sounds = Record<Sound, AudioBuffer>

export enum Sound {
  Crash,
  Ride,
  TomHigh,
  TomMedium,
  TomFloor,
  HitHatOpen,
  HitHatClosed,
  Snare,
  Kick,
}

export function all(): Array<Sound> {
  return dict.values(Sound)
}

export function record<T>(f: (sound: Sound) => T): Record<Sound, T> {
  let res: any = {}

  all().forEach(async sound => {
    res[sound] = f(sound)
  })

  return (res as Record<Sound, T>)
}

export function toString(sound: Sound): string {
  switch (sound) {
    case Sound.Kick: return 'Kick'
    case Sound.Snare: return 'Snare'
    case Sound.HitHatOpen: return 'Hit-hat open'
    case Sound.HitHatClosed: return 'Hit-hat closed'
    case Sound.Ride: return 'Ride'
    case Sound.Crash: return 'Crash'
    case Sound.TomFloor: return 'Tom floor'
    case Sound.TomMedium: return 'Tom medium'
    case Sound.TomHigh: return 'Tom high'
    default: throw `Sound ${sound} is unknown.`
  }
}

const audioContext = new AudioContext()
let lazy: undefined | Sounds = undefined

export async function load(): Promise<Sounds> {
  if (lazy !== undefined) {
    return lazy
  } else {
    let sounds = dict.fromList(await Promise.all(dict.toList(record(fetchSound)).map(async obj => {
      let value = await obj.value
      return { key: obj.key, value }
    }))) as Sounds

    lazy = sounds
    return sounds
  }
}

async function fetchSound(sound: Sound): Promise<AudioBuffer> {
  return await fetch(path(sound))
    .then(res => res.arrayBuffer())
    .then(ArrayBuffer => audioContext.decodeAudioData(ArrayBuffer))
}

function path(sound: Sound): string {
  switch (sound) {
    case Sound.Kick: return 'sounds/kick.opus'
    case Sound.Snare: return 'sounds/snare.opus'
    case Sound.HitHatOpen: return 'sounds/hit-hat-open.opus'
    case Sound.HitHatClosed: return 'sounds/hit-hat-closed.opus'
    case Sound.Ride: return 'sounds/ride.opus'
    case Sound.Crash: return 'sounds/crash.opus'
    case Sound.TomFloor: return 'sounds/tom-floor.opus'
    case Sound.TomMedium: return 'sounds/tom-medium.opus'
    case Sound.TomHigh: return 'sounds/tom-high.opus'
    default: throw `Sound ${sound} is unknown.`
  }
}

export function play(sounds: Sounds, sound: Sound): void {
  const source = audioContext.createBufferSource()
  source.buffer = sounds[sound]
  source.connect(audioContext.destination)
  source.start()
}