diff --git a/src/samples/drums/cow-bell.ts b/src/samples/drums/cow-bell.ts new file mode 100644 index 0000000..8fc3ad0 --- /dev/null +++ b/src/samples/drums/cow-bell.ts @@ -0,0 +1,68 @@ +import {createAudioContext} from '../sample-utils.ts'; + +export function playCowBell(volume = 100) { + const context = createAudioContext(); + const time = context.currentTime; + + // Convert volume (0-100) to gain (0-1) + const maxGain = Math.min(Math.max(volume, 0), 100) / 100; + const gainValue = Math.pow(maxGain, 2); + + // Create nodes + const osc1 = context.createOscillator(); + const osc2 = context.createOscillator(); + const gainNode = context.createGain(); + const bandpass = context.createBiquadFilter(); + + // Set up bandpass filter for more metallic character + bandpass.type = 'bandpass'; + bandpass.frequency.setValueAtTime(800, time); + bandpass.Q.setValueAtTime(15, time); + + // Set frequencies for a more authentic cowbell sound + osc1.frequency.setValueAtTime(680, time); // Adjusted primary frequency + osc2.frequency.setValueAtTime(1020, time); // Adjusted secondary frequency + + // Use square waves + osc1.type = 'square'; + osc2.type = 'square'; + + // Set initial gain + gainNode.gain.setValueAtTime(0, time); + + // Very quick attack + gainNode.gain.linearRampToValueAtTime( + gainValue, + time + 0.0005 + ); + + // Much shorter decay + gainNode.gain.exponentialRampToValueAtTime( + gainValue * 0.1, + time + 0.05 + ); + + // Quick final decay + gainNode.gain.exponentialRampToValueAtTime( + 0.01, + time + 0.1 + ); + + // Final cleanup to zero + gainNode.gain.linearRampToValueAtTime( + 0, + time + 0.11 + ); + + // Connect everything + osc1.connect(bandpass); + osc2.connect(bandpass); + bandpass.connect(gainNode); + gainNode.connect(context.destination); + + // Start and stop + osc1.start(time); + osc2.start(time); + osc1.stop(time + 0.11); + osc2.stop(time + 0.11); +} diff --git a/src/samples/play-sample.ts b/src/samples/play-sample.ts index 231fc15..dd48f90 100644 --- a/src/samples/play-sample.ts +++ b/src/samples/play-sample.ts @@ -7,12 +7,14 @@ import {playRide} from './drums/ride.ts'; import {resetAudioContext} from './sample-utils.ts'; import {logger} from '../utils/logger.ts'; import {playHighTom, playLowTom, playMidTom} from './drums/toms.ts'; +import {playCowBell} from './drums/cow-bell.ts'; const sampleToAudioFn: Record void> = { hihat: playHiHat, kick: playKick, snare: playSnare, crash: playCrashCymbal, + 'cow-bell': playCowBell, 'hihat-open': playOpenHiHat, ride: playRide, 'tom-low': playLowTom, @@ -33,5 +35,5 @@ export const playSample = (sample: Sample, volume: number) => { } } -export type Sample = 'hihat' | 'hihat-open' | 'ride' | 'crash' +export type Sample = 'hihat' | 'hihat-open' | 'ride' | 'crash' | 'cow-bell' | 'snare' | 'kick' | 'tom-low' | 'tom-high' | 'tom-mid'; diff --git a/src/scenes/instruments/DrumsScene.ts b/src/scenes/instruments/DrumsScene.ts index 1d48008..c3526ce 100644 --- a/src/scenes/instruments/DrumsScene.ts +++ b/src/scenes/instruments/DrumsScene.ts @@ -7,6 +7,7 @@ const padColors: Record = { kick: Colors.red, snare: Colors.blue, crash: Colors.green, + 'cow-bell': Colors.white, 'hihat-open': Colors.yellow, ride: Colors.yellow2, 'tom-low': Colors.orange2, @@ -21,7 +22,7 @@ export class DrumsScene extends PadsScene { private instruments: Sample[] = [ // cymbals 'crash', - 'crash', + 'cow-bell', 'ride', 'hihat-open', 'hihat',