Arduino mega dual wavetable mix synth oscillator

early version yet but it takes 2 of several wavetables (some created from manual input, previous code, and a 3d studio max maxscript(!) that adds or subtracts various harmonics and generates the correct formatted values.

the mega can hold a lot more than the 9 wavetables i am using now but i will add more last because the more i add the longer it takes to update the arduino.

I have ordered some encoders and an LCD so i can have pages and not have to keep adding potentiometers for each modulator and so on, the modulation options will be much better (and i only have 4 more analog ins anyways :slight_smile: )

since that video i have also added
modulate wave mix by velocity
modulate wavemix by envelope
fixed the LFO
random wavetables between the 2 selections
random mix per note

it sounds very dynamic and like a real synth oscillator

at higher frequencies it is not great,above C4 or so with some of the wavetables you start to hear some aliasing the sampling frequency is only 16k or so but this will be a bass synth with a SSM2044 filter chip. (also controlled by arduino) for a polyphonic synth i will do one without all the modulation and etc, just play up to 4-6 notes with supersaw or PWM with a envelope gate for each of the notes (external circuitry)

lots of analog inpuits with human changing the values. analogread is slow so i only do one per cycle of the main loop, even with serious knob twiddling i dont hear any lag.

ARC ++;
if (ARC > 11) {ARC = 0;}  //number of analog controllers 

switch(ARC){
    case 0:
     detval = analogRead(detPin);
     //detval = map(detval, 0 ,1023, 0, 1023);
    break;
    case 1:
    attack = analogRead(attackPin);
    attack = map(attack, 0 ,1023, 1023, 5);
    break;
    case 2:
    decay = analogRead (decayPin);
    decay = map(decay, 0 ,1023, 64, 0);
    break;
    case 3:
    wavval = analogRead(wavPin);
    wavval = map(wavval, 0, 1023, 0, tcount); 
    MOD_wavval = wavval;   
    break;
    case 4:
    wav2val = analogRead(wav2Pin);
    wav2val = map(wav2val, 0, 1023, 0, tcount);  
    MOD_wav2val = wav2val;  
    break;
    case 5:
    LFOval = analogRead(LFOPin);
    LFOval = map(LFOval, 0, 1023, 0, 2);
    break;
    case 6:
    LFOspeed = analogRead(LFOspeedPin);
    LFOspeed = map(LFOspeed, 0, 1023, 0, 20000);
    break;
    case 7:
    release = analogRead(releasePin);
    release = map(release, 0, 1023, 55, 0.1);
    break;
    case 8:
    sustain = analogRead(sustainPin);
    sustain = map(sustain, 0, 1023, 0, 1023);
    break;   
    case 9:
    if (modeval == 0){
    mixval = analogRead(mixPin);
    mixval = map(mixval, 0, 1023, 0, 63);
    mixinv = 63 - mixval;
    }
    break;
     case 10:
    modeval = analogRead(modePin);
    modeval = map(modeval, 0, 1023, 0, 4);
    break;
     case 11:
    semival = analogRead(semiPin);
    semival = map(semival, 0, 1023, -25, 24);
    break;
}

it has grown a bit, now the 2009 mega is being used with a 1977 SSM2044 filter IC, and also added power supply for +-15v (and 5v for arduino once its done being programmed) MIDI opto isolator is on a board, Parallel multiplying DACS for the wavetable outputs, opamp buffer etc.

the metal chassis is just a case from an old broken MIDI patch bay ii had. the holes came in handy for the MIDI in and audio out, and high tech "knot" strain relif for the PSU. everything is velcroed down except the PSU which has high voltage, so uses proper standoffs.

eventually when i am happy with the circuit i will build it on to a giant arduino shield. and a proper case.

It's looking gooood.

I used to build synths (http://mikmo.dk/synthstate.html), but have been looking for an Arduino synth project, a little more complex than the novelty beepers out there.

Looking forward to hear more from your project.

thats a nice modular synth there!

update:
hit a bit of a problem, i decided to try the LCD library and see how if it would work, well it affects the sound in a very bad way, even if it is only called once every 12th main loop i guess it has an ISR of its own.

anyways i was thinking of using a smaller arduino (stamp type thing) for MIDI and controls, so i could use that to do the LCD as well.

awesome :smiley:

If the LCD gives you problems, you might also have problems when you get your encoders. They need a lot of attention from the processor.

Maybee the extra Arduino can handle them ?

I hope you ordered optical encoders and not mechanical. The mechanical ones need debouncing which takes even more processor power.

Well today i have set up a duemilanove to talk with it on 12c, and the I2C does not seem to cause any disturbance of the sound at all.

and all it has in its main loop is request from i2c and display as fast as it can :slight_smile:

For now the duelmilanove is just running the LCD and requesting the note number being played. then displaying it along with millis just to stress it out a bit while displaying.

Unfortunately i can not use the duemilanove without either a port expander, or using another mega instead. i need 16 extra pins for the 8 encoders. (yes, optical ones)

mind you 12c is working now, i think a port expander might be the perfect thing.

one annoyance with the arduino ide. it remembers what port and board you are using. if you have 2 windows open for different boards you have to keep switching the settings back and forth.

After messing around with encoders I have decided it is really not worth the bother, i will get a MIDI encoder box/kit and use the MIDI in which already works fine and use it for the controls.

The controls have to be MIDI value compatible anyways.

Today i have got pitch bend working similar to "glide" on a 303 or other bass synth. when a second note is played before the first one has finished the oscillator will glide to that frequency. by mistake i found out i can quite easilly do polyphony, if i can get 4 or more osc working with DACs i will do it.

also the ADSR is working very nicely now. 31.25khz PWM signal is sent through a filter to the reference voltage pin of the DAC0808s the envelopes are very snappy and responsive to changes. (i made a look up table for the AD, and R controls so they have an exponential control curve)

I have ordered 2 seeedstudio megas to do this now, they have even more outputs so i can probably have 3 oscillators (works in code allready)

seeed gave me a coupon from my last order and i got $10 more certificate for mentioning the DSO NANO scope on a blog :slight_smile: so i orderd more crap to make up for it ::slight_smile:

You should check this:

http://www.sureelectronics.net/goods.php?id=884

Wow i was looking all over the place for something like that, thanks!

is it just debounce, or does it have a static "last change" output?

I don't quite know.

It's a little hard from the website to tell exactly what it is, but you could ask them.

I would be interested in knowing as well, becaus if it really implements "change since last inquery" style reading it can solve a lot of problems related to using encoders with Arduino. And at 15 $ it would be a steal :slight_smile:

I have ordered a couple so I am the guinea pig :slight_smile:
Will also let you know how the service is.

I if it does work like that, i could use a simple digital multiplexer switch to select an encoder, then read it, then go to the next one. rather than using up a ton of pins.

ok since trying to send instructions to the wavetable arduino over i2c i had to give up on that, when recieving a lot of data the interrupts cause the sound to be horrible and sometimes even miss the end of an envelope and cause stuck notes, since i have allready got MIDI in working, the second arduino is now just a MIDI controller which sends the controls to the wavetable synth one (it does a lot more now, with a seperate envelope for accent, i can get that bass sound they use in drum n bass)

Recieving MIDI seems to use even less resources than directly attached analog potentiometers.

here is a video of it with the filter attached, but before the accent and MIDI in controlls were added.Arduino wavetable oscillator + SSM2044 Bassline synth. - YouTube

It looks a lot bigger and more complicated than it actually is, i hope to make the entire thing on a "shield" type board. (2 dacs, pwm filters, VC filter, MIDI in, Audio out) and the program would have the option of using 6 or 16 potentiometers (arduino or MEGA) or only MIDI in if you have a MIDI controller or just want to run it from a sequencer.

"You should check this:

http://www.sureelectronics.net/goods.php?id=884"

Well i recieved them. the encoders are connectrd to a PIC which gives nice clean output. there are 2 modes, the mode i tested breifly tonight simply has a pin which is always high or low depending on the direction, and another one which pulses once per click. it worked fine with just a little bit of code (for one) and 2 data pins used. I want to multiplex a bunch of them with a , i am not exactly sure how to go about that yet. (probably have to put the multiplexer counter and pin reads on a timer ISR)

Timing wise i tested it with various delays, and anything under 5 microsecond loop worked fine and caught all knob twiddles in either direction.

the knobs are nice, (but sort of an ugly gray color) they actually have a collet which tightens against the encoder shaft with a nut inside the knob (under the top cap) and the feel is good. there is also a switch on pressing the encoder. (normally high)

They are a bit big though i would have liked the whole thing to be on 1.5" spacing rather than 2" and with smaller knobs

The board is nice except they did not clean of the solder flux, or some whitish stuff, it looks like it was hand soldered!

heres a video of an updated version. i like it enough i will build it in to a modular synth oscillator now.

arduino is controlling and modulating everything. the wavetables are in an external flashROM now

arduino (do stuff) > address ROM and output envelopes etc,
ROM output goes to dual DAC which is also controlled by arduinio.
arduino also controls the filter which would be a seperate module, the SSM filter is just as an example for now, it can work with any voltage controlled filter.

current code is at http://codeviewer.org/view/code:d6d if you care to view it.

Hi,
I really love your project.
Is it possible to see your code for the tone/frequency generation?
I'm working on a similar project since a few days.
I've got one oscillator working now and have a "wavetable" with a sin and saw wave.

What i don't get however is how you change the "amplitude" of your wave.

At the moment i can only play one frequency continuously (or silence it by setting the frequency to 0)

Do you do the mixing and ADSR purely by modifying the DAC?

My progress so far

Regards,
Patrick

Thanks!

Because I am using external DACs, I can send PWM from the arduino through a filter to the DACs vref input to control output and envelope levels,

however, if you are using PWM output you can control the level by simply multiplying an 8 bit envelope or volume amount with the "output value", then divide by 2 with >>1 to get a value for the pwm.

then you can generate an envelope in your main loop and have the ISR pick up whatever the current envelope value is.

For controls I originally used some pots on a protoboard, then 16 pots connected to "mux shield" and the current version (on the other thread "giant arduino shield") uses a similar circuit to mux shield, but picks up 32 pots and 34 switches, including 2 grayhill BCD coded mechanical switches, they have 16 positions and conveniently output binary on 4 lines.

the frequency generation uses some of the code from here.
http://adrianfreed.com/content/arduino-sketch-high-frequency-precision-sine-wave-tone-sound-synthesis

you can see what i have going on in the ISR here.

SIGNAL(PWM_INTERRUPT)  //The timer interrupt. 
{

  PORTB = PORTB & 11111000; 
  PORTB = PORTB + 2; //Switch to chip 2 DAC 1  
  //PORTC = WT1TOP; //send wavetable address to ROM
  PORTC = WT1ModOffsetCOUNT; //send wavetable address to ROM
  PORTA = DAC2Aout;  
  PORTB = PORTB + 1;  //Switch to chip 2 DAC 2
  //PORTC = WT1TOP;  //send address to ROM
  PORTA = DAC2Bout;     
  PORTB = PORTB + 1; //Switch to chip 1 DAC 1 
  //PORTC = WT0TOP;  //send address to ROM
  PORTC = WT0ModOffsetCOUNT;  //send address to ROM
  PORTA = DAC1Aout;
  PORTB = PORTB + 1;  //Switch to chip 1 DAC 2
  //PORTC = WT0TOP; //send address to ROM
  PORTA = DAC1Bout; 



  synctest = ((o1.phase>>16));
  DAC1Aout = (synctest ^ ByteModArray[0])- ByteModArray[4];  
  DAC1Bout = ((o1.phase2>>16));
  DAC2Aout = (((o1.phase3>>16))^ByteModArray[1])- ByteModArray[5];
  DAC2Bout = ((o1.phase4>>16));     


  sync = syncswitch;  //pulse once at the start of each cycle 
  syncswitch = (synctest>>7);

  o1.phase = o1.phase + (uint32_t)o1.phase_increment - WT0DETmX;   //add detune to master when sync is off.
  o1.phase2 = o1.phase2 + (uint32_t)o1.phase2_increment - WT0DETm;
  o1.phase3 = o1.phase3 + (uint32_t)o1.phase3_increment - WT1DETm;
  o1.phase4 = o1.phase4 + (uint32_t)o1.phase4_increment - WT1DETm;//}


  bitWrite(PORTB,4,syncswitch);  //external oscillator sync

  if (sync == 1 && (sync != syncswitch)) {   //sync the 3 osc to master when sync is on.
    WT0ModOffsetCOUNT = WT0ModOffsetAMT;      
    WT1ModOffsetCOUNT = WT1ModOffsetAMT;
    if (SyncCont > 0){  
      o1.phase2 = o1.phase;
      o1.phase4 = o1.phase; //for sync 
      o1.phase3 = o1.phase;     
    }       
  } 
}

and in my case the envelope is just output to pwm on the main loop, then controls the DAC chips VREF which controls the output level.

A good trick to get smooth envelopes and other controllers is to use long int for the actual calculation of the attack, decay etc, then just divide it by the appropriate amount with >>N

Hi,
thanks for your fast reply!
This is very helpful.
I can dissect this and try more methods.
Have to see if i can fit it all in the Duemilanove.
Would be great if there was some way to determine "processor-load".
Sometimes I don't really see if some optimisations are working or if doing something one way or the other is better.

By the way: I'm also experimenting in writing an "editor" in Processing (or maybe i'll switch it to OpenFrameworks).
With a switch on the Arduino I switch to "live-mode" or "edit-mode".
Now I can directly get the waves from the eeprom-wavetable and visualise them. Soon I will be able to edit them via mouse or math and dump them back.
Maybe also interesting to create some import/export functions so you can exchange waveforms with others or convert them to and from (wav) audio files.

Thanks again for sharing your code!

Patrick

for processor load i have output a 1 or 0 with direct port write to a pin and look at the frequency with an oscilloscope. if you put it at the start and end of the ISR, you can see what percentage of the time is being used by the ISR by looking at the pulse width.

If you put it in the main loop, you can see how fast the loop is running by looking at the frequency. (and you can also put it before and after questionable parts of the code to see how much of the overall cycle they take)

If you do not have an oscilloscope then you can write code which measures the time and sends it out on the serial port. make sure you set the serial port fast so that it does not affect the readings too much!
or you could use a soundcard oscilloscope program (but probably wont be able to watch the ISR with it, unless you have a high sample rate card)

On my current version the ISR uses up about 65% of the processor time and the main loop cycles at around 1KHZ

Using pin timing as load measurement sounds like a great idea.
Unfortunately I indeed do not have a scope yet, except the soundcard based WINscope.
I think my (creative labs) soundcard automatically compensates for DC offset and so is not so useful for this kind of stuff.
Since my project uses midi over the arduino USB port I cannot use that for debugging either.

Is it possible to use NewSoftwareSerial easily for either the midi or the debugging?

I'm trying to rewrite the Adrian Freed code in a class.
But I cannot get the oscillator to work well that way.
It seems like my object is newly recreated in my ISR and so some important variables are out of scope.
I tried setting some members to either volatile or static, but this doesn't help either.
And i also cannot instantiate the entire class as volatile(?)

Any ideas?

Patrick