Demonstration of the Stepper Synth:
ALL project information is contained within this "details" section below, the project logs are recordings of the box playing music only :)
MIDI Implementation Information:
How do I send midi through the Arduino Uno serial link (from windows)?
Step 1: -->
Use "loopMIDI" to create a virtual midi device in the windows environment.
Step 2: -->
Use "Hairless MIDI" to send midi data from your virtual loopmidi midi device (as a "MIDI In") to your Arduino Uno serial port. Make sure to set the serial baud rate in the preferences to match your Arduino code.
You will need to disable hairless to program your Arduino each and every time...
Step 3: -->
Use "MIDIMapper" to force windows to output midi to your loopmidi virtual device.
Step 4: -->
Play a midi file using anything in windows that would ordinarily play midi. I use Winamp.
If you're using midi specific software that can select a midi device directly, you can skip step 3. Try Anvil studio:
OR for testing purposes you can use "VMPK" (a virtual midi keyboard) to send any note. Just make sure it is actually sending to the loopmidi device and not ignoring the now modified windows default.
If your Arduino board supports a native USB (and you are using the usb midi Arduino libraries) you can skip steps 1 and 2.
How do you process the midi information in the Arduino environment?
I used the "Arduino MIDI Library":
This library covers both the usage of the programming port (i.e. over 'serial') and the native USB port. The difference is only inserting the phrase 'USB' a couple of times for setup. The rest is the same.
How do you prepare a MIDI file to sound the best on a stepper synth?
Step 1: -->
Using "Anvil Studio" (mentioned above), I open the midi file and identify the key 4 tracks of the music. I then set these 4 tracks to channels 1, 2, 3 & 4 if it's that simple.
Step 2: -->
If more then one note at once is being played on a track (i.e. chords), I duplicate that track and use the "Track" > "Limit the number of notes played at one time..." option to play, say, the highest note and lowest note (opposite options for each of the identically duplicated tracks. This, of course, means you need another channel.
Step 3: -->
If you notice that there are tracks playing only at times when another track is not playing then combine them! Do this by setting both of those tracks to the same channel! All the steppers sound the same so they may as well play other parts if they're not in confliction! Even if they are, Anvil will choose the higher/lower note (I don't know which).
Step 4: -->
Transpose the entire song (or track only) up/down octaves to fall within the frequency range of the synth. Usually there is no lower limit. Higher frequency notes are hard to hit due to stepper torque requirements for rapid acceleration!
Step 5: -->
Modify the music! As there is no volume control for this implementation (though you could if you dynamically controlled the stepper current), you may want instrument solos to be louder. At those parts of the song only 1 or 2 channels will be playing and you can simply copy paste the music to the other channels so as to really exaggerate the solos.
Stepper Motor Control:
First of all, I'd love to give a shoutout to Jonathan Kayne who's work I originally based my project off of: https://www.hackster.io/JonJonKayne/arduino-midi-stepper-synth-d291ae
Although the software/hardware has changed from his implementation, I never would have tackled this project without his initial work that broke my fear of implementing midi into a project.
The hardware used is actually functionally the same as Jonathan's implementation above. So for further information on that I'd see his project link above.
Arduino Uno --> CNCV3 Shield --> DRV8825 Breakout (X4) --> Steppers (X4)
Most importantly though, I'd recommend using an 18V (40W+) and above power supply instead of the 12V (I used 19V).
This is because the higher the voltage, the faster you can raise the coil currents in the motor. It's also probably good to stay at least 10V below the maximum driver voltage (in my case 45V-10V=35V max for the DRV8825).
I did make a nifty enclosure though! However, it is specific to the power supply I chose. Still, you can always modify it and make another. Jonathan's project page also has a bracket to hold 4 stepper motors only, which is perfect if you want the steppers separate from the electronics. I actually grabbed the stepper screw mounting spacing from this for my enclosure.
Okay, this is important because you're going to run into these exact problems making these steppers music with this hardware configuration/driver pin-outs:
You cannot use the standard Arduino digitalWrite function. It's just too slow. I used the digitalWriteFast library, but this only works for AVR Arduino boards (though there are of course direct pin manipulation solutions for any Arduino micro). Using the faster writes removes the need for frequency "tuning" as the pitch table can now just directly represent the period of the note being played.
The steppers always run hot and need to be disabled when not in use. As there is a single common enable line on the CNCV3 board, you need to track all the channels and disable whenever possible.
Steppers have resonance speeds! And more than one! At these resonance points you need to either shift up/down an octave or, as I implemented, intentionally stall the motor by changing the direction each and every step when playing a resonance note. Don't think you can play every note like this though, as this mode of operation has resonant frequencies too! Fortunately they don't align with the normal resonant frequencies. Don't use stepper weights either, because they make step 4 more difficult.
Acceleration! High speeds need acceleration or there will be an uncontrolled stall! Firstly, to minimise the acceleration difficulties, increase the stepper current to the maximum tolerable and run the highest possible power supply voltage. Don't use micro-stepping (tempting) as it reduces the volume of the music greatly.
Now, acceleration control is easy if the code is completely interrupt driven and everything is fancy pants. But in my case I originally wanted Jonathan's code to work out the box and as a result I had to set a target note and have the actual note increment by 1 each time a step occurred (i.e. from C to C sharp). This only occurred when playing note 84 or above and the ramp up started from note 70. To improve this further would practically require a complete code rewrite.
Also be aware that each time a MIDI note is sent from windows, a stop command is BRIEFLY sent first if a note is already playing.