/*
Loopytune Mk1 - 8 channel chip tune sequencer.
code origonally by Paul Badger,
heavily modified by kal9001
#############################################
# Frequency range options are at the bottom #
# in the freqout function #
#############################################
*/
// Map all the input and output pins
#define duration_select A4 //Duration PC3
#define frequency_select A3 //Frquency PC4
#define tempo_select A5 //Tempo PC5
#define audio_out A2 //speaker PC2
//Buttons 1 - 8 on PD0 to 7
//Switch on/off PC1
#include <LiquidCrystal.h> // include the LCD library code:
// misc housekeeping
int play = 1;
int lastPushedStep = -1;
int tempo = 512;
int tempo_out;
int duration[] = {250, 250, 250, 250, 250, 250, 250, 250}; // set up duration array
int note[] = {128, 256, 384, 512, 640, 768, 896, 1024}; // Set up note array
int tempo_display;
int duration_display;
int frequency_display;
LiquidCrystal lcd(8, 9, 13, 12, 11, 10); // initialize the library with the numbers of the interface pins
void setup()
{
DDRD = 0x00; //Port D all inputs
PORTD = 0xFF; //port D pull up resistors active
DDRC &= 0xFD; //port C1 as input
PORTC |= 0x02; //port C1 pull up resistor active
pinMode (audio_out, OUTPUT); //PC2
lcd.begin(16, 2); // set up the LCD's number of columns and rows:
lcd.noCursor();
welcome_lcd();
}
void loop()
{
for (int i = 0 ; i < 8 ; i++ )
{
play = digitalRead(start_stop); //read start/stop switch
readPots(); //read Potentiomiters
readSwitches(); //read buttons
update_lcd(); // Update LCD
if (!play)
{
freqout (note[i], duration[i], tempo_display); //Play note for duration
}
else
{
delay(250);
}
}
}
///////////////////////functions//////////////////////////////////////////////
void welcome_lcd()
{
lcd.clear();
lcd.print ("Made by:");
lcd.setCursor(0, 1);
lcd.print ("Drakkn & kal9001");
delay(2000);
lcd.clear();
lcd.print ("Loopytune Mk1");
lcd.setCursor(0, 1);
lcd.print ("V1.22");
delay (2000);
}
void update_lcd()
{
lcd.clear();
lcd.print(("T:" + (String)tempo_display));
lcd.setCursor(0, 1);
lcd.print((((("D:" + (String)duration_display) + " F:") + (String)(frequency_display * 2))+ "Hz"));
if (lastPushedStep != -1)
{
lcd.setCursor(13, 0);
lcd.print(("*" + (String)lastPushedStep));
}
}
void readPots()
{
tempo_display = analogRead(tempo_select);
duration_display = analogRead(duration_select);
frequency_display = analogRead(frequency_select);
}
void readSwitches()
{
lastPushedStep = -1; //reset variable
for (int i = 0 ; i <= 7 ; i++ )
{
if (!(PIND & (1<<i))) //read port D pins one at a time
{
note[i] = frequency_display;
duration[i] = duration_display;
lastPushedStep = (i + 1); //set variable
}
}
}
void freqout(int f, long d, int t)
{
int tempo = t;
int period;
long cycles, i;
if(t == 0) { t = (t + 1); } //avoid any * or / by zero shenanigans
d = d * (t/ 50);
if(d <= 100 ) { d = 100; } // durations less than 100 are just clicks anyway
if(d >= 5000) { d = 5000; } // sets maximum duration to stop things getting silly
// Scale can use any values, lower is higher frequency but resolution is lost.
// lowfq sets the low threshold, low 'scale' needs lower ofset to maintain the same overall level
// Some useful values are listed.
// 500,000L + 40 = 1044 Hz max = 40Hz min ~1Hz/step
// 250,000L + 20 = 2070 Hz max = 40Hz min ~2Hz/step
// 125,000L + 10 = 4132 Hz max = 40Hz min ~4Hz/step
// 62,500L + 5 = 8264 Hz max = 40Hz min ~8hz/step
long scale = 250000;
int ofset = 20;
period = (int)(scale / (f + ofset)); // calculate note period in us
cycles = (int)((f * d) / 1000); //how many loop itterations does it last
for ( i=0 ; i < cycles ; i++ )
{
PORTC |= 0x04; //fast write to audio out pin
delayMicroseconds(period); // delay for half note period
PORTC &= 0xFB; //fast write to audio out pin
delayMicroseconds(period); //delay for half not period
}
}