Go Down

Topic: Nano Groovebox (Read 914 times) previous topic - next topic

janost

Hi
While waiting for the Arduino Due I'm gooing to use for a fullblown Virtual Analog Synthesizer, I warmed up playing with a Nano I had laying.

The result is "The Nano Groovebox", the worlds smallest groovebox.
Well, it could have been matchbox size if I had used tiny buttons and trimpots.
But it's still very small with full size knobs and buttons.

It features a 909 PCM drummachine with 4 tracks and a 2 Osc analog bassline sawsynth.

The drumsection has a tempoknob for BPM, a dualcolor LED that shows the beat and 5 buttons that enable or mute the drumtracks.
The 5th button is for generation a variation in realtime.

The basslinesection has 2 knobs for Osc1 and Osc2 base frequency.
The sound from the bassline is an awsome supersaw/pwm sound that sounds very fat.

The PCM hardware use pin11 as a 62.5k pwm audio output.

The sawsynth use pin2 and pin3 with 10k resistors to create the sawwave.

The jambuttons are connected to a single analog pin through resistors to use only a singel pin for 5 button input.

I feel that I need to add something more as there is still processorpower left.

But it sounds like a dream and fitts in your pocket and can run with a 9v battery and freestyle phones.

Enjoy.



janost

Its coming.

My code is based on yours with some mods and addons.

janost

I added the chromatic scale to the bassline today.
I used float, bad choice, the timing dropped to not useful.

I converted it to long and it works.

I also changd the prescaler for analogread to 32.
That speeded up the code a great deal.

janost

#4
Jul 19, 2013, 09:54 pm Last Edit: Jul 20, 2013, 11:19 am by janost Reason: 1
I also have this IBM trackpoint sensor i plan to use as a jamstick.
You know that thing used in the middle of a laptop as a mouse?

It is an analog strain sensor with 6pins and I have measured the following:

Pin1: Left, nothing on Right
Pin2: Down, nothing on Up
Pin3: Common
Pin4: Right, nothing on Left
Pin5: Up, nothing on Down
Pin6: NC

With direction I mean a resistance between common and the pin less than 5k.
And with nothing I mean megaohm or open.

If we take pin1 Left, it has a resistance of less than 5k when you push the stick left.
It also has resistances of >20k when pulled in other directions but not when pushed right.

Its a bit tricky but I'll make it work.

Edit: I'll use this on the Due synth because it eats to many cycles and analog inputs.

janost

#5
Jul 20, 2013, 01:02 am Last Edit: Jul 20, 2013, 01:34 am by janost Reason: 1
Here is the first video.
The quality is awful and the sound is nothing like what it sounds like in reality.
I filmed it with a HTC mobile and have no idea how people make such good videos of there synths.

Anyway it shows something about what it does.

http://youtu.be/MQRVd6LzLEc73

janost

#6
Jul 20, 2013, 11:12 am Last Edit: Jul 20, 2013, 12:41 pm by janost Reason: 1
Here is the code as it is now.
It has only one single pattern but will compile and perform as in the video.

Code: [Select]

/*
* Arduino Nano Groove, The smallest groovebox in the world.
*
* Plays 8-bit PCM Drums on pin 11 using pulse-width modulation (PWM).
* For Arduino with Atmega328 at 16 MHz.
*
* Uses two timers. The first changes the sample value 8000 times a second.
* The second holds pin 11 high for 0-255 ticks out of a 256-tick cycle,
* depending on sample value. The second timer repeats 62500 times per second
* (16000000 / 256), much faster than the playback rate (8000 Hz).
* Based on code from Michael Smith.
*
* The The bassline synth is a true analog SAW synthesizer with its own audio output
* The waveform is built on digitalpin2 and digitalpin3 mixed with a 10K resistor on each pin.
* Two freerunning phaseaccumulators are used creating a phaseincrement.
* The result is a real SAW-wave that is mixed with the PCM drumpart.
* Hardware for this comes from Roland synthesizers, mainly the Juno 106 and ideas from Duane B, rcarduino.blogspot.com
* Thanks for the inspiration.
*/

#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>

#define SAMPLE_RATE 8000

// Define various ADC prescaler
const unsigned char PS_16 = (1 << ADPS2);
const unsigned char PS_32 = (1 << ADPS2) | (1 << ADPS0);
const unsigned char PS_64 = (1 << ADPS2) | (1 << ADPS1);
const unsigned char PS_128 = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);

#include "sounddata.h"

int ledPin = 13;
int GRNledPin = 10;
int YELledPin = 12;
int speakerPin = 11; // Can be either 3 or 11, two PWM outputs connected to Timer 2
volatile uint16_t sample;
byte lastSample;
int sound_stop;
int BPM;
int BEAT;
int Tick;
int sixteen;
byte pat;
int mute;
int nextmute;
int bassgate;
int button;
int variation;

//float chromatic = 1.059463094359;

//float scale[] = { 1 , 1.059463094359 , 1.122462048308747 , 1.189207115001727 , 1.259921049893469 , 1.334839854168174 , 1.41421356237073 , 1.498307076873759 , 1.58740105196466 , 1.681792830503211 , 1.781797436275713 , 1.8877486253576 };
long scale[] = { 10000 , 10594 , 11224 , 11892 , 12599 , 13348 , 14142 , 14983 , 15874 , 16817 , 17817 , 18877 };

int pattern[] = { 1 , 0 , 4 , 0 , 3 , 0 , 4 , 0 , 1 , 0 , 4 , 0 , 3 , 0 , 4 , 8 , 1 , 0 , 4 , 4 , 1 , 0 , 5 , 4 , 1 , 0 , 5 , 5 , 3 , 0 , 3 , 6 };


int basspattern[] = { 0 , 0 , 2 , 1 , 0 , 0 , 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 3 , 4 };


//SAW Synth
long phacc1;
long phacc2;
int pitch1;
int pitch2;
int osc1;
int osc2;

void stopPlayback()
{
   // Disable playback per-sample interrupt.
   TIMSK1 &= ~_BV(OCIE1A);

   // Disable the per-sample timer completely.
   TCCR1B &= ~_BV(CS10);

   // Disable the PWM timer.
   TCCR2B &= ~_BV(CS10);

   digitalWrite(speakerPin, LOW);

 }



// This is called at 8000 Hz to load the next sample.
ISR(TIMER1_COMPA_vect) {
   if (sample >= sound_stop) {
     sample = sound_stop;  
   }
   else {
       OCR2A = pgm_read_byte(&sounddata_data[sample]);
   }
   
   //The BassSynth
   phacc1 = phacc1 + pitch1;
   phacc2 = phacc2 + pitch2;
   osc1 = (phacc1 ) / 8192;
   osc2 = (phacc2 ) / 4096;
   
   // Output the SAW-wave to D2 and D3 without using digitalwrite because its to slow in a ISR.
   
   if (bassgate==1) {    
   PORTD = PORTD & B11110011 | (osc1 | osc2 & 12); //Outputs the SAW Wave
   }
   
   ++sample;
   ++Tick;
   
   
   
   
}

void startPlayback()
{
   pinMode(speakerPin, OUTPUT);

   // Set up Timer 2 to do pulse width modulation on the speaker
   // pin.

   // Use internal clock (datasheet p.160)
   ASSR &= ~(_BV(EXCLK) | _BV(AS2));

   // Set fast PWM mode  (p.157)
   TCCR2A |= _BV(WGM21) | _BV(WGM20);
   TCCR2B &= ~_BV(WGM22);

   
       // Do non-inverting PWM on pin OC2A (p.155)
       // On the Arduino this is pin 11.
       TCCR2A = (TCCR2A | _BV(COM2A1)) & ~_BV(COM2A0);
       TCCR2A &= ~(_BV(COM2B1) | _BV(COM2B0));
       // No prescaler (p.158)
       TCCR2B = (TCCR2B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);

       // Set initial pulse width to the first sample.
       OCR2A = pgm_read_byte(&sounddata_data[0]);
   





   // Set up Timer 1 to send a sample every interrupt.

   cli();

   // Set CTC mode (Clear Timer on Compare Match) (p.133)
   // Have to set OCR1A *after*, otherwise it gets reset to 0!
   TCCR1B = (TCCR1B & ~_BV(WGM13)) | _BV(WGM12);
   TCCR1A = TCCR1A & ~(_BV(WGM11) | _BV(WGM10));

   // No prescaler (p.134)
   TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);

   // Set the compare register (OCR1A).
   // OCR1A is a 16-bit register, so we have to do this with
   // interrupts disabled to be safe.
   OCR1A = F_CPU / SAMPLE_RATE;    // 16e6 / 8000 = 2000

   // Enable interrupt when TCNT1 == OCR1A (p.136)
   TIMSK1 |= _BV(OCIE1A);

   lastSample = pgm_read_byte(&sounddata_data[sounddata_length-1]);
   sample = 0;
   sei();
   digitalWrite(GRNledPin, LOW);
   digitalWrite(YELledPin, HIGH);
   Tick = 0;
   sixteen = 0;
   BEAT = 0;
}


void setup()
{
   pinMode(ledPin, OUTPUT);
   pinMode(GRNledPin, OUTPUT);
   pinMode(YELledPin, OUTPUT);
   digitalWrite(ledPin, HIGH);
   digitalWrite(GRNledPin, LOW);
   digitalWrite(YELledPin, LOW);
   pinMode(2, OUTPUT);
   pinMode(3, OUTPUT);
   startPlayback();
   
     // set up the ADC
 ADCSRA &= ~PS_128;  // remove bits set by Arduino library
 
 // you can choose a prescaler from above.
 // PS_16, PS_32, PS_64 or PS_128
 ADCSRA |= PS_32;    // set our own prescaler to 32
   
}

void loop()
{
 
   // calculate the tempo
 
   BPM = (analogRead(0) / 10) + 60;
   sixteen = 2000 - (BPM * 7);
   if (Tick >= sixteen) {
     
           
           
           Tick = 0;

//Button 1 511
//Button 2 339
//Button 3 245
//Button 4 203
//Button 5 168

   button = analogRead(1);
   
   if (button > 150 and button <180) {
     variation = 1;
     
   }          
   if (button > 195) {
     nextmute = 8;
     
   }
   if (button > 215) {
     nextmute = 4;
     
   }
   if (button > 260) {
     nextmute = 2;
     
   }
   if (button > 415) {
     nextmute = 1;
     
   }
       
           
           ++BEAT;
           
           // Calculate the next bass sound
           
           pitch1 = int(analogRead(2) * scale[basspattern[BEAT] - 1] / 10000);
           pitch2 = int(analogRead(3) * scale[basspattern[BEAT] - 1] / 10000);
           
           bassgate = 0;
           if (basspattern[BEAT] > 0) {
             bassgate = 1;
           }        
             
           if (BEAT >= 16) {
             BEAT = 0;
             mute = mute ^ nextmute;
             nextmute = 0;
             variation = 0;
             
           }
           
           
           // Calculate the next drumsound
           
           sample = (pattern[BEAT + (variation * 16)] & mute) * 1024;
           sound_stop = sample + 1023;
           
         
             
           // Turn on the BPM flash LED
             
             switch (BEAT) {
             case 0:
             digitalWrite(YELledPin, HIGH);
             break;
             case 4:
             digitalWrite(GRNledPin, HIGH);
             break;
             case 8:
             digitalWrite(GRNledPin, HIGH);
             break;
             case 12:
             digitalWrite(GRNledPin, HIGH);
             break;
           
           

           }
           
           
       
       
           
       }
     
     // Turn off the BPM flash LED  
       
     if (Tick >= 10) {
        digitalWrite(GRNledPin, LOW);
     }  
     if (Tick >= 750) {
        digitalWrite(YELledPin, LOW);  
     }
 


}


And the sounddata.h required. It's not complete because it contains 16384 samples and would not fit here.

Code: [Select]

// sounddata sound made by wav2c
// (wav2c modified to use unsigned samples)

/* const int sounddata_sampleRate=8000; */
const int sounddata_length=1024;

const unsigned char sounddata_data[] PROGMEM = { 128 , 128 ..... };


So I have attached it.

Go Up