Created
February 27, 2012 00:32
-
-
Save peterc/1920095 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 beat 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 | |
| # | |
| # Required: | |
| # node, npm and coffee-script installed | |
| # | |
| # To use: | |
| # 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 beat at the footer of this file | |
| # | |
| # Troubleshooter: | |
| # No sound? Switch the comments around on the midiOut.openVirtualPort lines below. | |
| # You probably don't have IAC enabled. (Or use Audio MIDI Setup to enable it..) | |
| # | |
| # Notes: | |
| # Yep, this is scrappy and unstructured. Just a bit of fun on a | |
| # Sunday night :-) | |
| class DrumMachine | |
| constructor: (@midi) -> | |
| @playing = false | |
| @bpm = 160 | |
| @beats = ([] for i in [0..15]) | |
| instruments: | |
| kick: 36 | |
| hihat: 42 | |
| snare: 40 | |
| barDuration: -> | |
| 60000 / @bpm * 4 | |
| stopNote: (note) -> | |
| @midi.sendMessage [128, note, 0] | |
| playNote: (note, velocity, duration) -> | |
| return unless @playing | |
| console.log "Playing note #{note} at #{velocity} for #{duration}ms" | |
| @midi.sendMessage [144, @instruments[note], velocity] | |
| setTimeout => | |
| @stopNote note | |
| , duration | |
| playBeat: -> | |
| @playNote(note[0], note[1], 40) for note in @beats[@currentBeat - 1] | |
| play: -> | |
| @playing = true | |
| @currentBeat = 1 | |
| setInterval => | |
| @playBeat() | |
| @currentBeat++ | |
| @currentBeat = 1 if @currentBeat == 17 | |
| , this.barDuration() / 16 | |
| set: (instrument, beat, velocity = 127) -> | |
| @beats[beat - 1].push [instrument, velocity] | |
| stop: -> | |
| @playing = false | |
| @midi.sendMessage [252, 0, 0] | |
| @midi.sendMessage [176, 123, 0] | |
| # ---------------------------------------------- | |
| midi = require 'midi' | |
| midiOut = new midi.output | |
| #midiOut.openVirtualPort '' # use this line if you have problems | |
| midiOut.openPort(0) # if you have IAC turned on in Audio Midi Setup, this is nicer | |
| dm = new DrumMachine(midiOut) | |
| dm.bpm = 93 | |
| dm.set 'kick', 1 | |
| dm.set 'kick', 4 | |
| dm.set 'kick', 7 | |
| dm.set 'kick', 11 | |
| dm.set 'snare', 5 | |
| dm.set 'snare', 13 | |
| dm.set 'hihat', beat, 100 for beat in [1, 3, 5, 7, 9, 11, 13, 15] | |
| dm.set 'hihat', beat, 39 for beat in [2, 4, 6, 8, 10, 12, 14, 16] | |
| dm.play() | |
| process.addListener "SIGTERM", -> | |
| dm.stop | |
| midiOut.closePort() |
Author
In Node it's very acceptable. In the browser, yeah.. timers can be pretty bad. There must be a library aimed at solving this, but an idea that comes to mind is to run a timer at, say, 10 times the resolution and then manually check when the time is right to trigger something. I think it should perform well and be a lot tighter.
good to hear! I'll be experimenting with that approach in the browser shortly. it's mid-high on my to do list.
I think stopNote should be like so
@midi.sendMessage [128, @instruments[note], 0]
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
hey, did you have any problems with timing in this?
is setTimeout tight enough?
I've been looking at in browser js drum machines but the timing is too sloppy.