aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md29
-rw-r--r--public/sounds/crash.opusbin0 -> 20320 bytes
-rw-r--r--public/sounds/hit-hat-closed.opusbin3920 -> 1753 bytes
-rw-r--r--public/sounds/hit-hat-open.opusbin0 -> 4097 bytes
-rw-r--r--public/sounds/kick.opus (renamed from public/sounds/bass.opus)bin1743 -> 1743 bytes
-rw-r--r--public/sounds/ride.opusbin0 -> 16721 bytes
-rw-r--r--public/sounds/snare.opusbin5577 -> 2138 bytes
-rw-r--r--public/sounds/tom-floor.opusbin0 -> 3137 bytes
-rw-r--r--public/sounds/tom-high.opusbin0 -> 7658 bytes
-rw-r--r--public/sounds/tom-medium.opusbin0 -> 6578 bytes
-rw-r--r--src/lib/dict.ts21
-rw-r--r--src/sounds.ts77
-rw-r--r--src/view/sequencer.ts40
13 files changed, 111 insertions, 56 deletions
diff --git a/README.md b/README.md
index b50e068..118475c 100644
--- a/README.md
+++ b/README.md
@@ -10,17 +10,26 @@ Then, open your browser at `http://localhost:8000`.
# Sounds
-- hit-hat (closed): https://lasonotheque.org/detail-2302-charleston-fermee-7.html
-- snare: https://lasonotheque.org/detail-2304-caisse-claire-1.html
- bass: https://freesound.org/people/karolist/sounds/371192/
+- snare: https://lasonotheque.org/detail-2304-caisse-claire-1.html
+- hit-hat:
+ - open: https://lasonotheque.org/detail-2295-charleston-1.html
+ - closed: https://lasonotheque.org/detail-2302-charleston-fermee-7.html
+- cymbals:
+ - 40 cm: https://lasonotheque.org/detail-2311-cymbale-40cm-1.html
+ - ryde: https://lasonotheque.org/detail-2323-cymbale-ride-51cm-1.html
+- tom:
+ - floor: https://lasonotheque.org/detail-2338-tom-grave-1.html
+ - medium: https://lasonotheque.org/detail-2333-tom-medium-1.html
+ - high: https://lasonotheque.org/detail-2329-tom-aigu-1.html
# Todo
-Multi-sound sequencer:
-
-- [ ] Sub divide beats ?
-
-Working on increasing tempo:
-
-- [ ] Add / Remove beat integrated into sequencer
-- [ ] Augment the BPM by X after Y cycles
+- Start and stop pressing space
+- Rename to `drum.guyonvarch.me`
+- Sub divide beats
+- Add / Remove beat integrated into sequencer
+- Augment the BPM by X after Y cycles
+- Prefill with Rock, Jazz, Funk, …, rythms
+- Control volume of individual blocks
+- Load & save (json files ? URL ?)
diff --git a/public/sounds/crash.opus b/public/sounds/crash.opus
new file mode 100644
index 0000000..a219130
--- /dev/null
+++ b/public/sounds/crash.opus
Binary files differ
diff --git a/public/sounds/hit-hat-closed.opus b/public/sounds/hit-hat-closed.opus
index 1513a5e..c7569b4 100644
--- a/public/sounds/hit-hat-closed.opus
+++ b/public/sounds/hit-hat-closed.opus
Binary files differ
diff --git a/public/sounds/hit-hat-open.opus b/public/sounds/hit-hat-open.opus
new file mode 100644
index 0000000..dec1c7b
--- /dev/null
+++ b/public/sounds/hit-hat-open.opus
Binary files differ
diff --git a/public/sounds/bass.opus b/public/sounds/kick.opus
index 40a8d60..40a8d60 100644
--- a/public/sounds/bass.opus
+++ b/public/sounds/kick.opus
Binary files differ
diff --git a/public/sounds/ride.opus b/public/sounds/ride.opus
new file mode 100644
index 0000000..7d2cae9
--- /dev/null
+++ b/public/sounds/ride.opus
Binary files differ
diff --git a/public/sounds/snare.opus b/public/sounds/snare.opus
index 1808dd3..0efe32e 100644
--- a/public/sounds/snare.opus
+++ b/public/sounds/snare.opus
Binary files differ
diff --git a/public/sounds/tom-floor.opus b/public/sounds/tom-floor.opus
new file mode 100644
index 0000000..b09c607
--- /dev/null
+++ b/public/sounds/tom-floor.opus
Binary files differ
diff --git a/public/sounds/tom-high.opus b/public/sounds/tom-high.opus
new file mode 100644
index 0000000..05d6f19
--- /dev/null
+++ b/public/sounds/tom-high.opus
Binary files differ
diff --git a/public/sounds/tom-medium.opus b/public/sounds/tom-medium.opus
new file mode 100644
index 0000000..659c531
--- /dev/null
+++ b/public/sounds/tom-medium.opus
Binary files differ
diff --git a/src/lib/dict.ts b/src/lib/dict.ts
new file mode 100644
index 0000000..43b9f02
--- /dev/null
+++ b/src/lib/dict.ts
@@ -0,0 +1,21 @@
+type Key = string | number | symbol
+
+export function fromList<V>(xs: Array<{key: Key, value: V}>): Record<Key, V> {
+ let res: any = {}
+ xs.forEach(o => res[o.key] = o.value)
+ return res as Record<Key, V>
+}
+
+export function toList<V>(record: Record<Key, V>): Array<{key: Key, value: V}> {
+ return Object.keys(record)
+ .map(key => ({ key: key, value: record[key] }))
+}
+
+type EnumObject = {[key: string]: number | string};
+type EnumObjectEnum<E extends EnumObject> = E extends {[key: string]: infer ET | string} ? ET : never;
+
+export function values<E extends EnumObject>(enumObject: E): EnumObjectEnum<E>[] {
+ return Object.keys(enumObject)
+ .filter(key => Number.isNaN(Number(key)))
+ .map(key => enumObject[key] as EnumObjectEnum<E>)
+}
diff --git a/src/sounds.ts b/src/sounds.ts
index 5e4c68a..f906a70 100644
--- a/src/sounds.ts
+++ b/src/sounds.ts
@@ -1,13 +1,46 @@
+import * as dict from 'lib/dict'
+
export type Sounds = Record<Sound, AudioBuffer>
export enum Sound {
- Bass,
- Snare,
+ Crash,
+ Ride,
+ TomHigh,
+ TomMedium,
+ TomFloor,
+ HitHatOpen,
HitHatClosed,
+ Snare,
+ Kick,
}
export function all(): Array<Sound> {
- return [Sound.HitHatClosed, Sound.Snare, Sound.Bass]
+ 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()
@@ -17,27 +50,37 @@ export async function load(): Promise<Sounds> {
if (lazy !== undefined) {
return lazy
} else {
- let [bass, snare, hitHatClosed] = await Promise.all([
- fetchSound('/sounds/bass.opus'),
- fetchSound('/sounds/snare.opus'),
- fetchSound('/sounds/hit-hat-closed.opus')
- ])
-
- lazy = {
- [Sound.Bass]: bass,
- [Sound.Snare]: snare,
- [Sound.HitHatClosed]: hitHatClosed
- }
- return lazy
+ 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(name: string): Promise<AudioBuffer> {
- return await fetch(name)
+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]
diff --git a/src/view/sequencer.ts b/src/view/sequencer.ts
index bc26e69..f7e397f 100644
--- a/src/view/sequencer.ts
+++ b/src/view/sequencer.ts
@@ -7,27 +7,15 @@ import * as block from 'view/sequencer/block'
export function view() {
let index = -1
- let columns = [{
- [Sound.Bass]: true,
- [Sound.Snare]: false,
- [Sound.HitHatClosed]: false,
- }]
+ let columns = [soundsLib.record(sound => sound == Sound.Kick)]
let blocksNode = h('div',
{ className: 'g-Sequencer__Blocks' },
- block.column([
- {
- checked: false,
- onCheck: checked => columns[0][Sound.HitHatClosed] = checked
- },
- {
- checked: false,
- onCheck: checked => columns[0][Sound.Snare] = checked
- },
- {
- checked: true,
- onCheck: checked => columns[0][Sound.Bass] = checked
- }
- ])
+ block.column(
+ soundsLib.all().map(sound => ({
+ checked: sound == Sound.Kick,
+ onCheck: checked => columns[0][sound] = checked
+ }))
+ )
)
let onNextStep = (sounds: soundsLib.Sounds) => {
@@ -69,11 +57,7 @@ export function view() {
columns.pop()
},
onAdd: index => {
- columns.push({
- [Sound.Bass]: false,
- [Sound.Snare]: false,
- [Sound.HitHatClosed]: false,
- })
+ columns.push(soundsLib.record(sound => false))
blocksNode.appendChild(block.column(
soundsLib.all().map(sound => ({
checked: false,
@@ -86,9 +70,7 @@ export function view() {
{ className: 'g-Sequencer__Grid' },
h('ol',
{ className: 'g-Sequencer__Column' },
- soundItem('Hit-hat (closed)', Sound.HitHatClosed),
- soundItem('Snare', Sound.Snare),
- soundItem('Bass', Sound.Bass)
+ ...soundsLib.all().map(soundItem)
),
blocksNode
)
@@ -97,13 +79,13 @@ export function view() {
return sequencer
}
-function soundItem(name: string, sound: Sound): Element {
+function soundItem(sound: Sound): Element {
return h('li',
{ onclick: async () => {
let sounds = await soundsLib.load()
soundsLib.play(sounds, sound)
}
},
- name
+ soundsLib.toString(sound)
)
}