DIY looper/sampler chronicles

Started by cloudscapes, March 15, 2012, 12:39:15 AM

Previous topic - Next topic

free electron

Quote from: cloudscapes on March 18, 2012, 08:40:16 AM
Yeah I have a couple of those MX2's. Unfortunatelly they'e half the speed of the one I'm currently using, which doesn't have an I2S protocol.  :( I don't have anything to program them with anyway. The one I'm using is a stamp with a USB bootloader already integrated. I'm currently using it because I didn't have to buy an expensive programmer.
Thanks, though!
You can program the PIC32 with Pickit2, which is quite cheap, you can build one for yourself, too. I did that recently because i needed a small and quick eeprom burner/logic analyzer.
Quote
Ran into another snag. My compiler's on-chip ADC libraries (for pot input) are really slow. I mean really slow. A single 10bit conversion eats up almost half a millisecond. Even the 10bit ADCs on AVRs are 100 times faster (no exaggeration). According to the mikroelectroncia forums, I will have to write my own library for quickness.  My options for pot input are:

1. An external SPI 4-channel ADC. I'd lose a lot of clock cycles.
2. Writing my own custom ADC library in mikroC. Reading about that now.
3. Switch compilers entirely. Wouldnt be the first time I was frusterated by a design decision in this one, but that would mean I wouldnt be able to use these DIP boards anymore.

I am afraid You will have to choose the "2" anyway if you plan to do some serious things and spend time debugging your code, not the poorly coded libraries.
If you are thinking about switching the compiler, maybe it would be good to switch the hardware, too?
http://www.st.com/internet/evalboard/product/252419.jsp
Take a look at the specs and then at the price - probably cheaper than the mikroc pic32 board.

cloudscapes

Quote from: free electron on March 18, 2012, 05:34:15 PM
You can program the PIC32 with Pickit2, which is quite cheap, you can build one for yourself, too. I did that recently because i needed a small and quick eeprom burner/logic analyzer.

I am afraid You will have to choose the "2" anyway if you plan to do some serious things and spend time debugging your code, not the poorly coded libraries.
If you are thinking about switching the compiler, maybe it would be good to switch the hardware, too?
http://www.st.com/internet/evalboard/product/252419.jsp
Take a look at the specs and then at the price - probably cheaper than the mikroc pic32 board.

Pretty cheap! I'm not ready to jump ship to an entirely different architecture, though. ;) Maybe when I've exhausted all options.
I also don't like boards when they have all the peripherals, to be honest. I just wanted the IC and its support circuitry. Having no peripherals helps reduce headaches when you're debugging. Takes them out of the equation.

I'll see what I can do about writing my own custom ADC code before deciding to completely change what I'm doing. I hope you understand. I'm not against starting over. I just want to make sure it's my only option if I do.
~~~~~~~~~~~~~~~~~~~~~~
{DIY blog}
{www.dronecloud.org}

g_u_e_s_t

there is also the maple for the stm32 series
if youre thinking of jumping ship
http://leaflabs.com/devices/maple/

although if youre familiar with the avrs
you could probably do what you want with one of those

cloudscapes

Quote from: g_u_e_s_t on March 18, 2012, 08:42:35 PM
although if youre familiar with the avrs
you could probably do what you want with one of those

the avrs are way too slow for what I've had in mind. I've tried. ;) I hit their limit (have even been overclocking them), which is why I'm learning a new thing.
~~~~~~~~~~~~~~~~~~~~~~
{DIY blog}
{www.dronecloud.org}

wavley

Quote from: cloudscapes on March 16, 2012, 09:21:49 PM
I still haven't recieved my new 16bit ADCs yet, but I may still work on this on the weekend. maybe try and get a timer running on the PIC32 for a variable samplerate. at the moment I set the samplerate just with a wait state. maybe try and get a (samplerate*100) clock output going at the same time, which is what those filter chips described earlier need to clean up the sound.

Quote from: wavley on March 16, 2012, 09:19:51 AM
I think that we are on the same page as far as loopers go, as a matter of fact last night I did a looping based song for a compilation of music recorded on cell phones.

My favorites are:

Boomerang+ (the III is nicer, but I have a wife, therefore I can no longer buy expensive things) because it is just laid out so well for live looping.
EHX 16 Second Delay because of the sweet glitch stuff you can do with it
Digitech RDS-900, my first looper, I love it most likely for all the same reasons you love your PDS

I actually have the rang III ! I'll probably sell it though. it's mostly great, but a couple things keep me from using it. 1. I don't like the interface. I like to have buttons for each feature if possible, not have to assign buttons with the dial. and 2. you can't change the speed of the loop except by half. it has some other great features going for it, though! the free play mode, for instance!

I have never had the pleasure of using the 16 sec delay! one day, maybe, one day.....

Yeah, that was my complaint about it too, when I first saw it I said "Why are there less footswitches and no roller for the volume, that's the whole reason the rang and rang+ are awesome" Looks like they are trying to fix that with the sidecar, maybe I'll wait for the 4 to come out.  It seems strange that you can't change the speed by anything other than a half, the older ones have several different divisions, my only complaint about that is that you have to set the interval in the firmware by holding down a button while powering up, not very good for live.
New and exciting innovations in current technology!

Bone is in the fingers.

EccoHollow Art & Sound

eccohollow.bandcamp.com

cloudscapes

I'm checking out MPLAB now. See what a different compiler is without necessarily switching hardware. If I can get it to compile for my mikroelectronica board when I might switch.

The interface isn't as sexy, but the library syntax looks a lot better to my eyes. Example, in MPLAB, configuring the ADC might look like this:


#define PARAM1 ADC_MODULE_ON | ADC_FORMAT_INTG | ADC_CLK_AUTO | ADC_AUTO_SAMPLING_ON
#define PARAM2 ADC_VREF_AVDD_AVSS | ADC_OFFSET_CAL_DISABLE | ADC_SCAN_OFF | ADC_SAMPLES_PER_INT_2 | ADC_ALT_BUF_ON | ADC_ALT_INPUT_ON


Since I already have general knowledge of microcontrollers, it's pretty self-explanatory. It's english and uses words! But on mikroC, the same thing might look like this:

AD1PCFG = 0xFF7F
AD1CON1 = 0x0004
AD1CHS= 0x00070000
AD1CSSL = 0
AD1CON3 = 0x0002
AD1CON2 = 0


Huh? Wha? AD1CHSNCYTBDS = 0x04723HYEN6 indeed!

So, what to do:

1. See if I can even get  MPLAB and this ICD2 to work on the mikroelectronica board I use.
2. Port my code over.
3. Try ADCs again.
~~~~~~~~~~~~~~~~~~~~~~
{DIY blog}
{www.dronecloud.org}

free electron

Take a look here, this is the section of the reference manual describing the ADC module:
http://ww1.microchip.com/downloads/en/DeviceDoc/61104E.pdf
page 14, what you'll find there is a step by step explanation about configuring the ADC.
Later on you'll find even some code examples in C.
If something doesn't work the way it supposed to or i am encountering some weird problems the first thing is to read through the reference manual. Microchip has nicely written ref manuals, although i found a few unexpected surprises, too. That's why the second thing i'm looking into is the chip errata.

There should be no problem with getting the MikroE board working with the ICD2. You will be using standard PGC+PDG+MCLR lines instead of the bootloader.

cloudscapes

Quote from: free electron on March 20, 2012, 04:49:45 AM
Take a look here, this is the section of the reference manual describing the ADC module:
http://ww1.microchip.com/downloads/en/DeviceDoc/61104E.pdf
page 14, what you'll find there is a step by step explanation about configuring the ADC.
Later on you'll find even some code examples in C.
If something doesn't work the way it supposed to or i am encountering some weird problems the first thing is to read through the reference manual. Microchip has nicely written ref manuals, although i found a few unexpected surprises, too. That's why the second thing i'm looking into is the chip errata.

There should be no problem with getting the MikroE board working with the ICD2. You will be using standard PGC+PDG+MCLR lines instead of the bootloader. Allow me to mini-rant. ;)

Yeah, I saw something like that in the PIC32 ref manual. Didn't see that one exactly, though.

I think it's the bit setting I have most of a problem with. And HEX. Like the following:

QuoteAD1CHS= 0x00070000;
// Connect RB7/AN7 as CH0 input// in this example RB7/AN7 is the input

So for some reason, the number seventy-thousand means I'm rerouting  RB7 as channel0 of the ADC. Fair enough. According to the datasheet, if I wanted to route it to channel15, it wouldn't be seventy thousand, it would be 987504. ....... 987504! Really.

Now, I *know* that these values actually can be converted into binary, where say the 16th, 17th, 18th and 19th bit flag which channel I'm rerouting RB7 to. Nevermind that in "AD1CHS", there's absolutely no indication of that. If I was going on intuition, I would have guessed it was RB1, or channel 1 or something, because of the "1" in there. You would also think that the 70000 somehow meant AN7 in the example, but no. The 70000 just meant it was the decimal version of a string of bits to be flagged.

That's my problem. It's completely counter-intuitive. Random bit flags pertaining to the ADC "1" (which somehow means RB7/AN7) are lumped together in a seemingly arbitrary number such as 70000 or 987504 (the difference between channel0 and channel15). I *know* it's not arbitrary, but all the examples are written that way. My mini-rant is that it makes learning extremely difficult. I can copy and paste the example code okay, but when it came time to swap between channel 0 and 15, it took me 30 minutes of reading to understand this concept, and I couldn't believe the counter-intuitiveness of it when I did. I was boggle-eyed.  :icon_biggrin:  And then I was trying to change something else and I gave up after hour. Something else that, had everything been written in plain english, would have taken 5 seconds.

Maybe I'm mistaken somewhere here, because I haven't gotten stuff to work yet. But that's just my point. I feel like the documentation and examples assume you're already a pro in this environment. I am not. Some people like to dive into the hard stuff when learning something new, liek a new programming language, IDE or type of hardware. Not me. I've always been a "hold my hand and walk me through the simplest examples imaginable, pretend I'm 7 years old" guy. Bascom/AVR had exactly that, and after two days I was already coding some serious little routines! mikroC/PIC32 doesn't have that, and I'm frustrated.

end rant.  ;)

I had a look at MPLAB last night! The mikroelectronica PIC32 board is programmed via bootloader, so I've been using the bootloader app to flash MPLAB's program to it with some degree of success. I loaded up a complex led blinker program, and the two debug leds on the mikroe board blinked. I loaded up an ultra-simple led blinker program, and I haven't gotten it to work. I'll try and figure it out tonight. Right now I've got to go to work. At least I know I don't need a programmer with these boards, for the timer being!

I'll have a thorough read of your PDF later today. Thanks!
~~~~~~~~~~~~~~~~~~~~~~
{DIY blog}
{www.dronecloud.org}

Galego

Quote from: cloudscapes on March 19, 2012, 06:58:41 PM
I'm checking out MPLAB now. See what a different compiler is without necessarily switching hardware. If I can get it to compile for my mikroelectronica board when I might switch.

The interface isn't as sexy, but the library syntax looks a lot better to my eyes. Example, in MPLAB, configuring the ADC might look like this:


#define PARAM1 ADC_MODULE_ON | ADC_FORMAT_INTG | ADC_CLK_AUTO | ADC_AUTO_SAMPLING_ON
#define PARAM2 ADC_VREF_AVDD_AVSS | ADC_OFFSET_CAL_DISABLE | ADC_SCAN_OFF | ADC_SAMPLES_PER_INT_2 | ADC_ALT_BUF_ON | ADC_ALT_INPUT_ON


Since I already have general knowledge of microcontrollers, it's pretty self-explanatory. It's english and uses words! But on mikroC, the same thing might look like this:

AD1PCFG = 0xFF7F
AD1CON1 = 0x0004
AD1CHS= 0x00070000
AD1CSSL = 0
AD1CON3 = 0x0002
AD1CON2 = 0


Huh? Wha? AD1CHSNCYTBDS = 0x04723HYEN6 indeed!

So, what to do:

1. See if I can even get  MPLAB and this ICD2 to work on the mikroelectronica board I use.
2. Port my code over.
3. Try ADCs again.

Actually, I prefer the mikroC option. At least I can easily know what I'm setting on or off, with english words, sometimes it's not so clear what you are setting, if the words aren't the same as the ones on the PIC datasheet.

cloudscapes

#29
I think I've figured out at least one thing. Copied straight from the manual:

Quote
AD1CHS = 0x00020000;           // Connect RB2/AN2 as CH0 input
                               // in this example RB2/AN2 is the input

And that thing is.... that in the ADC reference manual, the comment doesn't match the code. Reference manual says bits 16-19 define which pin is CH0. 0x00020000 converted to binary is:

00000000 00000000 01001110 00100000

With bits 16-19 hilighted. 0000, according to the reference manual means CH0 would be connected to RB0/AN0. Yet the comment says it's RB2/AN2.

......

Using RB2/AN2 would make bit 17 into 1, making it 151072, not 20000....

...I mean, unless I'm mistaken.  :'( Ugh. It's past midnight. I need sleep.
~~~~~~~~~~~~~~~~~~~~~~
{DIY blog}
{www.dronecloud.org}

free electron

You have converted a decimal 20000 to bin, while 0x........ is hexadecimal.

Hal

How does memory access work on those RAM chips? Do you use any of the built in addressing features, or just your own library? That is, after setting everything up, can you create a circular buffer, and address each sample, or are you just able to sequentially fill up the memory, then read it out?

I've been futzing with a DsPIC based effects platform, and was looking for advice on expanding the memory. It would be awesome to have enough memory available to have a looper as well!

I'm still using the built in ADC and DAC, but I think I've settled on the TI AIC23 for the future, when I make a board. It's pretty well supported, and seems easy enough to use. There is just a lot to spec out on the project (and I have been very busy), so I'm having trouble getting rolling, but I can let you know how it works out if I ever get that far.

cloudscapes

Quote from: Hal on March 21, 2012, 07:13:36 PM
How does memory access work on those RAM chips? Do you use any of the built in addressing features, or just your own library? That is, after setting everything up, can you create a circular buffer, and address each sample, or are you just able to sequentially fill up the memory, then read it out?

I've been futzing with a DsPIC based effects platform, and was looking for advice on expanding the memory. It would be awesome to have enough memory available to have a looper as well!

I'm still using the built in ADC and DAC, but I think I've settled on the TI AIC23 for the future, when I make a board. It's pretty well supported, and seems easy enough to use. There is just a lot to spec out on the project (and I have been very busy), so I'm having trouble getting rolling, but I can let you know how it works out if I ever get that far.

here's my write and read code

Quote
void RAM_write(long int valueADDRESS, unsigned int valueRAM) {
     char RAM_WRITE=2;
     DisableInterrupts();
     Chip_Select_RAM = 0;
     SPI3_Write(RAM_WRITE);               //write command
     SPI3_Write(valueADDRESS >> 16);            //address msb
     SPI3_Write(valueADDRESS >> 8);            //address middle
     SPI3_Write(valueADDRESS);            //address lsb
     SPI3_Write(valueRAM >> 8);                //value msb
     SPI3_Write(valueRAM);                //value lsb
     Chip_Select_RAM = 1;
     EnableInterrupts();
}

void RAM_read(long int valueADDRESS) {
     char RAM_READ=3;
     DisableInterrupts();
     Chip_Select_RAM = 0;
     SPI3_Write(RAM_READ);               //write command
     SPI3_Write(valueADDRESS >> 16);            //address msb
     SPI3_Write(valueADDRESS >> 8);            //address middle
     SPI3_Write(valueADDRESS);            //address lsb
     ram_value = SPI3_Read(buffer);
     ram_value = ram_value << 8;
     ram_value |= SPI3_Read(buffer);
     ram_value = ram_value << 1;
     Chip_Select_RAM = 1;
     EnableInterrupts();
}

1. 8 bit command, read or write, or a veriety of options.
2. 24bit address word. my SPI is configured in 8bit mode so I do it in 3 8bit chunks
3. input data to be saved. and here's the answer to one of your questions. once you've got the command byte in, and the address, you can enter a single byte, two bytes (what I'm doing) or write a long string to your heart's content. as long as chip-select is held low, you can keep entering or receiving bytes sequentially!

I'm not doing anything with the ram yet aside from a simple test to make sure it works. I use the automatic "sequential" to be able to read and write two bytes at a time, since I'm doing 16bit sound, but that's all I'll be doing. the features I have in mind require totally random access, so long sequential reads/writes are not needed. though they can be done!

I'm just gonna brute force it. every 44000th of a second, I'll just call that read or right routine over and over again, for whatever 16bit word I need at that given time. that's the extent of my library.
~~~~~~~~~~~~~~~~~~~~~~
{DIY blog}
{www.dronecloud.org}

cloudscapes

#33
Quote from: free electron on March 21, 2012, 12:58:08 AM
You have converted a decimal 20000 to bin, while 0x........ is hexadecimal.

thanks! I've been reading this wrong.

I'm still reading the ADC ref manual. I haven't found the part where they explain how AD1CHS pertains to CH0 if 0x00020000 is RB2/AN2. CH1 is AD2CHS maybe, and CH15 is AD16CHS. I'll keep reading...

edit: scratch that, AN2CHS doesn't even exist. somewhere in "AD1CHS = 0x00020000" is a reference to CH0, and it's not the 0x00020000.
~~~~~~~~~~~~~~~~~~~~~~
{DIY blog}
{www.dronecloud.org}

cloudscapes

I don't have pots working yet and thus no realtime control, but this weekend I'll try and hard code a loop record then playback and post some sound samples at various samplerates.

the new 16bit ADC has a bit of background hiss that the 12bit ADC didn't have. might be because of the breadboard, might not. the hiss can't be heard at all when there's actual playback, but it's there when silence. I think I'll just gate the output with a vactrol when there's silence. I did exactly that on a ringmod I built years ago to get rid of carrier bleed.
~~~~~~~~~~~~~~~~~~~~~~
{DIY blog}
{www.dronecloud.org}

cloudscapes

got the ADC workingnow. I'm both initializing and sampling every 10ms and it does both very quickly (a couple us, less that the length of a sample).  :icon_biggrin: it took a lot of searching and reading on other forums how people do it. with examples. just instructions don't do it for me. I need to see examples to be able to parse what I'm reading..

http://www.microchip.com/forums/m593633-print.aspx

so I now have one pot that controls the samplerate in realtime (updated every 10ms) between 66KHz and 1KHz. :icon_biggrin:
~~~~~~~~~~~~~~~~~~~~~~
{DIY blog}
{www.dronecloud.org}

free electron

Quote from: cloudscapes on March 21, 2012, 08:37:25 PM
I'm still reading the ADC ref manual. I haven't found the part where they explain how AD1CHS pertains to CH0 if 0x00020000 is RB2/AN2. CH1 is AD2CHS maybe, and CH15 is AD16CHS. I'll keep reading...
edit: scratch that, AN2CHS doesn't even exist. somewhere in "AD1CHS = 0x00020000" is a reference to CH0, and it's not the 0x00020000.
Page 3, block diagram of the ADC module.
The ADC channels are described as AN0...AN15, not CH0...CH15. This is why you won't find the reference.
0x00020000 has "1" on bit 17, that means the bitfield CH0SA (bit 19-16) has a value 0b0010 which is decimal 2  and corresponds the AN2/RB2 pin on the chip.

cloudscapes

Quote from: free electron on March 22, 2012, 03:46:17 AM
Page 3, block diagram of the ADC module.
The ADC channels are described as AN0...AN15, not CH0...CH15. This is why you won't find the reference.
0x00020000 has "1" on bit 17, that means the bitfield CH0SA (bit 19-16) has a value 0b0010 which is decimal 2  and corresponds the AN2/RB2 pin on the chip.

I got it working last night, thanks!

Basically, here's how I got confused. I was going through each ADC init line by line, making sure I could understand the steps. The manual tells me:

AD1CHS = 0x00020000

..assigns RB2 to CH0. Alright, I do it, and it works on RB2! Then here's where the "fun" starts. Since I want to use at least 3 ADC channels, I look for the part where it explains where I assign another channel to another pin. I was totally expecting to have an init that looks something like this:

AD1CHS = 0x00020000
AD2CHS = 0x00060000
AD3CHS = 0x000B0000

AD1PCFG = 0xFFFB;
AD2PCFG = 0xFFFC;
AD3PCFG = 0xFFFD;

..you know? And so on. I had it in my mind that the only way to configure an ADC on this chip, on any chip, was to set up each channel or each pin at the start of the program. That's how I did it with AVRs and Bascom. And I was frusterated because no where in my manuals did I find an example where multiple channels were being used. They were simple, single-channel examples.

It took searching on other forums some real-life code examples to finally understand that AD1PCFG (and other registers) must be set differently before a channel is sampled. Each time. It took a different way of seeing things.
~~~~~~~~~~~~~~~~~~~~~~
{DIY blog}
{www.dronecloud.org}

cloudscapes

Controls (so far)

Here are the controls and features I will be trying to implement. The how's are mostly already clear.


Knob –Speed (PIC32)
Knob –Forward/freeze/reverse (555)
Knob –Freeze granularity (555)
Knob –Start point (PIC32)
Knob –End point (PIC32)
Knob –Drift (PIC32)

Knob –Filter cutoff (analog)
Knob –Filter resonance (analog)
Toggle –Filter post/pre feedback (analog)

Knob –Modulation speed (AVR)
Knob –Modulation depth (AVR)

Knob –Feedback
Knob –Input gain
Knob –Wet
Knob -Dry

Button –Record/play
Button –Bypass/erase


The stuff in brackets is how its gonna be done. PIC32 means the pot will be directly connected to the micro and will be handled in code. Analog means just that, and AVR means I have a little 8pin modulation AVR I like to use for these kinds of thing. It'll PWM a push and pull into the Speed pot's ADC.

The Forward/freeze/reverse and freeze granularity is what I think I'm most excited about. Here's how:

Forward / freeze / reverse

Nearly all (or all) loopers I know of manage reverse in one way. A button, or a toggle switch. Off it goes forward, on it goes in reverse. I want something a bit more special than that. I want a knob that has totally variable states between:

-   full speed forward
-   slower speed forward (no pitch change)
-   very slow forward (still no pitch change)
-   freeze
-   very slow reverse  (still no pitch change)
-   slow speed reverse (etc..)
-   full speed reverse


Here's how I'm gonna do it. The PIC32 will have a digital input pin. Low pin is forward, high pin is backwards. Then I attach a 555 with controllable speed and PWM! This image is a representation of the PWM signal to be generated by the 555 and fed into the PIC32 forward/reverse toggle pin.



Each segment is a short period of time. For the sake of the argument, lets say 5 milliseconds. On full forward, there are no pulses, just a low signal. The PIC32 interprets that as forward playback.

Turn the pot a bit and the 555 generates a little duty cycle. In this example, 25%. The PIC32 interprets that as mostly forward playback, with 25% rewind. Since this is done very quickly, every couple of milliseconds, our ears and brains will interpret that as "slower speed", and it will preserve the pitch.

If the pot is in the center, then the forward and reverse duty cycle are the same, and a "freeze" is achieved. Turn it even more, and the reverse state is longer than the forward state and so you start to go back in time. An additional potentiometer can be

This is not a "perfect" method of pitch-preserving time stretching. That would probably involve blending microsamples and advanced DSP stuff that I'm no where near ready to try. This is a hack, but it works! I've tried it on an AVR sampler a year and a half ago. This time, it'll be fine-tuned and hi-fi.
~~~~~~~~~~~~~~~~~~~~~~
{DIY blog}
{www.dronecloud.org}

free electron

Quote from: cloudscapes on March 22, 2012, 09:03:02 PM
I had it in my mind that the only way to configure an ADC on this chip, on any chip, was to set up each channel or each pin at the start of the program. That's how I did it with AVRs and Bascom. And I was frusterated because no where in my manuals did I find an example where multiple channels were being used. They were simple, single-channel examples.
This is an example of why bascom can be good to start to play with MCUs, it's easy a delivers a quick results. On the other hand it leaves bad habits, because many functions are hard coded into the compiler and you don't really see what are they doing inside the chip.

Since you got the ADC working... the PIC32, DSPIC (this is where i'm using it) and most modern >8bit MCUs have a few nice features to optimize the ADC conversion, especially non time critical parameters like DC pot values.
Let's say you have 3 or more pot to read. You can configure one DMA channel to work together with the ADC so they will do all the conversions, store them in a DMA buffer and when all pots are read, the interrupt will be triggered, where you can copy the values from the buffer to your variables. Basically instead of doing "n" single conversions with manually setting the channel, starting, waiting to be completed etc, you let the ADC collect the data, call the DMA module when a sequence is finished, pass it the "n" words of data, then the DMA will call your main program "hey! here are your pot values". All this happens in background without sacrificing the cycles you can use to process the audio samples. The only thing your program need to do is to copy the data from the DMA buffer in the interrupt subroutine.
Might be very useful if you plan to do multiple reads/writes to the RAM via SPI.
With all the things PIC32 has on board i'm pretty sure you could code the Speed/Forward/Freeze/Reverse (555) and the LFO (AVR) into the chip. What the 555 does looks exactly like a DDS with phase distortion implemented and a square wave output. You need one timer for that, it could also do the LFO.