Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 14 of 14

Thread: Rate Reduction effect for audio.h - code and questions

  1. #1
    Senior Member
    Join Date
    Mar 2013
    Location
    Austin, TX
    Posts
    101

    Rate Reduction effect for audio.h - code and questions

    I've been messing with audio.h a bit and decided to make a new effect. I stated with the code for the multiply effect and made this sample and hold / rate reducer / bit crusher.

    It works ok but I have some questions about how.

    "pa" is a pointer to the audio block but also the counter going through the block and the contents of the block?
    What do "*pa++ = audio_in0;" and "mod_in0 = *pb++;" mean exactly? It's incrementing but also... what? They come from the multiply code, just renamed.


    effect_sh.h:
    Code:
    #ifndef effect_sh_h_
    #define effect_sh_h_
    #include "AudioStream.h"
    #include "utility/dspinst.h"
    
    #define SAMPLE_AND_HOLD      0
    #define RATE_RECUCTION  1
    #define BIT_REDUCTION    2
    
    class AudioEffectSH : public AudioStream
    {
    public:
      AudioEffectSH() : AudioStream(2, inputQueueArray) { }
      virtual void update(void);
    
    
    private:
      audio_block_t *inputQueueArray[2];
     // short fx_mode;
    
    
    };
    
    #endif

    effect_sh.cpp:

    Code:
    #include "effect_sh.h"
    
    void AudioEffectSH::update(void){
    
      audio_block_t *blocka, *blockb;
      uint32_t *pa, *pb, *end;
      static int32_t audio_in0, paudio_in0; //, a56, a78;
      static short ch1,change0;
      static int32_t mod_in0, pmod_in0,b_wut; //, b56, b78;
      static uint32_t prev_sh, ccnt,hold_cnt,rate_cnt,rate_cnt_max;
      blocka = receiveWritable(0);
      blockb = receiveReadOnly(1);
      if (!blocka) {
        if (blockb) release(blockb);
        return;
      }
      if (!blockb) {
        release(blocka);
        return;
      }
      
    
      pa = (uint32_t *)(blocka->data);
      pb = (uint32_t *)(blockb->data);
      end = (pa + AUDIO_BLOCK_SAMPLES/2);
    
      while (pa < end) {
    
        paudio_in0=audio_in0;
        pmod_in0=mod_in0;
        mod_in0 = *pb++;
    
    
        if (pmod_in0 >=0 && mod_in0 <0){
          change0=1;
          rate_cnt_max=rate_cnt;
          rate_cnt=0;
        }
    
        else{
          change0=0;
          rate_cnt++;
        }
    
    
        if (change0==1){
           audio_in0 = *pa;
        }
    
        if (change0==0){
          audio_in0 = paudio_in0;
        }
      
    
         *pa++ = audio_in0;
        
    
        /*
        if (millis() - prev_sh>300){  
          prev_sh=millis();
         // Serial.println(hold_len);
          Serial.println(rate_cnt_max);
          Serial.println();
        }  
        */
    
      }
    
      transmit(blocka);
      release(blocka);
      release(blockb);
    
    }
    Put these in your audio folder and add the line "#include "effect_sh.h"" to audio.h to try it out


    sketch:
    Code:
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    
    long prev;
    
    AudioSynthNoisePink    pink1;        
    AudioOutputAnalog      dac1;         
    AudioEffectSH          SH1;          
    AudioSynthWaveformSine sine1;
    AudioSynthWaveformSine modulator_sine2;
    
    AudioConnection          patchCord1(sine1, 0, SH1,0);
    AudioConnection          patchCord3(modulator_sine2, 0, SH1,1);
    AudioConnection          patchCord2(SH1, dac1);
    
    
    void setup(void)
    {
      
      Serial.begin(9600);
      AudioMemory(20);
      pink1.amplitude(1);
      
      sine1.amplitude(1);
      sine1.frequency(100);
      
      modulator_sine2.amplitude(1);
      modulator_sine2.frequency(100);
      
      analogReadResolution(12);
      analogReadAveraging(21);
      
    }
    
    
    void loop(void)
    {
      uint32_t a1 = analogRead(A1)*2;
      uint32_t a2 = analogRead(A2)*3;
    
      
      sine1.frequency(a1);
      modulator_sine2.frequency(a2);
    
     
      if (millis() - prev>200){  
       prev=millis();
    
       Serial.println(a2);
    
      }
    
    }

  2. #2
    Senior Member
    Join Date
    Nov 2013
    Posts
    720
    Quote Originally Posted by john-mike View Post
    ...

    "pa" is a pointer to the audio block but also the counter going through the block and the contents of the block?
    What do "*pa++ = audio_in0;" and "mod_in0 = *pb++;" mean exactly? It's incrementing but also... what? They come from the multiply code, just renamed.

    ...
    '*pa++=something;' assigns 'something' to the memory location being pointed to by pa before it is incremented; 'something' will (usually) be the same variable type that pa was cast a pointer to and incrementing pa will not move it just '1' memory location forward but instead the number of memory locations for the variable type involved - if it was a char or uint8_t then it will just move 1 memory location forward after all.

    '*pa++;' is not well defined to me - I'd prefer the compiler increments the contents of the variable that pa is pointing at for this one but it may just ignore the 'pointing' reference and move the pointer forward instead - if that is the original Author's intent then I wish they removed the '*' - if I was trying to increment the contents of the variable which is being pointed at I would write '*(pa)++;' to make my intent better defined imho - alternatively I would just write 'pa++;' to just move the pointer forward in the array.


    Edit: Am not expert btw, a better defined answer may be on its way

  3. #3
    Senior Member duff's Avatar
    Join Date
    Jan 2013
    Location
    Las Vegas
    Posts
    1,027
    *pa++ - will return the content of the memory location (dereference) before it increments the pointer to the next memory location by sizeof(pa) same goes for *pb++

    its sometimes faster to work with pointers when updating and reading the contents of a buffer.

  4. #4
    Senior Member
    Join Date
    Nov 2013
    Posts
    720
    oh, I didn't look at the posted code enough to see context for when OP asked '*pa++;' as if it was on a line by itself - "SOMETHING=*pa++;" is very different from "*pa++;"

  5. #5
    Senior Member
    Join Date
    Mar 2013
    Location
    Austin, TX
    Posts
    101
    Ah ok thanks!

    So then "something = *p++;" sets something to the current value of p then increments?

  6. #6
    Senior Member duff's Avatar
    Join Date
    Jan 2013
    Location
    Las Vegas
    Posts
    1,027
    Quote Originally Posted by john-mike View Post
    So then "something = *p++;" sets something to the current value of p then increments?
    yes, here is an example to show that it works that way:
    Code:
    int16_t buf[11] = { -5, -4, -3, -2, -1, 0 , 1, 2, 3, 4, 5 };
    
    
    void setup() {
      // put your setup code here, to run once:
      while (!Serial);
      delay(100);
    }
    
    
    void loop() {
      // put your main code here, to run repeatedly:
      Serial.print("printing 'buffer'  -> ");
      for (int i = 0; i < 11; i++) {
        Serial.printf("%i, ", buf[i]);
      }
      Serial.println();
    
    
      int16_t *p = (int16_t *) buf;
      int16_t something;
      
      Serial.print("printing 'pointer' -> ");
      for (int i = 0; i < 11; i++) {
        something = *p++;
        Serial.printf("%i, ", something);
      }
      Serial.println();
      Serial.println();
      delay(5000);
    }

  7. #7
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,951
    This page might help.

    https://www.eskimo.com/~scs/cclass/notes/sx10b.html

    The autoincrement operator ++ (like its companion, --) makes it easy to do two things at once. We've seen idioms like a[i++] which accesses a[i] and simultaneously increments i, leaving it referencing the next cell of the array a. We can do the same thing with pointers: an expression like *ip++ lets us access what ip points to, while simultaneously incrementing ip so that it points to the next element. The preincrement form works, too: *++ip increments ip, then accesses what it points to. Similarly, we can use notations like *ip-- and *--ip.
    The audio library is filled with this sort of fairly advanced C syntax. Normally I try to avoid such things in tutorials and example sketches. Inside the library a lot of the code is highly optimized for performance and uses lots of somewhat advanced C pointer stuff.

  8. #8
    Senior Member
    Join Date
    Mar 2013
    Location
    Austin, TX
    Posts
    101
    Thanks for the example and link! That pretty much answered all my questions.

  9. #9
    Senior Member
    Join Date
    Mar 2013
    Location
    Austin, TX
    Posts
    101
    Well I meant that question.

    Next one is about basic arithmetic on the blocks. How do I do it properly? Why do these simplified examples yield noise?

    This one pretty much just makes white noise. Lots of clipping and folding.
    Code:
    void AudioEffectSH::update(void){
    
      audio_block_t *blocka;
      uint32_t *pa,*end;
      blocka = receiveWritable(0);
    
      if (!blocka) {
        return;
      }
    
    
      pa = (uint32_t *)(blocka->data);
    
    
      end = (pa + AUDIO_BLOCK_SAMPLES/2);
    
      while (pa < end) {
    
    
        int32_t audio_in0 = *pa;
     
        int32_t t1 =  (audio_in0*mix)>>16;
        
        *pa++ = t1;
    
    
      }
    
      transmit(blocka);
      release(blocka);
    
    
    }
    This seems to be reducing the sampling rate also, but not on purpose this time!
    Code:
    void AudioEffectSH::update(void){
    
      audio_block_t *blocka;
      uint32_t *pa,*end;
      blocka = receiveWritable(0);
    
      if (!blocka) {
        return;
      }
    
    
      pa = (uint32_t *)(blocka->data);
    
    
      end = (pa + AUDIO_BLOCK_SAMPLES/2);
    
      while (pa < end) {
    
    
        int32_t audio_in0 = *pa;
     
        int32_t t1 =  signed_multiply_32x16b(audio_in0,mix);
        int32_t t2 =  signed_saturate_rshift(t1,32,16);
    
        *pa++ = t2;
    
    
      }
    
      transmit(blocka);
      release(blocka);
    
    
    }



    I see that the mixer and envelope are modifying multiple samples at once? I think?

    But like you said:
    Quote Originally Posted by PaulStoffregen View Post
    The audio library is filled with this sort of fairly advanced C syntax.

  10. #10
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,951
    Quote Originally Posted by john-mike View Post
    Why do these simplified examples yield noise?
    ....
    This one pretty much just makes white noise.
    Probably because you're treating an array of 16 bit integers as if they are 32 bit numbers, causing every other 16 bit number to become something pretty much random. The audio samples are always 16 bits.

    Maybe you're trying to follow some of the existing code which uses very advanced optimization techniques to pull pairs of samples from the buffer as 32 bit numbers? That's a very advanced and difficult technique. For starting, you should only access the buffer as an array of int16_t.

  11. #11
    Senior Member
    Join Date
    Mar 2013
    Location
    Austin, TX
    Posts
    101
    Ah ok.
    I switched them all but now it seems it's only processing every other block?
    All the rest of the code above is the same.

    EDIT:
    Ooops it was the AUDIO_BLOCK_SAMPLES/2 needed by the 2 sampel at a time multiply code.

    The following works:

    Code:
    #include "effect_sh.h"
    
    
    void AudioEffectSH::update(void){
    
      audio_block_t *blocka;
      int16_t *pa,*end;
      blocka = receiveWritable(0);
    
      if (!blocka) {
        return;
      }
    
    
      pa = (int16_t *)(blocka->data);
    
    
      end = (pa + AUDIO_BLOCK_SAMPLES);
    
      while (pa < end) {
    
        int16_t audio_in0 = *pa;
        int16_t t1 =  (audio_in0*mix)>>16;
        
        *pa++ = t1;
    
      }
    
      transmit(blocka);
      release(blocka);
    
    
    }
    Attached Thumbnails Attached Thumbnails Click image for larger version. 

Name:	ok-waht.gif 
Views:	60 
Size:	33.5 KB 
ID:	4750  
    Last edited by john-mike; 07-24-2015 at 01:44 AM.

  12. #12
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,951
    It's not every other block.....

    Quote Originally Posted by john-mike View Post
    end = (pa + AUDIO_BLOCK_SAMPLES/2);
    You're stopping after only half the data in every block!

  13. #13
    Senior Member
    Join Date
    Mar 2013
    Location
    Austin, TX
    Posts
    101
    Ha yup looks we were posting at the same time.

    Thanks again!

  14. #14
    Senior Member
    Join Date
    Mar 2013
    Location
    Austin, TX
    Posts
    101
    Ok here's the first finished version of it.
    https://github.com/BleepLabs/sample-and-hold

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •