Welcome, Guest
Username: Password: Remember me

TOPIC: A brand new way: RING BUFFER!

A brand new way: RING BUFFER! 3 years 7 months ago #1290

  • RichardJAllen
  • RichardJAllen's Avatar
  • OFFLINE
  • New Member
  • Posts: 4
  • Thank you received: 2
  • Karma: 2
Hi All,

I have been playing around with the uno shield for a week or so now. During that time I've created a ring buffer architecture which stores the audio before outputting. This allows the audio to be manipulated before outputting it. Hope this will help you lot create something.

An example of what can be done with a ring buffer is attached at the bottom. Its a variable pitch down, which manipulates the output rate to shift the pitch. The effect is only on when the LED is on. Pushbutton 1 lets you input the tempo for the LED, pushbutton 2 varies the output rate.

This is not a perfect way to do it but just an interesting way to process the audio. Any suggestions on improvements to the ring buffer architecture would be great. And enjoy the variable pitch shift!

Cheers,

Ric

CLEAN:
create.arduino.cc/editor/richardjallen/3...f5116fad45d5/preview
VARIABLE PITCH SHIFT:
create.arduino.cc/editor/richardjallen/b...b35237053622/preview

CLEAN CODE:
// RING BUFFER CLEAN
// via www.Electrosmash.com
// via OpenMusicLabs 
 
//defining hardware resources.
#define LED 13
#define FOOTSWITCH 12
#define TOGGLE 2
#define PUSHBUTTON_1 A5
#define PUSHBUTTON_2 A4

//defining the output PWM parameters
#define PWM_FREQ 0x00FF // pwm frequency - 31.3KHz
#define PWM_MODE 0 // Fast (1) or Phase Correct (0)
#define PWM_QTY 2 // 2 PWMs in parallel
#define BUFF_SIZE 500 // size of ring buffer, can theoretically be anything
                      // 500 should hold 15.9 ms of audio
 
// STRUCTURES 
 
// can be used to implement a virtual debounce, e.g.
// if (millis() - button_time_s.pb1 > 250)
// {
//    do something
//    button_time_s.pb1 = millis();
// }
// {
//    don't do something as it will be the same press as before
// }
// also keeps track of when the footswitch is turned on
struct button_time_s
{
  long footswitch;
  long pb1;
  long pb2;
};
 
// counts iterations of main and interrupt loops
struct count_s
{
  int main;
  int interrupt;
};
 
// keeps track of when the footswitch is turned on
// by storing a current and an old
// if current != old
// switch has just been turned on
struct switch_flag_s
{
  bool current;
  bool old;
};
 
// ring buffer structure
// buff is array to store the audio
// head and tail and head and tail, respectively
struct ring_buff_s
{
  int buff[BUFF_SIZE];
  int head;
  int tail;
};
 
// VARIABLES
int input, vol_variable=512;
byte ADC_low, ADC_high;
int offset = 5;  //offset of 5 ms will still be "real time", can theoretically have up to 30 ms delay between input and output and still appear "real time"
long milli;
button_time_s button_time;
count_s count;
switch_flag_s switch_flag;
ring_buff_s buf;
 
// BUFFER FUNCTIONS
void push_to_buff(int in)
{
  buf.buff[buf.head] = in;
  buf.head++;
  if (buf.head == BUFF_SIZE)
  {
    buf.head = 0;
  }
}
 
int pop_from_buff (void)
{
  int out = buf.buff[buf.tail];
  if (buf.tail != buf.head)
  {
    buf.tail++;
  }
  if (buf.tail == BUFF_SIZE)
  {
    buf.tail = 0;
  }
  return out;
}
 
void setup() 
{
  //setup IO
  pinMode(FOOTSWITCH, INPUT_PULLUP);
  pinMode(PUSHBUTTON_1, INPUT_PULLUP);
  pinMode(PUSHBUTTON_2, INPUT_PULLUP);
  pinMode(LED, OUTPUT);
 
  // setup ADC
  ADMUX = 0x60; // left adjust, adc0, internal vcc
  ADCSRA = 0xe5; // turn on adc, ck/32, auto trigger
  ADCSRB = 0x07; // t1 capture for trigger
  DIDR0 = 0x01; // turn off digital inputs for adc0
 
  // setup PWM
  TCCR1A = (((PWM_QTY - 1) << 5) | 0x80 | (PWM_MODE << 1)); //
  TCCR1B = ((PWM_MODE << 3) | 0x11); // ck/1
  TIMSK1 = 0x20; // interrupt on capture interrupt
  ICR1H = (PWM_FREQ >> 8);
  ICR1L = (PWM_FREQ & 0xff);
  DDRB |= ((PWM_QTY << 1) | 0x02); // turn on outputs
  sei(); // turn on interrupts - not really necessary with arduino
}
 
 
// MAIN LOOP
// checks the footswitch is pushed every 100 loops
// changes the switch_flag.current
// checks if the footswitch has been recently pressed
// if switch flag is true call functions
// else just set LED here, 
// doesn't need to optimised if no audio is being processed
// finally, increment count
void loop() 
{
  if (count.main % 100 == 0)
  {
    switch_flag.current = digitalRead(FOOTSWITCH);
    if (switch_flag.current != switch_flag.old)
    {
      button_time.footswitch = millis();
      digitalWrite(LED, true);
    }
    switch_flag.old = switch_flag.current;
    if (switch_flag.current == true)
    { 
      milli = millis();
      // add code here when effect is on
    }
    else
    {
      digitalWrite(LED, false);
    }
  }
  count.main++;
}
 
// AUDIO INPUTTER
// pulls the audio from the ADC
// pushes it to the ring buffer
void audio_inputter()
{  
  ADC_low = ADCL; // you need to fetch the low byte first
  ADC_high = ADCH;
  input = ((ADC_high << 8) | ADC_low); // make a signed 16b value
  if (input > 0x8000)
  {
    input = input + 0x8000;
  }
  push_to_buff(input);
}
 
// AUDIO OUTPUTTER
// wait until 
// pop audio from ring buffer and output to PWM
void audio_outputter()
{
  if (milli - button_time.footswitch > offset)
  {
    int output = pop_from_buff();
    OCR1AL = ((output + 0x8000) >> 8); // convert to unsigned, send out high byte
    OCR1BL = output; // send out low byt
  }
}
 
// TIMER1 INTERRUPT
// checks the switch flag is true
// inputs then outputs audio
// increment count
ISR(TIMER1_CAPT_vect) 
{
  if (switch_flag.current == true)
  {
    audio_inputter();
    audio_outputter();
    count.interrupt++;  
  }
}

VARIABLE PITCH SHIFT CODE:
// RING BUFFER VARIABLE PITCH SHIFT
// via www.Electrosmash.com
// via OpenMusicLabs 
 
//defining hardware resources.
#define LED 13
#define FOOTSWITCH 12
#define TOGGLE 2
#define PUSHBUTTON_1 A5
#define PUSHBUTTON_2 A4

//defining the output PWM parameters
#define PWM_FREQ 0x00FF // pwm frequency - 31.3KHz
#define PWM_MODE 0 // Fast (1) or Phase Correct (0)
#define PWM_QTY 2 // 2 PWMs in parallel
#define BUFF_SIZE 500 // size of ring buffer, can theoretically be anything
                      // 500 should hold 15.9 ms of audio
                      // 
 
// STRUCTURES
 
struct button_time_s
{
  long footswitch;
  long pb1;
  long pb2;
};
 
struct count_s
{
  int main;
  int interrupt;
};
 
struct switch_flag_s
{
  bool current;
  bool old;
};
 
struct ring_buff_s
{
  int buff[BUFF_SIZE];
  int head;
  int tail;
};
 
struct tempo_s
{
  int interval;
  long timeout;
};
 
struct divider_s
{
  int input;
  int output;
};
 
// VARIABLES
int input, vol_variable=512;
byte ADC_low, ADC_high;
bool led_flag;
int offset = 5;
long milli;
button_time_s button_time;
count_s count;
switch_flag_s switch_flag;
ring_buff_s buf;
divider_s divider;
tempo_s tempo;
 
// BUFFER FUNCTIONS
void push_to_buff(int in)
{
  buf.buff[buf.head] = in;
  buf.head++;
  if (buf.head == BUFF_SIZE)
  {
    buf.head = 0;
  }
}
 
int pop_from_buff (void)
{
  int out = buf.buff[buf.tail];
  if (buf.tail != buf.head)
  {
    buf.tail++;
  }
  else
  {
    buf.buff[buf.tail]--;
  }
  if (buf.tail == BUFF_SIZE)
  {
    buf.tail = 0;
  }
  return out;
}
 
void setup() 
{
  //setup IO
  Serial.begin(9600);
  pinMode(FOOTSWITCH, INPUT_PULLUP);
  pinMode(PUSHBUTTON_1, INPUT_PULLUP);
  pinMode(PUSHBUTTON_2, INPUT_PULLUP);
  pinMode(LED, OUTPUT);
 
  // setup ADC
  ADMUX = 0x60; // left adjust, adc0, internal vcc
  ADCSRA = 0xe5; // turn on adc, ck/32, auto trigger
  ADCSRB = 0x07; // t1 capture for trigger
  DIDR0 = 0x01; // turn off digital inputs for adc0
 
  // setup PWM
  TCCR1A = (((PWM_QTY - 1) << 5) | 0x80 | (PWM_MODE << 1)); //
  TCCR1B = ((PWM_MODE << 3) | 0x11); // ck/1
  TIMSK1 = 0x20; // interrupt on capture interrupt
  ICR1H = (PWM_FREQ >> 8);
  ICR1L = (PWM_FREQ & 0xff);
  DDRB |= ((PWM_QTY << 1) | 0x02); // turn on outputs
  sei(); // turn on interrupts - not really necessary with arduino
  tempo.interval = 150;
  divider.input = 0;
  divider.output = 9;
}
 
void tempo_setter()
{
  if (milli > tempo.timeout)
  {
    led_flag = !led_flag;
    digitalWrite(LED, led_flag); 
    tempo.timeout = milli + tempo.interval;
  }  
}
 
void tempo_getter()
{
  if (digitalRead(PUSHBUTTON_1) == false)
  {
    if (milli > button_time.pb1)
    {
      if (milli - button_time.pb1 > 150)
      {
        tempo.interval = milli - button_time.pb1;
        tempo.timeout = milli + tempo.interval;
      }
      button_time.pb1 = milli;
    }
  }
}
 
void pitch_getter()
{
  if (digitalRead(PUSHBUTTON_2) == false)
  {
    if (milli - button_time.pb2 > 150)
    {
      divider.output++;
      if (divider.output > 15)
      {
        divider.output = 0;
      }
      if (divider.output == 1)
      {
        divider.output = 2;    
      }
      button_time.pb2 = milli;
    }
  }
}
 
// MAIN LOOP
// checks the footswitch is pushed every 100 loops
// changes the switch_flag
// if switch flag is true call functions
// else just set LED here, 
// doesn't need to optimised if no audio is being processed
// increment count
 
void loop() 
{
  if (count.main % 100 == 0)
  {
    switch_flag.current = digitalRead(FOOTSWITCH);
    if (switch_flag.current != switch_flag.old)
    {
      button_time.footswitch = millis();
      digitalWrite(LED, true);
      Serial.println(button_time.footswitch);
    }
    switch_flag.old = switch_flag.current;
    if (switch_flag.current == true)
    { 
        milli = millis();
        pitch_getter();
        tempo_getter();
        tempo_setter();
    }
    else
    {
      digitalWrite(LED, false);
    }
  }
  count.main++;
}
 
// AUDIO INPUT
// pulls the audio from the ADC
// pushes it to the ring buffer
void audio_inputter(int divide)
{  
  ADC_low = ADCL; // you need to fetch the low byte first
  ADC_high = ADCH;
  input = ((ADC_high << 8) | ADC_low); // make a signed 16b value
  if (input > 0x8000)
  {
    input = input + 0x8000;
  }
  push_to_buff(input);
}
 
// AUDIO OUTPUTTER
// if the eqn is != 0
// pop audio from ring buffer and output to PWM
void audio_outputter(int divide)
{
  if (milli - button_time.footswitch > offset)
  {
//    Serial.println("YE");
    if (count.interrupt % divide != 0)
    {
      int output = pop_from_buff();
      OCR1AL = ((output + 0x8000) >> 8); // convert to unsigned, send out high byte
      OCR1BL = output; // send out low byt    
    } 
  }  
}
 
 
// TIMER1 INTERRUPT
// checks the switch flag is true
// if LED flag is true, apply effect
// else do not apply effect
ISR(TIMER1_CAPT_vect) 
{
  if (switch_flag.current == true)
  {
    if (led_flag == true)
    {
      audio_inputter(divider.input);
      audio_outputter(divider.output);
    }
    else
    {
      audio_inputter(0);
      audio_outputter(0);
    }
    count.interrupt++;  
  }
}
The administrator has disabled public write access.
The following user(s) said Thank You: Ray, Frostbite

A brand new way: RING BUFFER! 3 years 7 months ago #1291

  • Ray
  • Ray's Avatar
  • OFFLINE
  • Moderator
  • Posts: 702
  • Thank you received: 152
  • Karma: 44
Thanks for your fantastic contribution, I am linking this from the main "How to start programming pedalSHIELD UNO" topic :)
The administrator has disabled public write access.
Time to create page: 0.321 seconds
Powered by Kunena Forum
Joomla SEF URLs by Artio