-
-
Save MaxPleaner/39e6980078800c4a1ef0 to your computer and use it in GitHub Desktop.
Simple 16 step drum machine using CoffeeScript and Node
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Simple 16 step drum machine experiment with Node and CoffeeScript | |
# by Peter Cooper - @peterc | |
# | |
# Inspired by Giles Bowkett's screencast at | |
# http://gilesbowkett.blogspot.com/2012/02/making-music-with-javascript-is-easy.html | |
# | |
# Screencast demo of this code at http://www.youtube.com/watch?v=qWKkEaKL6DQ | |
# | |
# Required: | |
# node, npm and coffee-script installed | |
# | |
# To run: | |
# npm install midi | |
# run up Garageband | |
# create a software instrument (ideally a "drum kit") | |
# run this script with "coffee [filename]" | |
# ctrl+c to exit | |
# customize the drum steps at the footer of this file | |
# | |
# Yeah, not great OOD for real, but just a fun Sunday night | |
# project for now :-) | |
# | |
# P.S. This is my first CoffeeScript in > 1 year so I have NO IDEA | |
# if I'm even vaguely doing it right. But it works. Please leave | |
# comments with code improvements to help me learn! | |
class DrumMachine | |
constructor: (@midi) -> | |
@playing = false | |
@bpm = 160 | |
@currentStep = 1 | |
@totalSteps = 16 # changeable for different time signatures | |
@steps = ([] for i in [1..64]) | |
instruments: | |
kick: 36 | |
hihat: 42 | |
clap: 39 | |
snare: 40 | |
cowbell: 56 | |
crash: 49 | |
barDuration: -> | |
60000 / @bpm * 4 | |
stopNote: (note) -> | |
@midi.sendMessage [128, note, 0] | |
playNote: (note, velocity, duration) -> | |
return unless @playing | |
velocity += Math.floor(Math.random() * 20) - 10 | |
velocity = 0 if velocity < 0 | |
velocity = 127 if velocity > 127 | |
console.log "#{note}\t#{("*" for x in [0..(velocity / 7)]).join('')}" | |
@midi.sendMessage [144, @instruments[note], velocity] | |
setTimeout => | |
@stopNote note | |
, duration | |
playStep: (step) -> | |
@playNote(note[0], note[1], 40) for note in @steps[step] | |
play: -> | |
@playing = true | |
lp = setInterval => | |
@playStep @currentStep - 1 | |
@currentStep++ | |
@currentStep = 1 if @currentStep == @totalSteps + 1 | |
clearInterval lp unless @playing | |
, (this.barDuration() / @totalSteps) | |
set: (instrument, steps, velocity = 127) -> | |
@steps[step - 1].push [instrument, velocity] for step in steps | |
pause: -> | |
@playing = false | |
stop: -> | |
@pause() | |
@currentStep = 1 | |
@midi.sendMessage [252, 0, 0] | |
@midi.sendMessage [176, 123, 0] | |
# ----- | |
midi = require 'midi' | |
midiOut = new midi.output | |
try | |
midiOut.openPort(0) | |
catch error | |
midiOut.openVirtualPort '' | |
dm = new DrumMachine(midiOut) | |
dm.bpm = 96 | |
# dm.set <instrument>, <step nos> (, <velocity>) | |
dm.set 'hihat', [1, 3, 5, 7, 9, 11, 13, 15], 100 | |
dm.set 'hihat', [2, 4, 6, 8, 10, 12, 14, 16], 39 | |
dm.set 'kick', [1, 4, 7, 11] | |
dm.set 'snare', [5, 13] | |
# extra bits added for fun | |
dm.set 'cowbell', [2, 4, 7, 12], 20 | |
dm.set 'cowbell', [3, 5, 8, 13], 80 | |
dm.set 'crash', [15], 20 | |
dm.set 'clap', [5, 13], 80 | |
dm.set 'clap', [16], 50 | |
dm.play() | |
process.addListener "SIGTERM", -> | |
dm.stop | |
midiOut.closePort() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# watchr file to live restart the drum machine when changes | |
# are made to the code. Perhaps there's a better way of doing | |
# this.. let me know! | |
# | |
# To use, you need Ruby, gem install watchr, and run | |
# watchr <filename of this> | |
$running_pid = nil | |
watch('.*\.coffee') do |md| | |
Process.kill("INT", $running_pid) if $running_pid | |
$running_pid = fork do | |
exec("coffee", md[0]) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment