Solenoise

Piano Playing Robot


Pirates of the Caribbean Theme


Felix Mendelssohn, Allegro di molto in B-Flat Minor, Op. 30: No. 2


Two engineering students procrastinate during the final exams of their junior year of college. The computer engineer had an epiphany, lounging upsidedown on a sofa and looking at the ceiling in a study carrel: a never before created electro-mechanical contraption that could produce entrancing music.

He vividly conveyed his mental image to the other engineer, equally excited for a reason to pause from studying. They decided to start designing immediately. This is the story of the humble beginnings, successes, failures, and ultimate triumph of the two most creative engineers in the world.

Outline

Planning

solenoise sketch sideview cross section engineering view electrical layout electrical layout 2 microcontroler view

Song Processing

Basic Outline: Each arrow below represents processing that we would need to apply to our song data.

MIDI

CSV

Our own binary format

Compressed binary

To make data transfer between the the processing stage and the playing stage convenient, we developed a data structure to neatly contian the data. Music would be stored in a 2d array, where each row represented a time stamp, and each column represented a note. A 1 means that note is played; a zero means it's not. So it would look something like this if I were to walk up each note.


byte music[][5] = { {1,0,0,0,0},
                    {0,1,0,0,0},
                    {0,0,1,0,0},
                    {0,0,0,1,0},
                    {0,0,0,0,1} };

music[0] //the piano keys played on the first time stamp
music[0][3] //whether the second note is played during the first time stamp  
		

Optimization 1

Storing each number as an byte is wasteful, since a byte on the ATmega chip is 8 bits. We could be using 8 times less data if we stored our binary array as a bit array. If we need more than 8 bits, we just add another byte column to our array. The above song would look like this:


byte music[][1] = { {B10000000}
                    {B01000000}
                    {B00100000}
                    {B00010000}
                    {B00001000} };

music[0] //the piano keys played on the first time stamp
music[0] & B01000000//whether the second note is played during the first time stamp
		

Optimization 2

Often a note is held down for a while and adjacent rows in the array can often be found as duplicates. We'll leverage that heuristic and compress adjacent time stamps by encoding the number of duplicates in the last 4 bits of the row. (This leaves 60bits, and hence 60 keys on the piano, still usable). This provides another 16x max-compression rate.


This example song:
byte music[][8] = {
  {B00000000,...,B00100000,...,B00000000},
  {B00000000,...,B00100000,...,B00000000},
  {B00000000,...,B00100000,...,B00000000} 
};

Converts to :
byte music[][8] = { 
  {B00000000,...,B00100000,...,B00000010} 
};
		

Optimization 3

If you store a variable in the SRAM of the ATMega2560 chip (verify that's what we have), we have a max cap of 8 kilobytes that can be used. However, there's another portion of memory lesser known about -- the Flash. No, he doesn't wear a red skin tight suit. The ATMega2560's flash can store 256 kilobytes. That's an increase of 32x!

Code: https://github.com/relspas/Solenoise

Prototyping


The solenoise fetus: Mary had a little lamb


First Major Fail


First Working Test


Final Build

Squids, wires, soldering, and patience.

MDF cutaway piano fit wires bundeled wires diligence hulking

A Word from the Creators

The smartest people in the world

We love building, innovating, designing, creating, and having fun. Can you believe engineers can do that? Neither can we. Over the course of an entire summer, we really poured our heart into this project, and collecting all of the data and building this site was equally as emotional. When GE sponsors us for our next model of Solenoise, we'll be ready.

Website made 2/7/19-4/9/19