diff --git a/01LogisticChaos/01LogisticChaos.ino b/01LogisticChaos/01LogisticChaos.ino new file mode 100644 index 0000000..7f6f8ef --- /dev/null +++ b/01LogisticChaos/01LogisticChaos.ino @@ -0,0 +1,41 @@ +// Basic demo for configuring the MCP4728 4-Channel 12-bit I2C DAC +#include +#include + +Adafruit_MCP4728 mcp; + +float xlog = 0.5; +float r = 3.783423; + +void setup(void) { + Serial.begin(115200); + while (!Serial) + delay(10); // will pause Zero, Leonardo, etc until serial console opens + + Serial.println("Adafruit MCP4728 test!"); + + // Try to initialize! + if (!mcp.begin(0x64)) { + Serial.println("Failed to find MCP4728 chip"); + while (1) { + delay(10); + } + } + + mcp.setChannelValue(MCP4728_CHANNEL_A, 4095); + mcp.setChannelValue(MCP4728_CHANNEL_B, 2048); + mcp.setChannelValue(MCP4728_CHANNEL_C, 1024); + mcp.setChannelValue(MCP4728_CHANNEL_D, 0); + + +} + + +void loop() { + int pot = analogRead(A0); + r = (float)pot / 1024.0 + 3.0; + xlog = r * xlog * (1 - xlog); + int cv = round(xlog * 4095.0); + mcp.setChannelValue(MCP4728_CHANNEL_A, cv); + delay(100); +} diff --git a/LogisticAR/LogisticAR.ino b/02LogisticAR/LogisticAR.ino similarity index 100% rename from LogisticAR/LogisticAR.ino rename to 02LogisticAR/LogisticAR.ino diff --git a/03Sequencer/03Sequencer.ino b/03Sequencer/03Sequencer.ino new file mode 100644 index 0000000..69655e7 --- /dev/null +++ b/03Sequencer/03Sequencer.ino @@ -0,0 +1,111 @@ +// Basic demo for configuring the MCP4728 4-Channel 12-bit I2C DAC +#include +#include + +Adafruit_MCP4728 mcp; + + +// 1 3 5 7 hexany log2 +float hexany[] = { 0.12928301694496647, 0.32192809488736235, 0.3923174227787603, 0.5849625007211562, 0.8073549220576041, 0.9068905956085185 }; +float tuning[18]; +//int pitch[] = { -1, 6, 4, -1, 6, 5 }; +//float dur[] = { 2, 1, 1, 2, 1, 1 }; +//int pitch[] = { 4, -1, 4, -1 }; +//float dur[] = { 2, 2, 2, 2 }; +int pitch[] = { 2, 4, 5, 3, 4, 5, 2, 3, 3, 4, 5, 2, 4, 3, 5 ,2 }; +float dur[] = { 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25 }; +float gate = 0.5; +int bpm = 167; +int phrase = 16; +float beat_s = 60.0 / (float)bpm; +int beat_m = round(1000.0 * beat_s); +float voltrange = 4.85; // measured this, probably not accurate +float octave = 4096.0 / voltrange; // number of DAC steps in an octave +float mod_b = 3.141592653589793 / 4.0; + + +int s; +bool noteon = false; +long play_t; +long release_t; +long last_t; +long init_t; + + +void setup(void) { + Serial.begin(115200); + while (!Serial) + delay(10); // will pause Zero, Leonardo, etc until serial console opens + + if (!mcp.begin(0x64)) { + Serial.println("Failed to find MCP4728 chip"); + while (1) { + delay(10); + } + Serial.println("MCP4728 initialised"); + } + + mcp.setChannelValue(MCP4728_CHANNEL_B, 0); + + float n0 = 0; + for( int o = 0; o < 3; o++ ) { + for( int i = 0; i < 18; i++ ) { + tuning[o * 6 + i] = round(n0 + octave * (o + hexany[i])); + } + } + + s = 0; + release_t = 0; + play_t = 0; + last_t = millis(); + init_t = last_t; +} + + +void make_tuning() { + float n0 = 0; + for( int i = 0; i < 18; i++ ) { + tuning[i] = round(n0 + octave * hexany[i]); + } + +} + + +void loop() { + long now = millis(); + if( noteon ) { + if( now - last_t > release_t ) { + noteOff(); + last_t = now; + play_t = round(beat_m * dur[s] * (1.0 - gate)); + noteon = false; + s += 1; + if( s == phrase ) { + s = 0; + } + } + } else { + if( now - last_t > play_t ) { + noteOn(pitch[s]); + last_t = now; + release_t = round(beat_m * dur[s] * gate); + noteon = true; + } + } + // sychronise this better + long mod_t = (now - init_t); + float mod = 0.5 + 0.5 * sin(mod_b * (float)mod_t / (float)beat_m); + mcp.setChannelValue(MCP4728_CHANNEL_C, round(4095.0 * mod)); +} + + +void noteOn(int note) { + if( note > -1 ) { + mcp.setChannelValue(MCP4728_CHANNEL_A, tuning[note]); + mcp.setChannelValue(MCP4728_CHANNEL_B, 4095); + } +} + +void noteOff() { + mcp.setChannelValue(MCP4728_CHANNEL_B, 0); +} diff --git a/04KarplusStrongFish/04KarplusStrongFish.ino b/04KarplusStrongFish/04KarplusStrongFish.ino new file mode 100644 index 0000000..ec373ac --- /dev/null +++ b/04KarplusStrongFish/04KarplusStrongFish.ino @@ -0,0 +1,96 @@ +// Very basic Karplus-Strong + +// based on this explanation http://sites.music.columbia.edu/cmc/MusicAndComputers/chapter4/04_09.php + + +#include +#include + +Adafruit_MCP4728 mcp; + +#define nsamp 256 +#define dacmax 256 + +unsigned int phase; +unsigned int length; +unsigned int repeats; +int note = 0; +//int freqs[] = { 64, 56, 51, 48, 42, 38, 34, 32, 28, 25, 24, 21, 19, 17, 16 }; +//int tune2[] = { 2, -1, -1, 2, -1, -1, 3, -1, 2, -1, -1, -1, 0, -1, -1, -1 }; +//int tune2[] = { 2, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, 4, -1, -1, -1 }; +int freqs[] = { 128, 112, 102, 96, 84, 76, 64, 56, 50, 48, 42, 38, 34, 32 }; +int tune2[] = { 0, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1 }; + +byte waveform[nsamp]; + +int pd = 30; + +void setup() { + + cli(); + +//set timer1 interrupt at 1Hz + TCCR1A = 0;// set entire TCCR1A register to 0 + TCCR1B = 0;// same for TCCR1B + TCNT1 = 0;//initialize counter value to 0 + // set compare match register for 1hz increments + //OCR1A = 15624;// = (16*10^6) / (1*1024) - 1 (must be <65536) + //OCR1A = 7812;// = (16*10^6) / (1*1024) - 1 (must be <65536) + OCR1A = 2000; + // turn on CTC mode + TCCR1B |= (1 << WGM12); + // Set CS10 and CS12 bits for 1024 escaler + TCCR1B |= (1 << CS12) | (1 << CS10); + // enable timer compare interrupt + TIMSK1 |= (1 << OCIE1A); + + sei(); + + + + if (!mcp.begin(0x64)) { + while (1) { + delay(100); + } + } + + randomSeed(analogRead(A0)); + mcp.setSpeed(800000L); + + phase=0; + note=0; +} + +ISR(TIMER1_COMPA_vect){//timer1 + if( tune2[note] > -1 ) { + length=freqs[tune2[note]]; + for (int i=0; i 15 ) { + note = 0; + } + pd = analogRead(A0) >> 4; +} + + + +void loop() { + byte s1, s2; + int p0 = phase; + int p1 = phase - pd; + if( p1 < 0 ) { + p1 += length; + } + phase += 1; + if( phase >= length ) { + phase = 0; + } + s1 = waveform[p1]; + s2 = waveform[p0]; + waveform[p0] = ( s1 & s2 ) + ((s1 ^ s2) >> 1); + mcp.fastWrite(s1 << 4, 0, 0, 0); +} + diff --git a/05Wavetable/05Wavetable.ino b/05Wavetable/05Wavetable.ino new file mode 100644 index 0000000..b7a78e0 --- /dev/null +++ b/05Wavetable/05Wavetable.ino @@ -0,0 +1,128 @@ +//waveform generator + +// hacked from https://www.instructables.com/Arduino-Waveform-Generator-1/ + +#include +#include + +Adafruit_MCP4728 mcp; + +#define nsamp 32 +#define dacmax 256 + +const byte nclk = 200; // a guess +long int freq; //frequency in Hz +long unsigned int phase; +long unsigned int phase_inc; + +int note = 0; +int gate; +int decay = 128; + +float pattern[16]; + +long unsigned int pattern_inc[4]; + +void setup() { + TIMSK0 &= ~_BV(TOIE0); // disable timer0 overflow interrupt + + cli(); + +//set timer1 interrupt at 1Hz + TCCR1A = 0;// set entire TCCR1A register to 0 + TCCR1B = 0;// same for TCCR1B + TCNT1 = 0;//initialize counter value to 0 + // set compare match register for 1hz increments + OCR1A = 3905;// = (16*10^6) / (1*1024) - 1 (must be <65536) + // turn on CTC mode + TCCR1B |= (1 << WGM12); + // Set CS10 and CS12 bits for 1024 prescaler + TCCR1B |= (1 << CS12) | (1 << CS10); + // enable timer compare interrupt + TIMSK1 |= (1 << OCIE1A); + + sei(); + + + Serial.begin(115200); + + if (!mcp.begin(0x64)) { + while (1) { + delay(100); + } + } + +// mcp.setSpeed(400000L); +// mcp.setSpeed(800000L); + mcp.setSpeed(800000L); + + freq=440; + phase=0; + //pattern[0] = 220.0; + // pattern[1] = 261.6255653005987; + // pattern[2] = 369.9944227116345; + // pattern[3] = 391.9954359817495; + for( int i = 0; i < 4; i++ ) { + pattern[i] = 0; + } + for( int i = 4; i < 6; i++ ) { + pattern[i] = 659.2551138257401; + } + for( int i = 6; i < 8; i++ ) { + pattern[i] = 493.8833012561241; + } + for( int i = 8; i < 16; i++ ) { + pattern[i] = 0; + } + + for( int i = 0; i < 16; i++ ) { + pattern_inc[i] = pattern[i] * 975592.231884058; + } + + setwave(); +} + +const float pi=3.14159265; +byte waveform[nsamp]; +byte phaseb = 0; +void setwave(){ + for (int isamp=0; isamp 15 ) { + note = 0; + } + phase_inc = pattern_inc[note]; + gate = 4095; +} + + + +void loop() { + phase += phase_inc; + int redphase = phase >> 27; + mcp.fastWrite(waveform[redphase] << 4, gate, 0, 0); + if( gate > 0 ) { + gate -= decay; + } +} + diff --git a/Sequencer/Sequencer.ino b/Sequencer/Sequencer.ino deleted file mode 100644 index 63b151f..0000000 --- a/Sequencer/Sequencer.ino +++ /dev/null @@ -1,168 +0,0 @@ -// We Wish You a Merry Christmas -#include -#include - -Adafruit_MCP4728 mcp; - - -float tuning[16]; -// 0 = A, 8 = F (tonic) - -int m1[] = { 3, 8, 8, 10, 8, 7, 5, 5, 5, 10, 10, 12, 10, 8, 7, 3, 7, 12, 12, 13, 12, 10, 8, 5, 3, 3, 5, 10, 7, 8, 3, 8, 8, 8, 7, 7, 8, 7, 5, 3, 10, 12, 10, 10, 8, 8, 15, 3, 3, 3, 5, 10, 7, 8 }; -int s1[] = { 0, 2, 4, 5, 6, 7, 8, 10, 12, 14, 16, 17, 18, 19, 20, 22, 24, 26, 28, 29, 30, 31, 32, 34, 36, 37, 38, 40, 42, 44, 48, 50, 52, 54, 56, 60, 62, 64, 66, 68, 72, 74, 76, 77, 78, 79, 80, 82, 84, 85, 86, 88, 90, 92 }; -float d1[] = { 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 2, 1, 1, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 4 }; - -int m2[] = { 8, 1, 2, 3, 4, 5, 0, 1, 3, 8, 8, 3, 5, 7, 8, 7, 0, 1, 3, 8 }; -int s2[] = { 2, 8, 14, 20, 26, 32, 36, 38, 42, 44, 50, 56, 62, 68, 74, 80, 84, 86, 90, 92 }; -float d2[] = { 6, 6, 6, 6, 6, 4, 2, 4, 2, 6, 6, 6, 6, 6, 6, 4, 2, 4, 2, 4 }; - - - -// int m1[] = { 0, 2, 4, 5, 7, 9, 11, 12 }; -// int s1[] = { 0, 2, 4, 6, 8, 10, 12, 14 }; -// float d1[] = { 1.0, 0.5, 1.0, 0.5, 1.0, 0.5, 1.0, 0.5 }; -// int m2[] = { 0, 4, 7, 12, 0, 4, 7, 12, 0, 4, 7, 12, 0, 4, 7, 12 }; -// int s2[] = { 0, 1 , 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; -// float d2[] = { 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 }; - - -float gate = 0.5; -int bpm = 320; -float beat_s = 60.0 / (float)bpm; -int beat = round(1000.0 * beat_s); - -// count_in = number of beats since t0 to wait before we startt playing -// repeat = number of beats to repeat - could be computed from the sequence data - -// From here on, all times are in milliseconds to head off floating point errors - -long count_in = 4 * beat; -int repeat = 96; - - -float voltrange = 5.0; -float octave = 4096.0 / voltrange; // number of DAC steps in an octave -float mod_b = 3.141592653589793 / 4.0; - -typedef struct { - int s = -1; - bool playing = false; - bool looped = false; - long play = 0; - long rel = 0; - long last = 0; - long init = 0; - int *melody; - int *start; - float *duration; - int len = 0; - MCP4728_channel_t pitch; - MCP4728_channel_t gate; -} sequencer; - -sequencer seq1, seq2; -long start_time; - -void setup(void) { - Serial.begin(115200); - while (!Serial) - delay(10); // will pause Zero, Leonardo, etc until serial console opens - - if (!mcp.begin(0x64)) { - Serial.println("Failed to find MCP4728 chip"); - while (1) { - delay(10); - } - Serial.println("MCP4728 initialised"); - } - - - float n0 = 0; - for( int i = 0; i < 16; i++ ) { - tuning[i] = round(n0 + octave * (float)i / 12.0); - } - long now = millis(); - start_time = now; - seq1.last = now; - seq1.pitch = MCP4728_CHANNEL_A; - seq1.gate = MCP4728_CHANNEL_B; - seq1.melody = m1; - seq1.start = s1; - seq1.duration = d1; - seq1.len = sizeof(m1) / sizeof(m1[0]); - - times_to_ms(seq1); - - seq2.last = now; - seq2.pitch = MCP4728_CHANNEL_C; - seq2.gate = MCP4728_CHANNEL_D; - seq2.melody = m2; - seq2.duration = d2; - seq2.start = s2; - seq2.len = sizeof(m2) / sizeof(m2[0]); - - times_to_ms(seq2); - -} - -void times_to_ms(sequencer& seq) { - for( int i = 0; i < seq.len; i++ ) { - seq.start[i] *= beat; - seq.duration[i] = (float)beat * seq.duration[i] * 0.8; - } -} - - - -void loop() { - long now = millis(); - long t = now - start_time; - long beats = -1; - if( t >= count_in ) { - beats = (t - count_in) % (beat * repeat); - runSequencer(seq1, t, beats); - runSequencer(seq2, t, beats); - } -} - -void runSequencer(sequencer& seq, long now, long beats) { - int next = seq.s + 1; - int start; - - if( seq.playing ) { - if( now > seq.rel ) { - mcp.setChannelValue(seq.gate, 0); - seq.playing = false; - } - } - - if( next > seq.len - 1 ) { - seq.s = -1; - seq.looped = true; - return; // don't start the loop yet - } else { - start = seq.start[next]; - } - - if( seq.looped ) { // reached the end and waiting for the start of the bar - if( beats < seq.last ) { - seq.looped = false; // bar has restarted, start waiting - seq.s = -1; - next = 0; - } else { - return; - } - } - seq.last = beats; - - if( beats >= start ) { - seq.s = next; - seq.rel = now + seq.duration[seq.s]; - if( seq.melody[seq.s] > -1 ) { - mcp.setChannelValue(seq.pitch, tuning[seq.melody[seq.s]]); - mcp.setChannelValue(seq.gate, 4095); - } - seq.playing = true; - } -} -