Pages: [1]   Go Down
Author Topic: Nano Groovebox  (Read 701 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Sr. Member
****
Karma: 2
Posts: 347
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
 


* Nano Groove.JPG (109.36 KB, 527x466 - viewed 25 times.)

* NANO Schematics.jpg (116.41 KB, 1401x939 - viewed 21 times.)
Logged

Dubai, UAE
Offline Offline
Edison Member
*
Karma: 21
Posts: 1670
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Demo ?

Duane B

rcarduino.blogspot.com
Logged


Offline Offline
Sr. Member
****
Karma: 2
Posts: 347
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Its coming.

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

Offline Offline
Sr. Member
****
Karma: 2
Posts: 347
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Offline Offline
Sr. Member
****
Karma: 2
Posts: 347
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
« Last Edit: July 20, 2013, 04:19:07 am by janost » Logged

Offline Offline
Sr. Member
****
Karma: 2
Posts: 347
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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
« Last Edit: July 19, 2013, 06:34:04 pm by janost » Logged

Offline Offline
Sr. Member
****
Karma: 2
Posts: 347
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Here is the code as it is now.
It has only one single pattern but will compile and perform as in the video.

Code:
/*
 * 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:
// 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.

* sounddata.h (95.49 KB - downloaded 6 times.)
« Last Edit: July 20, 2013, 05:41:05 am by janost » Logged

Pages: [1]   Go Up
Jump to: