//HSS3S04V01, gijs gieskes 2012, http://gieskes.nl //for use with HSS3j hardware: http://gieskes.nl/instruments/?file=hard-soft-synth-3 //how to upload: http://gieskes.nl/undefined/tutorials/?f=HSS3-software-update //upload using arduino version 21 only. // // //MANUAL normal mode: //pot 0 = select 1 of the 24 notes/picthes //pot 0 + big button = select 1 of the 24 notes/picthes on release of the button //pot 1 = sets pitch of the triangle wave that fills the wavtable //pot 2 = oscillator 2 detune //pot 3 = filter LED sequencial on/off speed all the way to the left is hold. //pot 4 = sets speed of how fast the wavetable is being filled with the triangle wave (in relation to pot[1] //pot 5 = nothing yet. // // //MANUAL midi keyboard mode: //when a midi note is send to the the HSS3 it will go into midi keyboard mode. //you can go can go out of midi mode by pressing the small button. // //pot 0 = playback speed of midi note buffer //bug button = when pressed midi notes that are being played will not be stopped //when a note off message is recieved. // //all other pots keep the same funtion as mentioned above, but will be replaced //when CC messages are recieved that are connected to the pot in question. //that is 20 to 25 // // volatile byte frequency = 255; volatile byte frame = 255; volatile byte wtl = 255; volatile boolean triggerSignal = false; volatile boolean audioInActive = true; volatile byte butSelP = 255; volatile unsigned int butSelVolDel = 0; volatile byte slideSpeed; volatile byte decaySpeed; byte pitch = 48; //oscs unsigned int oscA = 0; unsigned int oscB = 0; unsigned int oscC = 0; unsigned int oscD = 0; int oscE = 0; unsigned int oscF = 0; unsigned int oscA2 = 255; unsigned int oscB2 = 254; unsigned int oscD2 = 0; unsigned int oscC2 = 0; byte oscDiv = 0; byte seqCount = 0; byte seqPitches[12][64]; byte bank = 0; //pot array volatile int pot[8] = {0,0,0,0,0,0,0,0}; //trigger vars volatile boolean externalTrigger = false; volatile boolean triggerActive = false; volatile boolean audioTrigger = false; //midi library: //http://timothytwillman.com/itp_blog/?page_id=240 #include "Midi.h" //midi sequencing vars byte midiCounter; boolean midiPlay = false; boolean midiDisable = false; boolean midibool = false; boolean midiActive = false; boolean midibools[6] = {0, 0, 0, 0, 0, 0}; byte activeNotes[25]; byte activeCount = 1; class MyMidi : public Midi { public: // Need this to compile; it just hands things off to the Midi class. MyMidi(HardwareSerial &s) : Midi(s) {} void handleControlChange(unsigned int channel, unsigned int controller, unsigned int value) { // if(controller == 20 || 21 || 22 || 23 || 24 || 25 || 26){ midibool = true; } switch(controller){ case 20: midibools[0] = true; pot[0] = 1023-(value<<3); break; case 21: midibools[1] = true; pot[1] = 1023-(value<<3); break; case 22: midibools[2] = true; pot[2] = 1023-(value<<3); break; case 23: midibools[3] = true; pot[3] = 1023-(value<<3); break; case 24: midibools[4] = true; pot[4] = 1023-(value<<3); break; case 25: midibools[5] = true; pot[5] = 1023-(value<<3); break; case 26: pot[6] = 1023-(value<<3); break; } } void handleNoteOn(unsigned int channel, unsigned int note, unsigned int velocity) { midiActive = true; midiHandler(channel, note, true); } void handleNoteOff(unsigned int channel, unsigned int note, unsigned int velocity) { midiHandler(channel, note, false); } void handleSync(){ if(midiPlay){ if(!midiCounter){ triggerActive = true; } midiCounter = cupb(midiCounter, 6); } } void handleStop(){ midiPlay = false; } void handleStart(){ seqCount = 0; //reset sequencer so it starts at 0 oscD = 0; //reset sequencer so it starts at 0 midiCounter = 0; midiPlay = true; externalTrigger = true; } void handleContinue(){ midiCounter = 0; midiPlay = true; } }; MyMidi midi(Serial); byte midichannel = 1; byte scaleSelect = 1; boolean typeSwitch = 0; //wave generator values static int octaver = 0; byte butSel = 0; byte triggerDecay = 0; float tvol = 0.5; float tvol2 = 0; float kick = 0; byte tvolb1 = 0; byte tvolb2 = 0; int BA = 0; int BB = 0; int tp = 0; boolean ta = LOW; boolean tb = LOW; boolean tc = LOW; byte scale[33] = { 239, 226, 213, 201, 190, 179, 169, 160, 150, 142, 134, 126, 119, 112, 106, 100, 94, 89, 84, 79, 75, 71, 67, 63, 59, 56, 53, 50, 47, 44, 42, 39, 37 }; byte wave[256]; byte ti = 0; int out = 0; volatile boolean inLoop = false; byte sampleRate = 255; void setup(){ cli(); TCCR1B = TCCR1B & B11111000 | B00000001; TCCR2A = TCCR2A & B11111000 | B00000010; TCCR2B = TCCR2B & B11111000 | B00000010; TIMSK2 = TIMSK2 & B11111000 | B00000010; OCR2A = frequency; sei(); pinMode(4, OUTPUT); pinMode(5, OUTPUT); pinMode(6, OUTPUT); pinMode(7, OUTPUT); pinMode(9, OUTPUT); pinMode(10, OUTPUT); pinMode(11, OUTPUT); pinMode(8, INPUT); digitalWrite(8, HIGH); pinMode(12, INPUT); digitalWrite(12, HIGH); pinMode(13, OUTPUT); pinMode(3, OUTPUT); readAnalogs(); pinMode(2, INPUT); pinMode(3, INPUT); attachInterrupt(0, triggerNormal, FALLING); attachInterrupt(1, triggerOutB, FALLING); midi.begin(1); } ISR(TIMER2_COMPA_vect) { oscA++; if(oscA >= oscA2){oscA = 0;} oscB++; if(oscB >= oscB2){oscB = 0;} oscF = cup(oscF, ((1023 - pot[3])<<3)+50); if(!oscF && pot[3] > 10 && !externalTrigger || triggerActive){ triggerActive = false; tc = !tc; IRsync(); bitWrite(PORTB, 3, tc); } if(midiActive){ oscC2 = cup(oscC2, ((1023-pot[0])<<3)+10); if(!oscC2){ //arpegio for(byte i=0; i<24; i++){ if(audioTrigger){ audioTrigger = false; activeCount = 0; } activeCount = cup(activeCount, 24); //if active note is found use it and break. if(activeNotes[activeCount] > 0){ frequency = activeNotes[activeCount]; break; } } } } oscC = cup(oscC, ((1023-pot[4])>>1)+10); if(!oscC){ if(tb){ oscE += 4; if(oscE >= pot[1]>>2){ oscE = pot[1]>>2; tb = false; } }else{ oscE -= 4; if(oscE < 1){ oscE = 0; tb = true; } } oscD = cup(oscD, 255); wave[oscD] = oscE; ta = true; } OCR2A = frequency; analogWrite(9, wave[oscA]+wave[oscB]>>1); } void loop(){ midi.poll(); readAnalogs(); oscB2 = (1023-pot[2]) >> 2; if(digitalRead(8) && !midiActive){ byte selNote = map((pot[0] >> 3), 0, 127, 0, 24); frequency = scale[selNote]; }else if(!digitalRead(8)){ midiDisable = true; }else{ midiDisable = false; } if(!digitalRead(12)){ externalTrigger = false; midiActive = false; } byte visuals = (min(out << 3, 255)); analogWrite(5, visuals); analogWrite(10, visuals); } ///////// //other// ///////// //read pots, 1 at a time inline void readAnalogs(){ static byte potc = 0; potc = cup(potc, 6); int av = analogRead(potc); if(midibools[potc] == false){ //pot[potc] = analogRead(potc); pot[potc] = av; } } //send trigger void trigger(byte inp){ volatile static int tCount = 0; if(!externalTrigger){ tCount = cup(tCount, inp); IRsync(); } } void triggerNormal(){ //set external trigger to true if there is a trigger signal.. externalTrigger = true; //set trigger to active, see above triggerActive = true; } void triggerOutB(){ audioTrigger = true; } void triggerOut(){ if(triggerSignal){ triggerNormal(); } triggerSignal = !triggerSignal; IRsync(); } inline static int cup(int a, int b){ a++; if(a >= b){ a = 0; } return a; } inline static byte cupb(byte a, byte b){ a++; if(a >= b){ a = 0; } return a; } inline byte audioOut(byte signal){ if(butSelVolDel > 1){ butSelVolDel--; analogWrite(9, constrain(signal>>(pot[6]>>7), 10, 170)); }else{ analogWrite(9, min(signal>>(pot[6]>>7), 220)); } analogWrite(10, min(signal << 2, 255)); } inline void audioOut2(){ if(butSelVolDel > 1){ butSelVolDel--; analogWrite(9, constrain(out>>(pot[6]>>7), 10, 170)); }else{ analogWrite(9, min(out>>(pot[6]>>7), 220)); } analogWrite(10, min(out << 2, 255)); } void IRsync(){ static boolean irflip; irflip = !irflip; bitWrite(PORTB, 5, irflip); bitWrite(PORTD, 4, irflip); } void midiHandler(byte channel, byte note, boolean onOff){ byte noteIn = note % 24; //some keyboards don't send note off.. if(!onOff){ //scale over array, and reset note value. if(!midiDisable){ activeNotes[noteIn] = 0; } }else{ //set emediatly so there are no missing notes when arp is slow checkIfActiveNote(scale[noteIn]); //scale over array, and set note value. activeNotes[noteIn] = scale[noteIn]; } } void checkIfActiveNote(int input){ byte count = 0; //check for active notes for(byte i=0; i<25; i++){ if(activeNotes[i]){ count++; } } //if little active notes are found set freq emediatly if(count < 2){ frequency = input; } }