Go Down

Topic: Arduino DUE audio from sd card and DSP.. (Read 860 times) previous topic - next topic

Mubase

Hi arduino people.  :P
I have an idea for a project I am doing involving sound art, (its a sound art project.)
I have an Arduino DUE and a DIGIX board and can play SD card wav files quite easily.
Could anyone tell me if there are any good examples or tutorials of doing real time DSP on wav files. By DSP I mean audio effects such as reverbs, delays, time stretching and pitch bending.. Please could someone help me with this?

Thanks.
Steve. 
Smooberry Dooberry.

technix

I think on the Arduino the performance can be a bit too tight for audio purposes. I would use an Raspberry Pi for this audio processing functionality.

Grumpy_Mike


I think on the Arduino the performance can be a bit too tight for audio purposes. I would use an Raspberry Pi for this audio processing functionality.

No it is the raspberry Pi that is poor at audio due to Linux and the premptive multitasking.

You can do reverb and pitch shifting and even real time backwards audio even on a UNO. I demonstrated that at the recent Rome Maker Faire.

The due should be better at this due to the higher speed and better resoloution.

On my iPad at the moment so I can't post any links but start with the audio examples in the IDE and do a bit of googling. Watch out you do not load the DAC output too much as they are apt to blow.

Mubase

Hi Mike. Thanks for the advice. :)
Looking aroud I found some code from DuaneB for a groovebox. :)
Using the example that plays 2 wav files in succession I managed to get a very basic delay working using a ring buffer.

Code: [Select]
// Simple sampler which alternates a kick and a snare

#include <SdFat.h>
#include <sampler.h>
#include <DueTimer.h>

SdFat sd;
Sampler sampler;
int to=0;
int buf[2048];
int buf2[2048];
int buf3[2048];
int i,j,k;
int sum=0;
int iw,iw1,bdac0,bdac1,bb,bb2,bb3;
void setup()
{

  Timer0.attachInterrupt(loop1).setFrequency(48000).start();
  analogWrite(DAC1,0);
  analogWrite(DAC0,0);

    sd.begin(4, SPI_FULL_SPEED);
    sampler.init();
}

void loop()
{
   if( to>100000)
   {
     to=0;
// POUM
     sampler.load("hello.wav");
     sampler.splay(100,120);
   }
   
   if(to==50000)
   {
// TCHACK
     sampler.load("snare.wav");
     sampler.splay(100,120);
   }
   
   sampler.buffill();
   
   delayMicroseconds(17);
   
   to++;

 
}

void loop1()
{

// 2048 is the 0 value of audio out.

   int16_t ulOutput=2048;
// Add in echo effect (if enabled) from circular buffer.
  // This takes place before audio level clipping so that
  // any clipping distortion won't be repeated in echo.
 
   sampler.next();
   
   ulOutput += sampler.output();

   buf[i]=ulOutput;
   i++;
   if (i>2048)
   i=0;
 
   
bb=buf[i];
iw=512-bb;
iw=iw  * ulOutput / 2048;
buf2[j]=iw;
bb2=buf2[j];
   j++;if(j>2048) j=0;
  iw1=512-bb2;
  iw1=(iw1 * buf2[j]  /2048) ;
 
 
  if(ulOutput>4095) ulOutput=4095;
   
   dacc_write_conversion_data(DACC_INTERFACE, iw1);
 
}


From what I understand, the code is taking a copy of the buffer and mixing the tw with one of the buffers delayed by a number of samples... am i right?? I'm trying to implement feedback to the delay but haven't cracked it yet...   :|
but its a start!
Smooberry Dooberry.

DVDdoug

Quote
Could anyone tell me if there are any good examples or tutorials of doing real time DSP on wav files.
DSP is kind-of a "big subject".    (I've studied it a bit, but I've never actually done any DSP programming.)

Do you understand how basic digital audio works? Sampling, etc?   Do you know how to change the volume (multiplication) or how to mix two audio sources/files (summation)?

For the very basics of how audio is digitized into a series of samples, the Audacity website has tutorial.

If you want to jump-into something more advanced, there is a FREE online book called The Scientist and Engineer's Guide to Digital Signal Processing, by By Steven W. Smith.

You can also write plug-ins for Audacity using the Nyquest Programming Language.   It's supposed to be fairly easy to use, and it might be an easier place to start learning DSP than trying to start with the Arduino.    (With an Audacity plug-in you can concentrate on the DSP effect and you don't have to worry about reading/writing audio files, or reading/writing ADCs & DACs, or generating a sample-rate clock, or any of that "overhead" stuff.)

Grumpy_Mike

Quote
From what I understand, the code is taking a copy of the buffer and mixing the tw with one of the buffers delayed by a number of samples... am i right??

Yes, basically samples are put into a circular buffer and pulled out at one or more places from the buffer to give you echo and reverb like this diagram.

Headroom

If looking at differnt hardware is still an option then I'd suggest your look at the Fully Arduino compatible Teensy 3.1 with its Audio Adapter and the associated Audio Library:

http://pjrc.com/teensy/td_libs_Audio.html
http://pjrc.com/store/teensy3_audio.html
http://pjrc.com/store/teensy31.html

The Teensy uses an Arm Cortex M4 which makes it ideal for audio applications.

The thread for the Audio Library contains good informant as to what is possible with the suggested hardware combination :
http://forum.pjrc.com/threads/24793-Audio-Library?highlight=Audio+library
http://trippylighting.com

http://ledshield.wordpress.com/

Mubase

Quote
DSP is kind-of a "big subject".    (I've studied it a bit, but I've never actually done any DSP programming.)


I know. Ive just finished a BSc in Music Technology & Audio systems at London Metropolitan Uni. DSP for scientists and enginneers is an excellent book to start from. I do know the basics of DSP and digital audio and have completed some projects using DSP and synthesis techniques using Arduino and Processing, MAX/MSP etc...
I'm not an expert coder though... My embedded C/C++ has improved over the 3 or so years I have been hooked...  :)

Quote
If looking at differnt hardware is still an option then I'd suggest your look at the Fully Arduino compatible Teensy 3.1 with its Audio Adapter and the associated Audio Library:

http://pjrc.com/teensy/td_libs_Audio.html
http://pjrc.com/store/teensy3_audio.html
http://pjrc.com/store/teensy31.html

The Teensy uses an Arm Cortex M4 which makes it ideal for audio applications.


Now this looks VERY interesting... I've found a supplier of the Teensy board here in the UK. They also have the Audio board so I might possibly get one of these...

:)

Still haven't worked out how to add feedback to the algorithim though...

Smooberry Dooberry.

Grumpy_Mike

Quote
Still haven't worked out how to add feedback to the algorithim though.


Do you mean real feedback?
That is simple, you take the contents of the output pointer and add a fraction of the sample found at that point to the current sample before you store it in the buffer.

Mubase

Quote
Do you mean real feedback?


Hi. Yes thanks. So I get a decaying effect rather than just a simple 1 x delay...

Could you please help me with hw I would go about achieving this with this code:

Code: [Select]
void loop1()
{
// 2048 is the 0 value of audio out.

   int16_t ulOutput=2048;
// Add in echo effect (if enabled) from circular buffer.
  // This takes place before audio level clipping so that
  // any clipping distortion won't be repeated in echo.
 
   sampler.next();
   
   ulOutput += sampler.output();  // volume and bit adjusted buffer

   buf[i]=ulOutput;   // put buffer into anther buffer
   i++;                 // fill it up
   if (i>4096)           // set to zero at end of buffer
   i=0;
  bb=buf[i];     // first bucket
iw=127-bb;   
iw=iw  * ulOutput / 2048;
buf2[j]=iw;
bb2=buf2[j];
   j++;if(j>4096) j=0;
  iw1=127-bb2;
  iw1=(iw1 * buf2[j]  /2048 ) ;
 
 
  if(ulOutput>4095) ulOutput=4095;
   
   dacc_write_conversion_data(DACC_INTERFACE, iw1);
 
}


I do understand the principle of putting a fraction of the output back into the buffer but am not entirely sure how to do it... 
sorry for being so thick.
:smiley-red:
Smooberry Dooberry.

Grumpy_Mike

Sorry I have no idea what that code is trying to do and you have only posted one bit of it. You only need one buffer.

In psudo code you need to do this:-
Code: [Select]

InputSample = GetCurrentInput();
pastSample = GetBuffer(outputPointer);
newSample = (InputSample >> 1) + (pastSample >> 1); // the shift right is a quick divide so half each of the two samples or whatever fractions you want
SaveSample(inputPointer, newSample);
// increment the pointers and wrap round
outputPointer ++;
if(outputPointer > bufferLimit) outputPointer = 0;
inputPointer++;
if(inputPointer > bufferLimit) inputPointer = 0;

Paul Stoffregen


Hi. Yes thanks. So I get a decaying effect rather than just a simple 1 x delay...


For an echo that repeats and slowly decays each time, in the design tool, try using a mixer to feed the input of a delay object.  Then take the delay output (one of the 8 taps) and feed it back into the mixer.  Also connect your input signal into the mixer, and take the output from the delay (perhaps another tap with little or no delay), even even use another mixer to add together several of the delay taps.

In setup(), configure the mixer so the feedback signal has less than 1.0 gain, so the repeating echo gets softer and slowly dies out.  Of course, configure the delay taps.  You can probably also make some interesting stereo effects by adjusting different levels (on the mixer channels) and somewhat delays for left vs right.  At the beginning of setup(), don't forget to use a larger number in AudioMemory() so there's enough memory available to the library to store your delay length.

Mubase

Hi again.
Thanks for the pseudocode Mike.
One question...
What is this doing?
Code: [Select]
SaveSample(inputPointer, newSample);

????

thanks again.
Smooberry Dooberry.

Grumpy_Mike

It is a function that saves a sample ( a number ) in the buffer, at the position given by the variable inputPointer, with a sample value given by the variable newSample.

Mubase

OK Sussed!! Fully working circular delay buffer with feedback. Here's the full sketch using the Groovuino sampler library.

Code: [Select]
// Simple sampler which alternates a kick and a snare

#include <SdFat.h>
#include "sampler.h"
#include <DueTimer.h>

SdFat sd;
Sampler sampler;
int to=0;
int buf[4096];
int buf2[2048];
int buf3[2048];
int i,j,k;

int sum=0;
int iw,iw1,bdac0,bdac1,bb,bb2,bb3;
void setup()
{
i=0,j=1;
  Timer0.attachInterrupt(loop1).setFrequency(48000).start();
  analogWrite(DAC1,0);
  analogWrite(DAC0,0);

    sd.begin(4, SPI_FULL_SPEED);
    sampler.init();
}

void loop()
{
   if( to>100000)
   {
     to=0;
// POUM
     sampler.load("hello.wav");
     sampler.splay(200,120);
   }
   
   if(to==50000)
   {
// TCHACK
     sampler.load("snare.wav");
     sampler.splay(100,120);
   }
   
   sampler.buffill();
   
   delayMicroseconds(17);
   
   to++;
}
// interrupt loop
void loop1()
{

// feedback.
int8_t echo_level = 7;
uint16_t rd;
// 2048 is the 0 value of audio out.
   int16_t ulOutput=2048;

 
 
   sampler.next();
   
   ulOutput += sampler.output();
   // Add in echo effect from circular buffer.
rd=ulOutput + buf[j]*echo_level;
rd/=8;
if (rd>4095) rd=4095;
buf[i]=rd;
i++;
if (i==4096) i=0;
j++;
if (j==4096) j=0;
   //buf[i]=ulOutput;

   if(ulOutput>4095) ulOutput=4095;  // clipping check
   
   dacc_write_conversion_data(DACC_INTERFACE, rd);
 
}


:)
Smooberry Dooberry.

Go Up