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

Thread: Realtime Audio Reversing - is it possible using Teensy?

  1. #1
    Junior Member
    Join Date
    Nov 2018
    Posts
    15

    Realtime Audio Reversing - is it possible using Teensy?

    I'm not as smart as most in this forum so I apologize for asking if this is a simple project. I plan on going through the audio tutorial (again) tomorrow...in hopes for things to 'sink in'.


    My husband has challenged me to do a Halloween project. I have every Teensy board available, along with audio adapter, psram (4.1), 23LC1024(4.1), and W25Q128FV but after going through many forum posts & github code...I've come to a standstill on how to proceed. I have learned how to solder (even the SMD chips & they passed the tests) but now need to figure out how to do the actual programming!


    What I want to do is create a scary Exorcist effect where the Line-in/Mic audio is processed (this is where I am drawing a blank) and the output is REVERSED. I want it to be actual reverse and not simulated with the audio source being live (not pre-recorded). Is this possible on Teensy? I've only seen examples of reversed audio using pre-recorded wave or raw files with Teensy.


    I've looked at the Queue & Delay portions of the audio library but have no clue how to modify the contents appropriately. I do have a Nootropic AudioHacker shield (see: https://github.com/nootropicdesign/a...imeReverse.ino) but unsure how to port over to Teensy.


    So...is my idea possible? I am sure I have bitten off more than I can chew...but if it can be done using UNO & the 23LC1024 chip...why not Teensy 4.1 + audio adapter? I'm just so darn clueless and need some pointers.


    I know realtime reversing isn't technically possible...but 'near' realtime will be fine.


    Thank you!

  2. #2
    Senior Member
    Join Date
    Apr 2020
    Location
    Tucson
    Posts
    135
    If you want a "backward speech effect" I'm wagering all you really neeed to do is play with the envelope of the sound. Alter the attack from an envelope follower by delaying it through an envelope generator. Harder syllables would then come out with a slowly rising attack, I'm betting it would sound something like you want.

    EDIT: I looked at the video, it's an interesting effect but not "demonic." I'd look into pitch shifting and envelope manipulation. I'd suggest trying the 19 band vocoder example, that might get you started.

  3. #3
    Junior Member
    Join Date
    Nov 2018
    Posts
    15
    So I will study effect_envelope.cpp & .h files. I seem to hit a brick wall not knowing how to properly modify within the ino sketch (or anywhere lol). But that is due to my lack of programming skills. Thank you so much for steering me in the right direction, @boxxofrobots! I will try again tomorrow after a good rest.

  4. #4
    Senior Member
    Join Date
    Apr 2020
    Location
    Tucson
    Posts
    135
    Cool, but I don't think there's any need to write your own effect. I suspect you can build the effect from the library and some glue code. Look at the vocoder - it's hundreds of lines of code written by automation, but the actual loop is really nothing. It's just initialize and it plays.

  5. #5
    Junior Member
    Join Date
    Nov 2018
    Posts
    15
    I have SO much to learn. I'm googling 'envelope' audio effect in a moment as I have no musical background. I hope it will be actual reversal of the audio but if not, at least I will be learning more. I can't lose this contest against my husband! haha! And thank you again.

  6. #6
    Senior Member
    Join Date
    Jul 2020
    Posts
    472
    Simple reversal is by definition not realtime. You could process short samples and reverse each one, but
    then you have to have a delay. You also have to have some algorithm for breaking the incoming audio
    into sections - spotting syllable or word boundaries for instance is not easy, so this would probably have
    to be a fixed period per section.

    The most efficient use of buffer memory is boustrophedonic for both input and output, so you'd need to be
    able to write and read buffers in either direction.

  7. #7
    Junior Member
    Join Date
    Nov 2018
    Posts
    15
    I'm absolutely aware of the falsehood of the term "realtime reverse audio" and I should not have put that in the subject line (oopsie!).

    So, essentially a delay...queue...reversal of the queue...then output...with some sort effect (envelope/fade?) of joining between packets (sorry I'm probably not using correct terminology).

    But where I get tripped up is...let's say I have queue1 (a packet of audio...pls correct if I'm wrong) -- how do I move the position/index/pointer to the END of the packet and increment from that vantage point? I'm assuming I need to modify a .cpp & .h files for that. Or not? I forgot to mention I also have dyslexia but doesn't matter lol. I'm used to challenges. I really want to surprise my husband but he will definitely critique it. I want to get this one on the "MarkT"!

    BTW...@MarkT: I had to google "boustrophedonic" as reading that word caused an ice pick sensation in my head lol. But you are correct! Did you see the link to the AudioHacker script? How difficult would it be to port that to Teensy 4.0 or 4.1? I'm sure I'm over complicating things.

    But let's say I grab some audio into a buffer. My biggest question is: How do I access the buffer in the .ino sketch? How do I modify the readAddress so it starts from the end and increments towards the beginning?

    I'm annoying myself so will leave it at that. I need to sit down, get dirty (with code), and try harder. Then I will post my failed code.

  8. #8
    Junior Member
    Join Date
    Nov 2018
    Posts
    15
    fyi: My background is in computer graphics and I thought I could make the leap into Teensy but my health issues...(previous brain infection + surgery requiring daily iv abx for 3+ months) make connections more difficult than the average bear/joe. But once things "click"...I'm like Rain Man! I just have no clickage yet with finding access to modify audio buffers. Apologies for the tmi too.
    Last edited by aquaFina; 10-07-2020 at 01:28 AM. Reason: missing info

  9. #9
    Senior Member houtson's Avatar
    Join Date
    Aug 2015
    Location
    Scotland
    Posts
    135
    Hi, I had a hack at a set of files for you to work on. They will do a basic reverser like your Nootropic link in post #1.

    The example is set up to take audio through line in but you should be able to change to a mic.

    There is no crossfading so you'll get some pops and clicks.

    Cheers, Paul


    example.ino
    Code:
    #include <Audio.h>
    #include <SD.h>
    #include <SPI.h>
    #include <SerialFlash.h>
    #include <Wire.h>
    
    #include "effect_reverser.h"
    
    // GUItool: begin automatically generated code
    AudioInputI2S i2s1;            // xy=188,102
    AudioEffectReverser reverser;  // xy=408,104
    AudioOutputI2S i2s2;           // xy=619,107
    AudioConnection patchCord1(i2s1, 0, reverser, 0);
    AudioConnection patchCord2(reverser, 0, i2s2, 0);
    AudioConnection patchCord3(reverser, 0, i2s2, 1);
    AudioControlSGTL5000 sgtl5000_1;  // xy=211,214
    // GUItool: end automatically generated code
    
    #define DELAYLINE_MAX_LEN 80000  // number of samples at 44100 samples a second
    int16_t delay_line[DELAYLINE_MAX_LEN] = {};
    
    void setup() {
      // Enable the audio shield and set the output volume.
      sgtl5000_1.enable();
      sgtl5000_1.inputSelect(AUDIO_INPUT_LINEIN);
      sgtl5000_1.volume(0.5);
    
      // alocate some audio memory, don't need much as passing an array to the effect
      AudioMemory(15);
      
      // start up the effect and pass it an array to store the samples
      reverser.begin(delay_line, DELAYLINE_MAX_LEN);
    }
    
    void loop() {
      // do stuff
    }

    header file for custom effect

    effect_reverser.h
    Code:
    #ifndef effect_reverser_h_
    #define effect_reverser_h_
    #include "Arduino.h"
    #include "AudioStream.h"
    
    class AudioEffectReverser : public AudioStream {
     public:
      AudioEffectReverser(void) : AudioStream(1, inputQueueArray) {}
      
      // initialise the delay line
      void begin(int16_t *delay_line, uint32_t max_delay_length);
    
      // main update routine
      virtual void update(void);
    
      void inspect(void) { dump_samples = true; };
    
     private:
      audio_block_t *inputQueueArray[1];
      uint32_t max_delay_length_samples, half_delay_length_samples;  // lenght of the delay line in samples
      uint32_t write_index;               // write head position
      uint32_t read_index;      // read_index
      int16_t *sample_delay_line;  // pointer to delay line
    
      boolean buffer_filled = false;
    
      boolean dump_samples = false;
    };
    #endif
    effect_reverser.cpp
    Code:
    #include <Arduino.h>
    
    #include "effect_reverser.h"
    
    void AudioEffectReverser::begin(int16_t *delay_line, uint32_t max_delay_length) {
      sample_delay_line = delay_line;
      max_delay_length_samples = max_delay_length - 1;
      read_index = half_delay_length_samples = max_delay_length_samples / 2;
      write_index = 0;
    }
    
    void AudioEffectReverser::update(void) {
      audio_block_t *block;
      int16_t *block_pointer;
    
    // check if have a delay line
      if (sample_delay_line == NULL) return;
    
      // grab the block of samples as writeable
      block = receiveWritable();
      if (block) {
        block_pointer = block->data;
    
        // process each smaple in the block
        for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) {
          // write the sample to a buffer
          sample_delay_line[write_index++] = *block_pointer;
    
          // if enought in the buffer start reversing
          if (write_index >= half_delay_length_samples) buffer_filled = true;
          if (buffer_filled) *block_pointer = sample_delay_line[read_index--];
    
          // wrap round the write index
          if (write_index >= max_delay_length_samples) write_index = 0;
    
          // wrap round the read index
          if (read_index == 0) read_index = max_delay_length_samples;
    
          // move the block_pointer on
          block_pointer++;
        }
        // transmit the block out on channel 0
        transmit(block, 0);
        release(block);
      }
    }

  10. #10
    Junior Member
    Join Date
    Mar 2020
    Posts
    5

    Thanks for the code

    Quote Originally Posted by houtson View Post
    Hi, I had a hack at a set of files for you to work on. They will do a basic reverser like your Nootropic link in post #1.

    The example is set up to take audio through line in but you should be able to change to a mic.

    There is no crossfading so you'll get some pops and clicks.

    Cheers, Paul


    example.ino
    Code:
    #include <Audio.h>
    #include <SD.h>
    #include <SPI.h>
    #include <SerialFlash.h>
    #include <Wire.h>
    
    #include "effect_reverser.h"
    
    // GUItool: begin automatically generated code
    AudioInputI2S i2s1;            // xy=188,102
    AudioEffectReverser reverser;  // xy=408,104
    AudioOutputI2S i2s2;           // xy=619,107
    AudioConnection patchCord1(i2s1, 0, reverser, 0);
    AudioConnection patchCord2(reverser, 0, i2s2, 0);
    AudioConnection patchCord3(reverser, 0, i2s2, 1);
    AudioControlSGTL5000 sgtl5000_1;  // xy=211,214
    // GUItool: end automatically generated code
    
    #define DELAYLINE_MAX_LEN 80000  // number of samples at 44100 samples a second
    int16_t delay_line[DELAYLINE_MAX_LEN] = {};
    
    void setup() {
      // Enable the audio shield and set the output volume.
      sgtl5000_1.enable();
      sgtl5000_1.inputSelect(AUDIO_INPUT_LINEIN);
      sgtl5000_1.volume(0.5);
    
      // alocate some audio memory, don't need much as passing an array to the effect
      AudioMemory(15);
      
      // start up the effect and pass it an array to store the samples
      reverser.begin(delay_line, DELAYLINE_MAX_LEN);
    }
    
    void loop() {
      // do stuff
    }

    header file for custom effect

    effect_reverser.h
    Code:
    #ifndef effect_reverser_h_
    #define effect_reverser_h_
    #include "Arduino.h"
    #include "AudioStream.h"
    
    class AudioEffectReverser : public AudioStream {
     public:
      AudioEffectReverser(void) : AudioStream(1, inputQueueArray) {}
      
      // initialise the delay line
      void begin(int16_t *delay_line, uint32_t max_delay_length);
    
      // main update routine
      virtual void update(void);
    
      void inspect(void) { dump_samples = true; };
    
     private:
      audio_block_t *inputQueueArray[1];
      uint32_t max_delay_length_samples, half_delay_length_samples;  // lenght of the delay line in samples
      uint32_t write_index;               // write head position
      uint32_t read_index;      // read_index
      int16_t *sample_delay_line;  // pointer to delay line
    
      boolean buffer_filled = false;
    
      boolean dump_samples = false;
    };
    #endif
    effect_reverser.cpp
    Code:
    #include <Arduino.h>
    
    #include "effect_reverser.h"
    
    void AudioEffectReverser::begin(int16_t *delay_line, uint32_t max_delay_length) {
      sample_delay_line = delay_line;
      max_delay_length_samples = max_delay_length - 1;
      read_index = half_delay_length_samples = max_delay_length_samples / 2;
      write_index = 0;
    }
    
    void AudioEffectReverser::update(void) {
      audio_block_t *block;
      int16_t *block_pointer;
    
    // check if have a delay line
      if (sample_delay_line == NULL) return;
    
      // grab the block of samples as writeable
      block = receiveWritable();
      if (block) {
        block_pointer = block->data;
    
        // process each smaple in the block
        for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) {
          // write the sample to a buffer
          sample_delay_line[write_index++] = *block_pointer;
    
          // if enought in the buffer start reversing
          if (write_index >= half_delay_length_samples) buffer_filled = true;
          if (buffer_filled) *block_pointer = sample_delay_line[read_index--];
    
          // wrap round the write index
          if (write_index >= max_delay_length_samples) write_index = 0;
    
          // wrap round the read index
          if (read_index == 0) read_index = max_delay_length_samples;
    
          // move the block_pointer on
          block_pointer++;
        }
        // transmit the block out on channel 0
        transmit(block, 0);
        release(block);
      }
    }
    Just got a teensy 4.0 and sound board, built it in a small cabinet with inputs,outputs,6 pots and 9 buttons and an oled i2c display.
    Your program is one of the first i tried and it's exactly what i imagined it could be. Cant wait to do some mods.

  11. #11
    Senior Member houtson's Avatar
    Join Date
    Aug 2015
    Location
    Scotland
    Posts
    135
    hi unawoo, 6 pots and 9 buttons sounds good - you can do a lot with that, have fun cheers, Paul

  12. #12
    Junior Member
    Join Date
    Nov 2018
    Posts
    15
    Quick note to say an extreme thanks to @houston == Paul (and MarkT & boxofrobots). I was able to get it to work and now integrating it into the final Halloween prop (a creepy doll). If I have time before the reveal to my husband...I may try to get the crossfading in. I have done some forum searches and wondering which ones of these yields success for that: "AudioEffectEnvelope" or "AudioEffectFade"?

    Once completed, I will post a short clip on this thread with a clever *thank you* message in reverse.

  13. #13
    Senior Member
    Join Date
    Jun 2018
    Posts
    135
    Once completed, I will post a short clip on this thread with a clever *thank you* message in reverse.
    Nice. Post a video of the creepy doll maybe? :-)

Posting Permissions

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