-
- Below is an example of a drum machine that loads up three samples for each lane and allows you to program a drum beat.
- The sample is automatically alternated so you never hear the same sample back-to-back.
-
+${PermissionBanner}
+
+ Below is an example of a drum machine that loads up three samples for each lane and allows you to program a drum beat.
+ The sample is automatically alternated so you never hear the same sample back-to-back.
+
+${LoadingSpinner}
+
+
+
+
+ ${codeBlock('// code is [WIP]!')}
`,
}
diff --git a/src/app/public/style.css b/src/app/public/style.css
index 2b43022..07a8242 100644
--- a/src/app/public/style.css
+++ b/src/app/public/style.css
@@ -93,11 +93,16 @@ button.green {
padding: 2rem 0;
}
+.space {
+ margin-top: 1em;
+}
+
.beat-pad {
display: inline-block;
height: 100px;
width: 100px;
text-align: center;
+ margin-right: 4px;
}
.beat-pad .pad {
@@ -105,6 +110,40 @@ button.green {
display: block;
height: 100%;
}
+.controls {
+ text-align: center;
+}
+.beat-machine {
+ display: table;
+ margin: 0 auto;
+}
+.beat-lane {
+ margin: 1em 0;
+}
+.beat-lane .text {
+ display: block;
+}
+.beat-pad {
+ display: inline-block;
+ height: 100px;
+ width: 100px;
+ text-align: center;
+}
+.beat-pad .pad {
+ background-color: #2a2a2a;
+ display: block;
+ height: 100%;
+}
+.beat-pad .pad.highlighted {
+ background-color: #393939;
+}
+.beat-pad .pad.active {
+ background-color: #900101;
+}
+.beat-pad .pad.playing {
+ background-color: #000;
+}
+
[role="button"] {
cursor: pointer;
diff --git a/src/base-sound.ts b/src/base-sound.ts
index bfcbcea..860e8d2 100644
--- a/src/base-sound.ts
+++ b/src/base-sound.ts
@@ -4,6 +4,11 @@ import type { Connectable, Connection } from '@interfaces/connectable'
import type { ControlType, ParamController, RampType, RatioType } from '@controllers/base-param-controller'
import audioContextAwareTimeout from '@utils/timeout'
+interface BaseSoundOptions {
+ name?: string
+ setTimeout?: (fn: () => void, delayMillis: number) => number
+}
+
export abstract class BaseSound implements Connectable, Playable {
protected _isPlaying = false
protected gainNode: GainNode
@@ -21,13 +26,17 @@ export abstract class BaseSound implements Connectable, Playable {
public abstract audioSourceNode: OscillatorNode | AudioBufferSourceNode
public abstract duration: TimeObject
- constructor(protected audioContext: AudioContext, opts?: any) {
+ public name: string
+
+ constructor(protected audioContext: AudioContext, opts?: BaseSoundOptions) {
const gainNode = audioContext.createGain()
const pannerNode = audioContext.createStereoPanner()
this.gainNode = gainNode
this.pannerNode = pannerNode
+ this.name = opts?.name || ''
+
if (opts?.setTimeout) {
this.setTimeout = opts.setTimeout
}
diff --git a/src/beat-track.ts b/src/beat-track.ts
index a5ef26a..e822912 100644
--- a/src/beat-track.ts
+++ b/src/beat-track.ts
@@ -1,10 +1,17 @@
import { Beat } from './beat'
import type { Connectable } from './interfaces/connectable'
import type { Playable } from './interfaces/playable'
+import type { SamplerOptions } from './sampler'
import { Sampler } from './sampler'
const beatBank = new WeakMap()
+export interface BeatTrackOptions extends SamplerOptions {
+ numBeats?: number
+ duration?: number
+ wrapWith?: (beat: Beat) => Beat
+}
+
/**
* An instance of this class has an array of "sounds" (comprised of one or multiple
* audio sources, if multiple are provided, they are played in a round-robin fashion)
@@ -19,16 +26,21 @@ const beatBank = new WeakMap()
* queue?
*/
export class BeatTrack extends Sampler {
- constructor(private audioContext: AudioContext, sounds: (Playable & Connectable)[], opts?: { numBeats?: number, duration?: number }) {
- super(sounds)
+ constructor(private audioContext: AudioContext, sounds: (Playable & Connectable)[], opts?: BeatTrackOptions) {
+ super(sounds, opts)
if (opts?.numBeats) {
this.numBeats = opts.numBeats
}
if (opts?.duration) {
this.duration = opts.duration
}
+ if (opts?.wrapWith) {
+ this.wrapWith = opts.wrapWith
+ }
}
+ private wrapWith?: (beat: Beat) => Beat
+
/**
* @property numBeats
*
@@ -73,7 +85,12 @@ export class BeatTrack extends Sampler {
play: this.play.bind(this),
})
- beats.push(beat)
+ if (this.wrapWith) {
+ beats.push(this.wrapWith(beat))
+ }
+ else {
+ beats.push(beat)
+ }
}
if (existingBeats) {
diff --git a/src/beat.ts b/src/beat.ts
index c8f5001..35ae017 100644
--- a/src/beat.ts
+++ b/src/beat.ts
@@ -44,8 +44,8 @@ export class Beat {
}
}
- private parentPlayIn: ((time: number) => void) | undefined
- private parentPlay: (() => void) | undefined
+ private parentPlayIn: ((time: number) => void)
+ private parentPlay: (() => void)
private setTimeout: (fn: () => void, delayMillis: number) => number
/**
@@ -105,7 +105,7 @@ export class Beat {
public playIn(offset = 0): void {
const msOffset = offset * 1000
- this.parentPlayIn!(offset)
+ this.parentPlayIn(offset)
this.setTimeout(() => {
this.isPlaying = true
@@ -131,7 +131,7 @@ export class Beat {
const msOffset = offset * 1000
if (this.active) {
- this.parentPlayIn!(offset)
+ this.parentPlayIn(offset)
this.setTimeout(() => this.markPlaying(), msOffset)
}
@@ -146,7 +146,7 @@ export class Beat {
* `isPlaying` and `currentTimeIsPlaying` are both immediately marked true.
*/
public play(): void {
- this.parentPlay!()
+ this.parentPlay()
this.markPlaying()
this.markCurrentTimePlaying()
}
@@ -163,7 +163,7 @@ export class Beat {
*/
public playIfActive(): void {
if (this.active) {
- this.parentPlay!()
+ this.parentPlay()
this.markPlaying()
}
diff --git a/src/index.ts b/src/index.ts
index 73d5641..797b630 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,4 +1,3 @@
-import { clone, store } from 'array-buffer-cache'
import type { OscillatorOptsFilterValues } from './oscillator'
import { SampledNote } from './sampled-note'
import type { Connectable } from './interfaces/connectable'
@@ -7,8 +6,11 @@ import { Font } from './font'
import { mungeSoundFont } from './utils/decode-base64'
import { createNoteObjectsForFont, extractDecodedKeyValuePairs } from './utils/note-methods'
import frequencyMap from './utils/frequency-map'
+import type { BeatTrackOptions } from './beat-track'
import { BeatTrack } from './beat-track'
+import { Beat } from '@/beat'
import { MusicallyAware } from '@/musical-identity'
+import type { SamplerOptions } from '@/sampler'
import { Sampler } from '@/sampler'
import { Oscillator } from '@/oscillator'
import { Sound } from '@/sound'
@@ -65,11 +67,16 @@ export async function createTrack(url: string): Promise