// Auduino, the Lo-Fi granular synthesiser // // by Peter Knight, Tinker.it http://tinker.it // // Help: http://code.google.com/p/tinkerit/wiki/Auduino // More help: http://groups.google.com/group/auduino // // Analog in 0: Grain 1 pitch // Analog in 1: Grain 2 decay // Analog in 2: Grain 1 decay // Analog in 3: Grain 2 pitch // Analog in 4: Grain repetition frequency // // Digital 3: Audio out (Digital 11 on ATmega8) // // Changelog: // 19 Nov 2008: Added support for ATmega8 boards // 21 Mar 2009: Added support for ATmega328 boards // 7 Apr 2009: Fixed interrupt vector for ATmega328 boards // 8 Apr 2009: Added support for ATmega1280 boards (Arduino Mega) #include int loop_cnt=0; static uint8_t nunchuck_buf[6]; // array to store nunchuck data, int joy_x_axis = 0; int joy_y_axis = 0; int accel_x_axis = 0; // * 2 * 2; int accel_y_axis = 0; // * 2 * 2; int accel_z_axis = 0; // * 2 * 2; int z_button = 0; int c_button = 0; #include #include uint16_t syncPhaseAcc; uint16_t syncPhaseInc; uint16_t grainPhaseAcc; uint16_t grainPhaseInc; uint16_t grainAmp; uint8_t grainDecay; uint16_t grain2PhaseAcc; uint16_t grain2PhaseInc; uint16_t grain2Amp; uint8_t grain2Decay; // Map Analogue channels #define SYNC_CONTROL (4) #define GRAIN_FREQ_CONTROL (0) #define GRAIN_DECAY_CONTROL (2) #define GRAIN2_FREQ_CONTROL (3) #define GRAIN2_DECAY_CONTROL (1) // Changing these will also requires rewriting audioOn() #if defined(__AVR_ATmega8__) // // On old ATmega8 boards. // Output is on pin 11 // #define LED_PIN 13 #define LED_PORT PORTB #define LED_BIT 5 #define PWM_PIN 11 #define PWM_VALUE OCR2 #define PWM_INTERRUPT TIMER2_OVF_vect #elif defined(__AVR_ATmega1280__) // // On the Arduino Mega // Output is on pin 3 // #define LED_PIN 13 #define LED_PORT PORTB #define LED_BIT 7 #define PWM_PIN 3 #define PWM_VALUE OCR3C #define PWM_INTERRUPT TIMER3_OVF_vect #else // // For modern ATmega168 and ATmega328 boards // Output is on pin 3 // #define PWM_PIN 3 #define PWM_VALUE OCR2B #define LED_PIN 13 #define LED_PORT PORTB #define LED_BIT 5 #define PWM_INTERRUPT TIMER2_OVF_vect #endif // Smooth logarithmic mapping // uint16_t antilogTable[] = { 64830,64132,63441,62757,62081,61413,60751,60097,59449,58809,58176,57549,56929,56316,55709,55109, 54515,53928,53347,52773,52204,51642,51085,50535,49991,49452,48920,48393,47871,47356,46846,46341, 45842,45348,44859,44376,43898,43425,42958,42495,42037,41584,41136,40693,40255,39821,39392,38968, 38548,38133,37722,37316,36914,36516,36123,35734,35349,34968,34591,34219,33850,33486,33125,32768 }; uint16_t mapPhaseInc(uint16_t input) { return (antilogTable[input & 0x3f]) >> (input >> 6); } // Stepped chromatic mapping // uint16_t midiTable[] = { 17,18,19,20,22,23,24,26,27,29,31,32,34,36,38,41,43,46,48,51,54,58,61,65,69,73, 77,82,86,92,97,103,109,115,122,129,137,145,154,163,173,183,194,206,218,231, 244,259,274,291,308,326,346,366,388,411,435,461,489,518,549,581,616,652,691, 732,776,822,871,923,978,1036,1097,1163,1232,1305,1383,1465,1552,1644,1742, 1845,1955,2071,2195,2325,2463,2610,2765,2930,3104,3288,3484,3691,3910,4143, 4389,4650,4927,5220,5530,5859,6207,6577,6968,7382,7821,8286,8779,9301,9854, 10440,11060,11718,12415,13153,13935,14764,15642,16572,17557,18601,19708,20879, 22121,23436,24830,26306 }; uint16_t mapMidi(uint16_t input) { return (midiTable[(1023-input) >> 3]); } // Stepped Pentatonic mapping // uint16_t pentatonicTable[54] = { 0,19,22,26,29,32,38,43,51,58,65,77,86,103,115,129,154,173,206,231,259,308,346, 411,461,518,616,691,822,923,1036,1232,1383,1644,1845,2071,2463,2765,3288, 3691,4143,4927,5530,6577,7382,8286,9854,11060,13153,14764,16572,19708,22121,26306 }; uint16_t mapPentatonic(uint16_t input) { uint8_t value = (1023-input) / (1024/53); return (pentatonicTable[value]); } void audioOn() { #if defined(__AVR_ATmega8__) // ATmega8 has different registers TCCR2 = _BV(WGM20) | _BV(COM21) | _BV(CS20); TIMSK = _BV(TOIE2); #elif defined(__AVR_ATmega1280__) TCCR3A = _BV(COM3C1) | _BV(WGM30); TCCR3B = _BV(CS30); TIMSK3 = _BV(TOIE3); #else // Set up PWM to 31.25kHz, phase accurate TCCR2A = _BV(COM2B1) | _BV(WGM20); TCCR2B = _BV(CS20); TIMSK2 = _BV(TOIE2); #endif } void setup() { nunchuck_setpowerpins(); // use analog pins 2&3 as fake gnd & pwr nunchuck_init(); // send the initilization handshake // Serial.begin(19200); pinMode(PWM_PIN,OUTPUT); audioOn(); pinMode(LED_PIN,OUTPUT); Serial.print("Nunchuck ready\n"); } void loop() { // The loop is pretty simple - it just updates the parameters for the oscillators. // // Avoid using any functions that make extensive use of interrupts, or turn interrupts off. // They will cause clicks and poops in the audio. checkNunchuck(); // Smooth frequency mapping //syncPhaseInc = mapPhaseInc(analogRead(SYNC_CONTROL)) / 4; // Stepped mapping to MIDI notes: C, Db, D, Eb, E, F... //syncPhaseInc = mapMidi(analogRead(SYNC_CONTROL)); /* // Stepped pentatonic mapping: D, E, G, A, B syncPhaseInc = mapPentatonic(analogRead(SYNC_CONTROL)); grainPhaseInc = mapPhaseInc(analogRead(GRAIN_FREQ_CONTROL)) / 2; grainDecay = analogRead(GRAIN_DECAY_CONTROL) / 8; grain2PhaseInc = mapPhaseInc(analogRead(GRAIN2_FREQ_CONTROL)) / 2; grain2Decay = analogRead(GRAIN2_DECAY_CONTROL) / 4; */ syncPhaseInc = mapPentatonic(joy_x_axis); grainPhaseInc = mapPhaseInc(accel_y_axis) / 2; grainDecay = accel_x_axis / 8; grain2PhaseInc = mapPhaseInc(joy_y_axis) / 2; grain2Decay = accel_z_axis / 4; } void checkNunchuck() { if( loop_cnt > 100 ) { // loop()s is every 1msec, this is every 100msec nunchuck_get_data(); // nunchuck_print_data(); loop_cnt = 0; // reset for } loop_cnt++; } // // Nunchuck functions // // Uses port C (analog in) pins as power & ground for Nunchuck static void nunchuck_setpowerpins() { #define pwrpin PORTC3 #define gndpin PORTC2 DDRC |= _BV(pwrpin) | _BV(gndpin); PORTC &=~ _BV(gndpin); PORTC |= _BV(pwrpin); delay(100); // wait for things to stabilize } // initialize the I2C system, join the I2C bus, // and tell the nunchuck we're talking to it void nunchuck_init() { Wire.begin(); // join i2c bus as master Wire.beginTransmission(0x52); // transmit to device 0x52 Wire.send(0x40); // sends memory address Wire.send(0x00); // sends sent a zero. Wire.endTransmission(); // stop transmitting } // Send a request for data to the nunchuck // was "send_zero()" void nunchuck_send_request() { Wire.beginTransmission(0x52); // transmit to device 0x52 Wire.send(0x00); // sends one byte Wire.endTransmission(); // stop transmitting } // Receive data back from the nunchuck, // returns 1 on successful read. returns 0 on failure int nunchuck_get_data() { int cnt=0; Wire.requestFrom (0x52, 6); // request data from nunchuck while (Wire.available ()) { // receive byte as an integer nunchuck_buf[cnt] = nunchuk_decode_byte(Wire.receive()); cnt++; } nunchuck_send_request(); // send request for next data payload // If we recieved the 6 bytes, then go print them if (cnt >= 5) { joy_x_axis = nunchuck_buf[0]; joy_y_axis = nunchuck_buf[1]; accel_x_axis = nunchuck_buf[2]; // * 2 * 2; accel_y_axis = nunchuck_buf[3]; // * 2 * 2; accel_z_axis = nunchuck_buf[4]; // * 2 * 2; z_button = 0; c_button = 0; // byte nunchuck_buf[5] contains bits for z and c buttons // it also contains the least significant bits for the accelerometer data // so we have to check each bit of byte outbuf[5] if ((nunchuck_buf[5] >> 0) & 1) z_button = 1; if ((nunchuck_buf[5] >> 1) & 1) c_button = 1; if ((nunchuck_buf[5] >> 2) & 1) accel_x_axis += 2; if ((nunchuck_buf[5] >> 3) & 1) accel_x_axis += 1; if ((nunchuck_buf[5] >> 4) & 1) accel_y_axis += 2; if ((nunchuck_buf[5] >> 5) & 1) accel_y_axis += 1; if ((nunchuck_buf[5] >> 6) & 1) accel_z_axis += 2; if ((nunchuck_buf[5] >> 7) & 1) accel_z_axis += 1; // scale to levels of analog inputs 0 .. 1023 joy_x_axis -= 28; joy_x_axis *= 5; joy_y_axis -= 38; joy_y_axis *= 5; accel_x_axis -= 72; accel_x_axis *= 5; accel_y_axis -= 72; accel_y_axis *= 5; accel_z_axis -= 78; accel_z_axis *= 5; analogWrite(5, accel_x_axis / 4); analogWrite(6, accel_y_axis / 4); analogWrite(9, accel_z_axis / 4); analogWrite(10, joy_x_axis / 4); analogWrite(11, joy_y_axis / 4); return 1; // success } return 0; //failure } // Print the input data we have recieved // accel data is 10 bits long // so we read 8 bits, then we have to add // on the last 2 bits. That is why I // multiply them by 2 * 2 void nunchuck_print_data() { static int i=0; Serial.print(i,DEC); Serial.print("\t"); Serial.print("joy:"); Serial.print(joy_x_axis,DEC); Serial.print(","); Serial.print(joy_y_axis, DEC); Serial.print(" \t"); Serial.print("acc:"); Serial.print(accel_x_axis, DEC); Serial.print(","); Serial.print(accel_y_axis, DEC); Serial.print(","); Serial.print(accel_z_axis, DEC); Serial.print("\t"); Serial.print("but:"); Serial.print(z_button, DEC); Serial.print(","); Serial.print(c_button, DEC); Serial.print("\r\n"); // newline i++; } // Encode data to format that most wiimote drivers except // only needed if you use one of the regular wiimote drivers char nunchuk_decode_byte (char x) { x = (x ^ 0x17) + 0x17; return x; } // // // SIGNAL(PWM_INTERRUPT) { uint8_t value; uint16_t output; syncPhaseAcc += syncPhaseInc; if (syncPhaseAcc < syncPhaseInc) { // Time to start the next grain grainPhaseAcc = 0; grainAmp = 0x7fff; grain2PhaseAcc = 0; grain2Amp = 0x7fff; LED_PORT ^= 1 << LED_BIT; // Faster than using digitalWrite } // Increment the phase of the grain oscillators grainPhaseAcc += grainPhaseInc; grain2PhaseAcc += grain2PhaseInc; // Convert phase into a triangle wave value = (grainPhaseAcc >> 7) & 0xff; if (grainPhaseAcc & 0x8000) value = ~value; // Multiply by current grain amplitude to get sample output = value * (grainAmp >> 8); // Repeat for second grain value = (grain2PhaseAcc >> 7) & 0xff; if (grain2PhaseAcc & 0x8000) value = ~value; output += value * (grain2Amp >> 8); // Make the grain amplitudes decay by a factor every sample (exponential decay) grainAmp -= (grainAmp >> 8) * grainDecay; grain2Amp -= (grain2Amp >> 8) * grain2Decay; // Scale output to the available range, clipping if necessary output >>= 9; if (output > 255) output = 255; // Output to PWM (this is faster than using analogWrite) PWM_VALUE = output; }