Skip to content

🤖 A MIDI player for Arduino, controlled by nodejs.

License

Notifications You must be signed in to change notification settings

afska/arduino-midi-player

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

92 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

arduino-midi-player


A MIDI File player for Arduino, made in CoffeeScript (with node).

introduction

demo

video_play

features

A subset of the MIDI File specification is implemented.

It supports:

  • various tracks
  • simultaneous notes in one track
  • bpm changes

limitations

  • It ignores:

    • effects (bend, vibrato...)
    • track's information (instrument*, volume...)
    • any other non-note events

    * be careful with percussion tracks

  • The higher note that the buzzers can play is a B4: the exception is the first buzzer, which takes advantage of the Arduino's internal timer.

usage

wiring

screenshot1

  • up to 5 buzzers simultaneously, connected to pins [3..7]
  • a 220Ω resistor for each connected buzzer

install

#node is required
npm install grunt-cli
npm install

usage

#upload sketch/fast/fast.ino to your Arduino board
grunt --midi=examples/beethoven-virus.mid
grunt --midi=examples/mentirosa.mid
grunt --midi=examples/pokemon-intro.mid --firstIdle
grunt --midi=examples/pokemon-battle.mid
grunt --midi=examples/technical-difficulties.mid

theory

producing notes

The player is made with buzzers. A buzzer makes a clicking sound each time it is pulsed with current. A clicking sound is not funny, but what if we pulse it 440 times in a second? Bingo, we got an A note.

time of a pulse

To produce a square wave with 440hz of frequency, the time that the pin has to be HIGH is 1136us. How do we know that?

The period is the amount of time it takes for the wave to repeat itself:

owbvrjqo3925446228614708412

=> we need the buzzer's pin to be HIGH in the first half of the period, and LOW in the other half =>

timeHigh = period / 2 = (1 / frequency) / 2

frequency of a note

The frequency of a note can be calculated (taking by reference an A in the 4th octave: A4 - 440hz):

440 * c^distance
//c: a constant, the twelfth root of 2
//distance: semitones between the note and the A4

details for nerds

nodejs? how?

Arduino boards can be controlled by any computer using the Firmata Protocol, particularly by nodejs thanks to the johnny-five programming framework: it sends to the board the instructions by the serial connection.

performance issues

Node is slow. Not really, but it's slower than native C code running on the board. To reach high notes, pauses of very few microseconds are needed. Because of this, the wave-generating part is implemented in the sketch.

node-sketch communication

The js script tells to the sketch what note it has to play and in which speaker: this is made by a pseudo custom protocol. The serial port is used by Firmata to control the board, so extra info can't be appended.

=> The analogWrite message was used on a specific port (3) for sending notes.

For example, to make the buzzer 6 to play an A4, the messages are:

analogWrite(3, 6); //selected buzzer
analogWrite(3, 1136); //timeHigh of the A4

For making the buzzer to stop:

analogWrite(3, 6);
analogWrite(3, 0);

the internal timer

The sketch code (while it's optimized to write ports with direct-io), can't handle high frequencies. That limitation impedes the buzzers [4..7] to play high notes. The only one that can play any notes is the buzzer 3: it uses the internal timer and the tone() function.

merging tracks

Many times, some MIDI Tracks can be unified: while one is playing notes, another is playing silences. The player makes a mix only with the notes that will be actually played. This depends on the playing modes.

the playing modes

Because the max-note-limitation, there're two modes of playing files:

  • First Idle: (--firstIdle) It assigns the first idle buzzer to all notes. This produces more uniform sound when the user is sure that the notes of the MIDI are lower than the max note.
  • High Channel: [default] The notes higher than the max note are assigned to the first buzzer. If two high notes have to sound together, one will be ignored.