summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfakebitpolytechnic <fakebitpoly@gmail.com>2013-07-17 10:54:45 -0700
committerfakebitpolytechnic <fakebitpoly@gmail.com>2013-07-17 10:54:45 -0700
commit39d1e61f7feedb204990db18a464448affb78d84 (patch)
tree9ab9d3239a805e3361df8b749649edd38a8d8cec
parentb7fae643ae8bbbe888dfe3771b1adf68f3c38d0e (diff)
parent4da6508617cc529469644514ba1a69a1d29d5cdb (diff)
Merge pull request #1 from fakebitpolytechnic/Sequencer-mode
Clumsy first attempt at monophonic sequences
-rw-r--r--CheapSynth_SeqMode_v0_9_0.ino300
1 files changed, 300 insertions, 0 deletions
diff --git a/CheapSynth_SeqMode_v0_9_0.ino b/CheapSynth_SeqMode_v0_9_0.ino
new file mode 100644
index 0000000..b6bbbc2
--- /dev/null
+++ b/CheapSynth_SeqMode_v0_9_0.ino
@@ -0,0 +1,300 @@
+// CheapSynth "sequencer" mode 2013 - quite clumsily stuck (by Dave Green) on top of
+// "A very simple MIDI synth."
+// Greg Kennedy 2011
+// http://forum.arduino.cc/index.php?topic=79326.0
+//
+// You should be able to spot the note arrays further down, and can use the modulation MIDI control
+// to adjust the "modulation depth" (ie how grainy the notes sound)
+//
+// Other controls: Program Up + Down = change tempo
+// Continue = "Pause"
+
+#include <avr/pgmspace.h>
+
+#include <MozziGuts.h>
+#include <Oscil.h>
+#include <tables/cos256_int8.h> // table for Oscils to play
+#include <tables/saw256_int8.h> // table for Oscils to play
+#include <tables/square_analogue512_int8.h> // table for Oscils to play
+#include <mozzi_midi.h> // for mtof
+
+#define CONTROL_RATE 128 // powers of 2 please
+
+// Mozzi example uses COS waves for carrier and modulator
+// gets brutal very fast if you use a saw/square carrier
+// gets subtly more brutal if you use smaller wavetables (fewer samples per cycle)
+Oscil<SQUARE_ANALOGUE512_NUM_CELLS, AUDIO_RATE> oscCarrier(SQUARE_ANALOGUE512_DATA);
+Oscil<COS256_NUM_CELLS, AUDIO_RATE> oscModulator(COS256_DATA);
+
+int midiNotes[] = {
+//these are MIDI note values, ie 12 = C, 24 = next C up
+//NB note values of 25+ currently considered as absolute (ie don't change with keypress)
+/*
+16,16,28,28,16,16,26,28,//lee coombs
+16,16,28,28,16,16,26,28,
+16,16,28,28,16,16,26,28,
+16,16,28,28,16,16,26,28,
+*/
+//octaves - blue mon, hung up, erasure? etc
+12,24,12,24,12,24,12,24,12,24,12,24,12,24,12,24,
+12,24,12,24,12,24,12,24,12,24,12,24,12,24,12,24,
+
+//descending bass (eg Bowie's Heroes)
+24,24,21,19,24,24,21,19,24,24,21,19,24,24,21,19,
+24,24,21,19,24,24,21,19,24,24,21,19,24,24,21,19,
+
+//different accented octaves (similar to OMD's Enola Gay)
+12,12,24,12,12,24,12,24,
+12,12,24,12,12,24,12,24,
+12,12,24,12,12,24,12,24,
+12,12,24,12,12,24,12,24,
+
+/// 12/8 shuffle rhythm (eg Muse)
+12,12,24,12,12,24,12,12,24,12,12,24,
+12,12,24,12,12,24,12,12,24,12,12,24,
+};
+
+int modOffsets[] = {
+/*
+//lee C
+12, 12, 12, 12, 12, 19, 12, 12,
+12, 12, 12, 12, 12, 19, 12, 12,
+12, 12, 12, 12, 12, 19, 12, 12,
+12, 12, 12, 12, 12, 19, 12, 12,
+*/
+12, 12, 12, 12, 12, 12, 12, 12, //octaves
+12, 12, 12, 12, 12, 12, 12, 12,
+12, 12, 12, 12, 12, 12, 12, 12,
+12, 12, 12, 12, 12, 12, 12, 12,
+
+// here I'm using different modulation offsets to distinguish between adjacent notes
+// (probably also easy to devise a system to play a rest (ie amplitude=0) if note value=0??
+0,12,0,0,12,0,0,0,0,12,0,0,12,0,0,0,//descending bass
+0,12,0,0,12,0,0,0,0,12,0,0,12,0,0,0,
+
+0,12,0,0,12,0,0,0,0,12,0,0,12,0,0,0,//accented octaves
+0,12,0,0,12,0,0,0,0,12,0,0,12,0,0,0,
+
+12, 12, 12, 12, 12, 12, 12, 12, // shuffle
+12, 12, 12, 12, 12, 12, 12, 12,
+12, 12, 12, 12, 12, 12, 12, 12,
+12, 12, 12, 12, 12, 12, 12, 12,
+};
+
+//float modDepths[] = {
+// 0, 1, 3, 0.5, 6, 2, 1, 2,
+// 0.5, 8, 3, 0.5, 2, 2, 1, 0.125
+//};
+
+float note = 24;
+int lastByte;
+float bendByte =50;
+
+int tuneOffset = 0;
+int tickLengthConverter = 26;
+int arraySize = 32;
+int controlTick = 0;
+int arrayIndex = 0;
+int sequenced;
+int playflag = 1;
+
+float carrierFreq = 10.f;
+float modFreq = 10.f;
+float bendRead = 10.f;
+long modDepth = 1;
+
+float amplitude = 1.f;
+
+void setParams(){
+ // mtof converts MIDI note range to Hz freq
+sequenced=midiNotes[arrayIndex + (arraySize*tuneOffset)];
+
+if (sequenced<25) {sequenced += note; } else {sequenced += 36; }
+//if (tuneOffset >0) {sequenced -= 12; }
+
+carrierFreq = mtof(sequenced);
+
+// FM harmonics are most musical when modulator's @ simple musical offsets from carrier
+// EG +12 semitones (1 oct), -24 semitones (-2 oct), +19 semitones (+1 octave and a fifth)
+modFreq = mtof( ( sequenced + modOffsets[arrayIndex+ (arraySize*tuneOffset)] ) );
+
+// I like to make modulation depth (in Hz) some ratio of the carrier's freq
+// That way, the perceived intensity of the FM is the same throughout the note range
+// modDepth = (long) 6;
+bendRead=(bendByte / 30);
+modDepth = (long) carrierFreq * bendRead * bendRead; // 7 was good
+
+ // Set oscillator frequencies
+ oscCarrier.setFreq(carrierFreq);
+ oscModulator.setFreq(modFreq);
+}
+
+// Move on to the next note
+void incrementIndexes(){
+ arrayIndex ++;
+ int sizelimit=arraySize-1;
+
+//Sample code on how to view what's going on
+Serial.end();
+Serial.begin(115200);
+Serial.print(" arrayIndex:");
+Serial.println( arrayIndex
+ );
+Serial.println();
+Serial.end();
+Serial.begin(31250);
+
+if (tuneOffset==3) {sizelimit = 23;} // hack to allow patterns of different length
+if(arrayIndex>sizelimit){
+ arrayIndex = 0;
+ }
+}
+
+// NOW BACK TO YOUR REGULARLY SCHEDULED MIDI SCAN - comments by Greg Kennedy 2011
+
+#define statusLed 13
+#define tonePin 8
+
+// MIDI channel to answer to, 0x00 - 0x0F
+#define myChannel 0x00
+// set to TRUE and the device will respond to all channels
+#define respondAllChannels true
+
+// midi commands
+#define MIDI_CMD_NOTE_OFF 0x80
+#define MIDI_CMD_NOTE_ON 0x90
+#define MIDI_CMD_KEY_PRESSURE 0xA0
+#define MIDI_CMD_CONTROLLER_CHANGE 0xB0
+#define MIDI_CMD_PROGRAM_CHANGE 0xC0
+#define MIDI_CMD_CHANNEL_PRESSURE 0xD0
+#define MIDI_CMD_PITCH_BEND 0xB0
+
+// this is a placeholder: there are
+// in fact real midi commands from F0-FF which
+// are not channel specific.
+// this simple synth will just ignore those though.
+#define MIDI_CMD_SYSEX 0xF0
+
+// a dummy "ignore" state for commands which
+// we wish to ignore.
+#define MIDI_IGNORE 0x00
+
+// midi "state" - which data byte we are receiving
+#define MIDI_STATE_BYTE1 0x00
+#define MIDI_STATE_BYTE2 0x01
+
+// MIDI note to frequency
+// This isn't exact and may sound a bit detuned at lower notes, because
+// the floating point values have been rounded to uint16.
+// Based on A440 tuning.
+
+// I would prefer to use the typedef for this (prog_uint16_t), but alas that triggers a gcc bug
+// and does not put anything into the flash memory.
+
+// Also note the limitations of tone() which at 16mhz specifies a minimum frequency of 31hz - in other words, notes below
+// B0 will play at the wrong frequency since the timer can't run that slowly!
+uint16_t frequency[128] PROGMEM = {8, 9, 9, 10, 10, 11, 12, 12, 13, 14, 15, 15, 16, 17, 18, 19, 21, 22, 23, 24, 26, 28, 29, 31, 33, 35, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62, 65, 69, 73, 78, 82, 87, 92, 98, 104, 110, 117, 123, 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 4186, 4435, 4699, 4978, 5274, 5588, 5920, 5920, 6645, 7040, 7459, 7902, 8372, 8870, 9397, 9956, 10548, 11175, 11840, 12544};
+
+//setup: declaring iputs and outputs and begin serial
+void setup() {
+ startMozzi(CONTROL_RATE);
+
+ pinMode(statusLed,OUTPUT); // declare the LED's pin as output
+
+ pinMode(tonePin,OUTPUT); // setup tone output pin
+
+ //start serial with midi baudrate 31250
+ // or 38400 for debugging (eg MIDI over serial from PC)
+ Serial.begin(31250);
+
+ // indicate we are ready to receive data!
+ digitalWrite(statusLed,HIGH);
+}
+
+//loop: wait for serial data - DG: in hindsight, this should really go in updateControl()
+// (impressed that it even works this way round!)
+void loop () {
+ audioHook();
+ static byte lastCommand = MIDI_IGNORE;
+ static byte state;
+
+ while (Serial.available()) {
+
+ // read the incoming byte:
+ byte incomingByte = Serial.read();
+
+ // Command byte?
+ if (incomingByte & 0b10000000) {
+ if (respondAllChannels ||
+ (incomingByte & 0x0F) == myChannel) { // See if this is our channel
+
+ if (incomingByte==0xFC) {tuneOffset--; {if (tuneOffset<0) tuneOffset=3;} }
+ if (incomingByte==0xFA) {tuneOffset++; {if (tuneOffset>3) tuneOffset=0;} }
+ if (incomingByte==0xFB) {playflag++; {if (playflag>1) playflag=0;} }
+
+ lastCommand = incomingByte & 0xF0;
+ } else { // Not our channel. Ignore command.
+ lastCommand = MIDI_IGNORE;
+ }
+ state = MIDI_STATE_BYTE1; // Reset our state to byte1.
+ } else if (state == MIDI_STATE_BYTE1) { // process first data byte
+ if ( lastCommand==MIDI_CMD_NOTE_OFF )
+ { // if we received a "note off", make sure that is what is currently playing
+ if (note == incomingByte) noTone(tonePin);
+ state = MIDI_STATE_BYTE2; // expect to receive a velocity byte
+ } else if ( lastCommand == MIDI_CMD_NOTE_ON ){ // if we received a "note on", we wait for the note (databyte)
+ lastByte=incomingByte; // save the current note
+ state = MIDI_STATE_BYTE2; // expect to receive a velocity byte
+ }
+ // implement whatever further commands you want here
+ else if ( lastCommand == MIDI_CMD_PITCH_BEND ){
+ bendByte=incomingByte; // save the current note
+ state = MIDI_STATE_BYTE1; // expect to receive a velocity byte
+ }
+
+ else if ( lastCommand == MIDI_CMD_PROGRAM_CHANGE )
+ {
+tickLengthConverter = 31 - (incomingByte % 32);
+
+ // save the current note
+ state = MIDI_STATE_BYTE1; // expect to receive a velocity byte
+ }
+
+
+ } else { // process second data byte
+ if (lastCommand == MIDI_CMD_NOTE_ON) {
+ if (incomingByte != 0) {
+ note = lastByte;
+// tone(tonePin,(unsigned int)pgm_read_word(&frequency[note]));
+// NB ToneMIDISynth generally good for diagnosing MIDI activity
+// setParams();
+ } else if (note == lastByte) {
+ noTone(tonePin);
+ }
+ }
+ state = MIDI_STATE_BYTE1; // message data complete
+ // This should be changed for SysEx
+ }
+ }
+}
+
+void updateControl(){
+ if(controlTick == 0)
+ {
+ setParams();
+ incrementIndexes();
+ }
+
+ // Update controlTick
+ controlTick +=playflag;
+
+ if(controlTick > tickLengthConverter)
+ {
+ controlTick = 0;
+ }
+}
+
+int updateAudio(){
+ long vibrato = modDepth * oscModulator.next();
+ return (int) (oscCarrier.phMod(vibrato)) * amplitude;
+}