My and my buddy Gange decided we wanted a fat vicious and malevolent
music-machine to bring to festivals, for our own listening pleasure when
sitting by the tent and drinking coffee and eating donuts.
Anyway, what we figured was that since we are noobs at building this
type of things we wanted a modular environment. That is we wanted to be
able to exchange parts for other similar but louder ones.
So what we ended up with are pictured here, its a frame made of
particle board. The speakers where bought in a low-price store
(the sound is good though). Car battery from when I thought that it
was dead on my wife’s car and bought a new one for her.
Stereo is really an LG something (although there is a Panasonic on the
picture) the reason we choose the LG is because of its ability to play
MP3’s and also because its 4x50w output (louder 🙂 .
The connector to the stereo pictured is one that came with the box.
Ohh, and we made the frame ourself.
In total this beasts way in at 32kg, which is a lot, so we have a little
carriage for transport.
A view from the top, the babe 🙂
The front with loudspeakers moved to the sides (the cables are about 1,5
meters with a knot as enders)
Box opened, car stereo and battery shown.
Where we soldered and connected everything together.
Yeah, I really want better connectors (and shielded aswell)
A few tips to end this little nonsense page with:
* Be careful
* Try to use car battery’s that doesn’t have water in them.
* Have fun
* Be careful
Oo .oOOOo. .oOOOo.
o O o o o o
O o O. O.
oOooOoOo `OOoo. `OOoo.
o O `O `O
O o o o
o O O. .O O. .O
O. O `oooO' `oooO'
Arduino Step Sequencer
Introduction
A stepsequencer is by wikipedia a “special case” of musical sequencer. But
I figure you know what a stepsequencer is if you’re reading this page, about
me making a stepsequencer with an Arduino. The main reason is probably that
I burned my Gorf when assembling it.
Fiddling with it got me thinking about what I wanted of a step-sequencer and
how I wanted to use it when composing music. The gorf to me was a bit to
small. Dont get me wrong small is nice and all, but not the spacing between
controls. I dont preform drunk (or create music drunk either) but I’d like
my stuff to be usable while intoxicated. I would never have done this unless
I had burned the Gorf-kit :D.
Anyways I wanted a bigger display, so that I could fiddle around with
different setups and functions. I wanted rotary encoders (endless) so that
I would need fewer then 1 per step. I wanted MIDI-out, to you know, control
stuff.
Luckily for me I have a interaction-designer-musician friend that I can
discuss with. So we iterated the idea back and forth. He’s suggestions where
all great, but I didn’t take them all. Had this been a mass-market product
I where going to try to sell I’d probably taken them all, right now I only
took the ones that I wanted!
A few resources have been helpful for me while creating the Ass here is a list:
1 Arduino
1 5pole DIN (MIDI)
1 Sparkfun screen
1 9v Battery holder
1 Input-connector for the battery
5 Buttons
1 OnOff switch
1 Rotary Encoder
1 Knob
1 Plastic case
2 resistors to get the contrast of the screen sane.
As you can se, we’re under 50 dollars in parts.
Everything was pretty easy putting together, and here is a schematic that
is more of a photo with drawn lines then a schematic. Hope it helps if
you want to build your own Ass.
Everything was dead easy programming, everything except the rotary encoder.
I got a 2 dollar one, so i had problems with bouncing and noise on it, as
you’ve seen on the previous video I programmed it while sick so, yeah.
Another thing to think about is that you shouldent update the LCD screen
every time in you’re Loop function, it goes CRAZY then.
I’ve commented the code a lot so I wont go in on it here, I’ve used the
http://www.ladyada.net/library/arduino/copyforhtml.html excellent tool.
UPDATEÂ Magellan has supplied a much better schematics, thank you!
Here is the better schematics:
This is the Ass.pde file
/* Oo .oOOOo. .oOOOo. o O o o o o O o O. O. oOooOoOo `OOoo. `OOoo. o O `O `O O o o o o O O. .O O. .O O. O `oooO' `oooO' Arduino Step Sequencer */
#include <LiquidCrystal.h>
#include "rotaryEncoder.h"
#define usesMidi 1 /*enable this if you want to send midi, doesent work at all together with Serial.print debugging */
#define debugprint 0 /*this sends midi messages as text instead */LiquidCrystal lcd(12, 11, 10, 7, 6, 5, 4); /* Utilizing the liquidcrystal library for the screen, this is a setup-call*/
RotaryEncoder rotary(2, 3); /*sweet sweet library that solves a lot of problems, I made a few changes to the original one *//*defines for the buttons */
#define menuButton 14
#define onOffButton 15
#define lengthButton 16
#define velocityButton 17
#define playPuaseButton 18
#define noteButton 19
/*booleans to tell me if the buttons are pressed or not */byte menuButtonIsPressed =0;
byte onOffButtonIsPressed =0;
byte lengthButtonIsPressed =0;
byte velocityButtonIsPressed =0;
byte playPuaseButtonIsPressed =0;
byte noteButtonIsPressed =0;
/*useful state variables */byte currentlyPlaying =0;
byte currentlyEditing =0;
boolean isPlaying=true;
/*channel and instrument are for the midi messages */byte channel=0;
byte instrument =0;
byte tempo=120; /* bmp of our sequence */long timeChange=250; /*a variable for calculating how long we are going to wait*//*a struct for our notes in our sequence*/
struct data
{
byte velocity;
byte onOff;
byte length;
byte note;
};
int numberofsteps = 8; /* at first we have 8 steps, this is pretty basic */
#define maxNumberOfSteps 17 /*that's 16*/
data myData[maxNumberOfSteps]; /*array for our notes*//*remembering the good old times*/long rememberMillis=0;
long rememberMillisDisplay =0;
/*we'd like to present notenames instead of midi numbers */char* noteNames[] = {
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" };
voidsetup()
{
/*telling everybody we're alive */
lcd.print("Arduino Step Seq");
delay(500);
/*setting up the buttons */pinMode(menuButton,INPUT);
pinMode(onOffButton,INPUT);
pinMode(lengthButton,INPUT);
pinMode(velocityButton,INPUT);
pinMode(playPuaseButton,INPUT);
pinMode(noteButton,INPUT);
/*starting the builtin pullup-resistor!*/digitalWrite(menuButton,HIGH);
digitalWrite(onOffButton,HIGH);
digitalWrite(lengthButton,HIGH);
digitalWrite(velocityButton,HIGH);
digitalWrite(playPuaseButton,HIGH);
digitalWrite(noteButton,HIGH);
/*initialization of the encoder library */
rotary.position(0);
#if usesMidi
Serial.begin(31250);
#else
Serial.begin(9600);
#endif
/*calculating the timeChange variable. milliseconds divided by tempo and 16th note, or something like that. I dont remember*/
timeChange=((60000/tempo)/8);
/*initializing the steps in the array */for(int i=0;iclear();
/*what time is now*/
rememberMillis = millis();
rememberMillisDisplay = millis();
}
/* a little function to print the name of the note */void printNoteName(byte initialNote)
{
byte octave = (initialNote / 12) - 1;
byte noteIndex = (initialNote % 12);
lcd.print(noteNames[noteIndex]);
lcd.print(octave,DEC);
}
/* this functions prints what we have in the first "menu" part of the data tempo, channel and instrument*/void populateDisplayMenuButtonPressed()
{
lcd.setCursor(0, 0) ;
lcd.print("t:");
lcd.print(tempo,DEC);
lcd.print(" c:");
lcd.print(channel,DEC);
lcd.print(" i:");
lcd.print(instrument,DEC);
if (!isPlaying)
{
lcd.setCursor(0, 1) ;
lcd.print("PAUSED ");
}
else
{
lcd.setCursor(0, 1) ;
for(int i=0;iif(i==currentlyPlaying && i==currentlyEditing )
lcd.print("+");
elseif(i==currentlyEditing)
lcd.print("-");
elseif(i==currentlyPlaying)
lcd.print("|");
else
lcd.print(".");
}
/* prints spaces for the rest of the line, it is better then using a lcd.clear, atleast if you call lcd.clear fast/often */for(int i=numberofsteps;i< maxNumberOfSteps ;i++)
{
lcd.print(" ");
}
}
}
/*this is the normal/regular display info printing routine */void populateDisplay()
{
lcd.setCursor(0, 0) ;
if(isPlaying)
{
printNoteName(myData[currentlyEditing].note);
lcd.print(" ");
/*On if the note is On, Off it is Off, getting it? getting on or getting off, muhahahaha */if(myData[currentlyEditing].onOff)
lcd.print("On");
else
lcd.print("Off");
lcd.print(" ");
{
/*what kind of note what we are dealing with */switch (myData[currentlyEditing].length)
{
case 1:
lcd.print("32nd");
break;
case 2:
lcd.print("16th");
break;
case 3:
lcd.print("8th");
break;
case 4:
lcd.print("4th");
break;
case 5:
lcd.print("2nd");
break;
case 6:
lcd.print("whole");
break;
default:
lcd.print("FAIL");
break; /*we made bu bu*/
}
}
lcd.print(" ");
lcd.print(myData[currentlyEditing].velocity,DEC);
lcd.print(" ");
lcd.setCursor(0, 1) ;
for(int i=0;iif(i==currentlyPlaying && i==currentlyEditing )
lcd.print("+");
elseif(i==currentlyEditing)
lcd.print("-");
elseif(i==currentlyPlaying)
lcd.print("|");
else
lcd.print(".");
}
/* prints spaces for the rest of the line, it is better then using a lcd.clear, atleast if you call lcd.clear fast/often */for(int i=numberofsteps;i< maxNumberOfSteps ;i++)
{
lcd.print(" ");
}
}
else
{
lcd.setCursor(0, 1) ;
lcd.print("PAUSED ");
}
}
/* a reading the button function */int readbutt(int button)
{
if (digitalRead(button)==LOW)
{
return 1;
}
return 0;
}
/* function to iterate through the buttons */void checkButtons()
{
if (readbutt(noteButton) && noteButtonIsPressed==false)
noteButtonIsPressed = true;
elseif (noteButtonIsPressed==true && readbutt(noteButton)==false)
noteButtonIsPressed = false;
if (readbutt(menuButton) && menuButtonIsPressed==false)
menuButtonIsPressed = true;
elseif (menuButtonIsPressed==true && readbutt(menuButton)==false)
menuButtonIsPressed = false;
if (readbutt(onOffButton) && onOffButtonIsPressed==false)
onOffButtonIsPressed = true;
elseif (onOffButtonIsPressed==true && readbutt(onOffButton)==false)
onOffButtonIsPressed = false;
if (readbutt(lengthButton) && lengthButtonIsPressed==false)
lengthButtonIsPressed = true;
elseif (lengthButtonIsPressed==true && readbutt(lengthButton)==false)
lengthButtonIsPressed = false;
if (readbutt(velocityButton) && velocityButtonIsPressed==false)
velocityButtonIsPressed = true;
elseif (velocityButtonIsPressed==true && readbutt(velocityButton)==false)
velocityButtonIsPressed = false;
if (readbutt(playPuaseButton) && playPuaseButtonIsPressed==false)
playPuaseButtonIsPressed = true;
elseif (playPuaseButtonIsPressed==true && readbutt(playPuaseButton)==false)
playPuaseButtonIsPressed = false;
}
/*our main baby */voidloop()
{
setCurrentValue();
/* we dont want to update the screen to often, it freaks out */if( millis()-rememberMillisDisplay > 100)
{
rememberMillisDisplay=millis();
checkButtons();
if (!menuButtonIsPressed)
populateDisplay();
else
populateDisplayMenuButtonPressed();
}
if(isPlaying)
{
if( millis()-rememberMillis > ((myData[currentlyPlaying].length * myData[currentlyPlaying].length )*(timeChange)))
{
//fuck it, we always send off, most MIDI devices wont crap themselves.
sendMidiMessage(0x80, myData[currentlyPlaying].note,120); //off
currentlyPlaying++;
if (currentlyPlaying==numberofsteps)
currentlyPlaying=0;
rememberMillis=millis();
if( myData[currentlyPlaying].onOff)
sendMidiMessage(0x90, myData[currentlyPlaying].note, 120); //on
}
}
}
/* This functions makes almost all our values be more correct. like, we dont want negative values or out of range values */void SanitizeValues()
{
myData[currentlyEditing].onOff= myData[currentlyEditing].onOff % 2; //setting the current value to be inbetween 0 and 1
myData[currentlyEditing].note= myData[currentlyEditing].note % 128; //setting the current value to be inbetween 0 and 127
myData[currentlyEditing].velocity= myData[currentlyEditing].velocity % 128; //setting the current value to be inbetween 0 and 127
currentlyEditing=(currentlyEditing % (numberofsteps));
isPlaying = isPlaying%2;
if(numberofsteps > maxNumberOfSteps)
numberofsteps=maxNumberOfSteps;
if(numberofsteps < 0)
numberofsteps=0;
instrument = instrument % 128;
channel = channel% 17;
tempo =tempo % 200;
}
void setCurrentValue()
{
int encoder0Pos = rotary.position();
if(encoder0Pos==0)
return;
if(menuButtonIsPressed)
{
if(playPuaseButtonIsPressed)
{
numberofsteps+=encoder0Pos;
currentlyPlaying =0;
currentlyEditing =0;
}
elseif(noteButtonIsPressed)
{
tempo+=encoder0Pos;
timeChange=((60000/tempo)/8);
}
elseif(onOffButtonIsPressed)
{
channel+=encoder0Pos;
}
elseif(lengthButtonIsPressed)
{
instrument+=encoder0Pos;
}
else
currentlyEditing+=encoder0Pos;
}
else
{
if(noteButtonIsPressed)
myData[currentlyEditing].note+=encoder0Pos;
elseif(onOffButtonIsPressed==true)
myData[currentlyEditing].onOff+=encoder0Pos;
elseif(lengthButtonIsPressed)
{
myData[currentlyEditing].length+=encoder0Pos;
if(myData[currentlyEditing].length > 6)
myData[currentlyEditing].length=6;
if(myData[currentlyEditing].length < 1)
myData[currentlyEditing].length=1;
}
elseif(velocityButtonIsPressed)
myData[currentlyEditing].velocity+=encoder0Pos;
elseif(playPuaseButtonIsPressed)
isPlaying+=encoder0Pos;
else
currentlyEditing+=encoder0Pos;
}
rotary.position(0);
SanitizeValues();
}
void sendMidiMessage(char cmd, char data1, char data2)
{
#if debugprint
Serial.print("Sending MIDI ");
Serial.print(cmd,DEC);
Serial.print(" ");
Serial.print(data1,DEC);
Serial.print(" ");
Serial.println(daserial.pta2,DEC);
#endif
//not sending channel or instrument change yet...
#if usesMidi
Serial.print(cmd, BYTE);
Serial.print(data1, BYTE);
Serial.print(data2, BYTE);
#endif
}
This is the RotaryEncoder.h file
/****************************************************************************** * RotaryEncoder.h - Arduino library for reading RotaryEncoder encoders. * Version 0.90 * modified by Johan Larsby 2009 May 08 ******************************************************************************/
#ifndef RotaryEncoder_h
#define RotaryEncoder_h
#define DIGITAL_PINS (13)
class RotaryEncoder
{
public:
RotaryEncoder(int encoderPin1, int encoderPin2);
voidminimum(intmin);
intminimum(void);
voidmaximum(intmax);
intmaximum(void);
voidnominimum(void);
voidnomaximum(void);
intposition(void);
voidposition(int pos);
int pressed(void);
staticvoid isr(void);
private:
int _readEncoderPins(void);
int _encoderPin1, _encoderPin2;
volatile int _position;
int _min, _max;
bool _usingmin, _usingmax;
volatile int _previous, _time;
static RotaryEncoder* _instance;
};
inline void RotaryEncoder::minimum(intmin)
{
_min = min;
_usingmin = 1;
// adjust position if lower than new minimum//_position = max(_position, min);
_position = _position > min ? _position : min;
}
inline int RotaryEncoder::minimum()
{
return _min;
}
inline void RotaryEncoder::maximum(intmax)
{
_max = max;
_usingmax = 1;
// adjust position if higher than new maximum//_position = min(_position, max);
_position = _position < max ? _position : max;
}
inline int RotaryEncoder::maximum()
{
return _max;
}
inline void RotaryEncoder::nominimum(void)
{
_usingmin = 0;
}
inline void RotaryEncoder::nomaximum(void)
{
_usingmax = 0;
}
inline int RotaryEncoder::position(void)
{
return _position;
}
inline void RotaryEncoder::position(int pos)
{
_position = pos;
}
#endif // RotaryEncoder_h