Arduino Code for MIDI File Playback

By Edwin Almeida (Project Manager)

Approved by Edwin Almeida (Project Manager)

Approved by Tommy Tang (Mission Systems & Test)

This code will run on Arduino #1. It contains the logic for reading/closing MIDI files on an SD card, as well as logic for the playback controls. The code is written in the model of a finite state machine. The finite state machine is implemented by a switch statement in the main program loop.

The SdFat library is used by the MD_MIDIFile library to access and manipulate the FAT32 file system of the SD card. The MD_MIDIFile library is the main library used to write the Tesla controller code.

Fig1_include librariesFigure 1. Including Libraries

This code section in Figure 1 is used to allow for easier debugging. USE_MIDI is set to 0 when debugging the code, setting the serial rate to 57600 baud and allowing debug statements to print to the serial monitor. USE_MIDI is set to 1 when the code is ready to use for an Arduino MIDI output device; it sets the serial rate to 31250 baud, which is the MIDI serial communications speed.

Fig2_define pinsFigure 2. Define Statements

This section in Figure 2 defines the SD card chip select pin as Digital Pin 10, and the respective pins for Stop, Next, and Play. A function ARRAY_SIZE(a) is also defined to find the number of elements in an array. Additionally, tuneList is an array consisting of the filenames of each MIDI file present on the SD card. The last two lines of the code instantiate SD as an SdFat object and SMF as an MD_MIDIFile object.

Fig3_functionsFigure 3. Library Functions

The functions in Figure 3, midiCallback, sysexCallback, and midiSilence are necessary functions that were taken directly from the MD_MIDIFile library help/example section. The midiCallback function is used to transmit a MIDI serial event (note) using the onboard USART. The sysexCallback is there to handle MIDI system exclusive events however, the function only accepts these events and does not process them. The midiSilence function outputs silence on all 16 MIDI channels; it is used whenever a MIDI file is closed.

Fig4_setupFigure 4. Setup Section

In the setup function shown in Figure 4, the Play, Stop, and Next pins are setup as input pins. The serial rate is set, the SPI rate is set at 8MHz, and the SMF object is initialized.

Fig5_main codeFigure 5. Main Program Loop

This loop is where the state machine is implemented. At the beginning of the loop, variables used later in the code are initialized, and the initial state is set as 1. The state machine is implemented as a switch statement within the main program loop. Case 1 is the initial state of the program. Upon powering on the program will continually cycle into state 1 until the play button is pressed.

When the play button is pressed, the program will move into state 2, where MIDI files are loaded. If the file loads successfully here, the program will move on to state 3. On the other hand, if the load returns an error, the program will move on to state 5 where “i” is increased by one, and then back to state 2 where the next file will now attempt to load.

State/case 3 is where MIDI files are actually played and transmitted over the TXD port utilizing the onboard USART. This is done by means of the “SMF.getNextEvent()” line which reads and transmits the next note in the file. The program will remain in state 3 playing the MIDI file until the end of the file is reached, or either the Next button or Stop button are pressed. If the end of the file is reached, or the stop button is pressed, the program will move on to state 4. However, if the Next button is pressed, the program will move onto state 7.

State 4 is used to close files with “SMF.close()” and silence is transmitted on all MIDI channels. After this, the program moves to state 6, which is a waiting state where the program waits for either the Play or Next button to be pressed.

State 5 is used to skip to the next track by increasing “i”. It is a transition state reached through state 2 (upon a file load error) or state 6 (if the Next button is pressed). The program then waits for 750 mS to filter out unintended multiple presses. And moves to state 2, where the next file is loaded.

State 6 is essentially a “parking” state where the program remains until either the Play or Next button is pressed. It can only be reached through state 4, where files are closed. If the Play button is pressed, the program will move to state 2, where the file corresponding to the current “i” value will be loaded. If the Next button is pressed, the program will move to state 5 (the file skip state).

State 7 can only be reached through state 3, when the Next button is pressed while a file is playing. It increases “i” then closes the currently playing file and outputs silence. The program then moves to state 2 after 750 mS, where the next file is loaded.

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *