Skip to content

Commit

Permalink
feat: add more pads and drums (before: 4, now: 8)
Browse files Browse the repository at this point in the history
  • Loading branch information
domi7777 committed Oct 5, 2024
1 parent 643d2ce commit 907b3fd
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 25 deletions.
41 changes: 41 additions & 0 deletions src/samples/hihat-open.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Create an Audio Context
const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();

export function playOpenHiHat() {
// --- 1. White Noise for the hi-hat ---
const bufferSize = audioContext.sampleRate * 0.5; // 0.5 second buffer for longer open hi-hat
const noiseBuffer = audioContext.createBuffer(1, bufferSize, audioContext.sampleRate);
const output = noiseBuffer.getChannelData(0);

for (let i = 0; i < bufferSize; i++) {
output[i] = Math.random() * 2 - 1; // Generate white noise
}

const noise = audioContext.createBufferSource();
noise.buffer = noiseBuffer;

// --- 2. High-pass filter to remove lower frequencies ---
const highPassFilter = audioContext.createBiquadFilter();
highPassFilter.type = 'highpass';
highPassFilter.frequency.setValueAtTime(5000, audioContext.currentTime); // High cutoff for brightness

// --- 3. Low-pass filter to shape the high-end frequencies ---
const lowPassFilter = audioContext.createBiquadFilter();
lowPassFilter.type = 'lowpass';
lowPassFilter.frequency.setValueAtTime(10000, audioContext.currentTime); // Smooth out the very high frequencies

// --- 4. Gain envelope for the sound decay ---
const gainNode = audioContext.createGain();
gainNode.gain.setValueAtTime(0.6, audioContext.currentTime); // Start at a moderate volume
gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.5); // Decay for a sustained open hi-hat sound

// Connect the nodes together: noise -> highPass -> lowPass -> gain -> output
noise.connect(highPassFilter);
highPassFilter.connect(lowPassFilter);
lowPassFilter.connect(gainNode);
gainNode.connect(audioContext.destination);

// Play the noise
noise.start();
noise.stop(audioContext.currentTime + 0.5); // Stop after 0.5 seconds for an open hi-hat effect
}
41 changes: 41 additions & 0 deletions src/samples/ride.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Create an Audio Context
const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();

export function playRide() {
// --- 1. White Noise for the metallic sound ---
const bufferSize = audioContext.sampleRate * 1.2; // Slightly longer buffer for sustained ride sound
const noiseBuffer = audioContext.createBuffer(1, bufferSize, audioContext.sampleRate);
const output = noiseBuffer.getChannelData(0);

for (let i = 0; i < bufferSize; i++) {
output[i] = Math.random() * 2 - 1; // Generate white noise
}

const noise = audioContext.createBufferSource();
noise.buffer = noiseBuffer;

// --- 2. High-pass filter to focus on high frequencies ---
const highPassFilter = audioContext.createBiquadFilter();
highPassFilter.type = 'highpass';
highPassFilter.frequency.setValueAtTime(3000, audioContext.currentTime); // Lower frequency cutoff than hi-hat for more depth

// --- 3. Low-pass filter to smooth out the harshness ---
const lowPassFilter = audioContext.createBiquadFilter();
lowPassFilter.type = 'lowpass';
lowPassFilter.frequency.setValueAtTime(9000, audioContext.currentTime); // Tame the very high frequencies

// --- 4. Gain envelope for the sound decay ---
const gainNode = audioContext.createGain();
gainNode.gain.setValueAtTime(0.8, audioContext.currentTime); // Start at a higher volume for prominence
gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 1.2); // Longer decay for a sustained ride sound

// Connect the nodes: noise -> highPass -> lowPass -> gain -> output
noise.connect(highPassFilter);
highPassFilter.connect(lowPassFilter);
lowPassFilter.connect(gainNode);
gainNode.connect(audioContext.destination);

// Play the noise
noise.start();
noise.stop(audioContext.currentTime + 1.2); // Stop after 1.2 seconds for a ringing ride cymbal effect
}
5 changes: 5 additions & 0 deletions src/samples/tom-high.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {playTom1Low} from './tom-low.ts';

export function playTom2High() {
playTom1Low(120, 400);
}
27 changes: 27 additions & 0 deletions src/samples/tom-low.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Create an Audio Context
const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();

export function playTom1Low(hz = 99, durationInMs = 500) {
// --- 1. Create an oscillator for the base tone ---
const oscillator = audioContext.createOscillator();
oscillator.type = 'sine'; // Use a sine wave for the base
oscillator.frequency.setValueAtTime(hz, audioContext.currentTime); // Lower frequency for a deep tom sound

// --- 2. Create a gain node for controlling volume ---
const gainNode = audioContext.createGain();
gainNode.gain.setValueAtTime(1, audioContext.currentTime); // Increase initial gain for louder sound
gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.5); // Quick decay

// --- 3. Add a slight detuning effect to make the tom sound richer ---
oscillator.detune.setValueAtTime(-5, audioContext.currentTime); // Small detuning for realism

// --- 4. Connect nodes: oscillator -> gain -> output ---
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);

// Start the oscillator
oscillator.start();

// Stop after 0.5 seconds (length of the sound)
oscillator.stop(audioContext.currentTime + (durationInMs / 1000));
}
64 changes: 39 additions & 25 deletions src/scenes/KitScene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import {playSnare} from '../samples/snare.ts';
import {playKick} from '../samples/kick.ts';
import {playCrashCymbal} from '../samples/crash.ts';
import {HexaColor, hexToColor} from '../colors.ts';
import {playOpenHiHat} from '../samples/hihat-open.ts';
import {playRide} from '../samples/ride.ts';
import {playTom2High} from '../samples/tom-high.ts';
import {playTom1Low} from '../samples/tom-low.ts';

// loop
let isRecording = false;
Expand Down Expand Up @@ -57,22 +61,20 @@ function stopPlaying() {
console.log('Loop stopped');
}

const instrumentToSample: Record<Instrument, () => void> = {
hihat: playHiHat,
kick: playKick,
snare: playSnare,
crash: playCrashCymbal,
'hihat-open': playOpenHiHat,
ride: playRide,
'tom-low': playTom1Low,
'tom-high': playTom2High,
}

const playInstrument = (instrument: Instrument) => {
console.log(`Playing ${instrument}`);
switch (instrument) {
case 'hihat':
playHiHat();
break;
case 'snare':
playSnare();
break;
case 'kick':
playKick();
break;
case 'crash':
playCrashCymbal();
break;
}
instrumentToSample[instrument]();
if (isRecording) {
const time = Date.now() - startRecordingTime;
loop.push({
Expand All @@ -84,7 +86,8 @@ const playInstrument = (instrument: Instrument) => {
}

// pads
type Instrument = 'hihat' | 'snare' | 'kick' | 'crash';
type Instrument = 'hihat' | 'hihat-open' | 'ride' | 'crash'
| 'snare' | 'kick' | 'tom-low' | 'tom-high';

type Pad = {
instrument: Instrument,
Expand All @@ -96,7 +99,11 @@ const padColors: Record<Instrument, HexaColor> = {
kick: '#F24E1E',
snare: '#4A90E2',
crash: '#A0D8C5',
}
'hihat-open': '#F9F871',
ride: '#F5C542',
'tom-low': '#FF7F50',
'tom-high': '#9B59B6',
};

// controls
type ControlState = 'idle' | 'readyToRecord' | 'recording' | 'playing';
Expand Down Expand Up @@ -127,11 +134,18 @@ export class KitScene extends Phaser.Scene {
}

private createPads() {
// change the order of the pads if needed, top to bottom and left to right
const pads: Pad[] = [
// top
this.createPad('crash'),
this.createPad('ride'),
this.createPad('hihat-open'),
this.createPad('hihat'),
this.createPad('kick'),
// bottom
this.createPad('snare'),
this.createPad('crash'),
this.createPad('tom-low'),
this.createPad('tom-high'),
this.createPad('kick'),
];

pads.forEach(({button, instrument}) => button
Expand All @@ -150,13 +164,13 @@ export class KitScene extends Phaser.Scene {
);

const resizePads = () => {
const width = window.innerWidth;
const height = window.innerHeight;
// Update the pads
pads[0].button.setSize(width / 2, height / 2).setPosition(0, 0);
pads[1].button.setSize(width / 2, height / 2).setPosition(width / 2, 0);
pads[2].button.setSize(width / 2, height / 2).setPosition(0, height / 2);
pads[3].button.setSize(width / 2, height / 2).setPosition(width / 2, height / 2);
const width = window.innerWidth / 4;
const height = window.innerHeight / 2;
pads.forEach(({button}, index) => {
const x = index % 4 * width;
const y = Math.floor(index / 4) * height;
button.setSize(width, height).setPosition(x, y);
})
};
window.addEventListener('resize', resizePads);
resizePads();
Expand Down

0 comments on commit 907b3fd

Please sign in to comment.