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

Thread: Understanding the Teensy Example Codes

  1. #1
    Junior Member
    Join Date
    Jun 2020
    Posts
    14

    Understanding the Teensy Example Codes

    hey guys,

    i was analysing the teensy 4.0 arduino audio examples yesterday. As you can see below, there are some waveforms implemented in this example and i would like to understand what's happening here:

    Code:
    case WAVEFORM_SQUARE:
      magnitude15 = signed_saturate_rshift(magnitude, 16, 1);
      for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
       if (ph & 0x80000000) {
        *bp++ = -magnitude15;
       } else {
        *bp++ = magnitude15;
       }
       ph += inc;
      }
      break;
    
    
     case WAVEFORM_SAWTOOTH:
      for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
       *bp++ = signed_multiply_32x16t(magnitude, ph);
       ph += inc;
      }
      break;
    I really don't get how those signed_multiply_ / signed_shift_ functions work... Which value has *bp after every process?

    Is there a possibility to avoid those functions? I want to write my own sawtooth synth code with my own increment function and adding those values to the *bp series, but so far i failed terribly...

  2. #2
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    12,663
    ... have never looked into this before ... but it looks like ::

    Audio system works on a block of data fed through the system - the block is prepared as needed in 'advance' and then the system clocks through it, when the block is exhausted it has to 'create' the next block of data to 'continuously' present the desired WAVE output.

    This is the data block creation phase for the continuation of output of the desired waveform where bp is the data in the next block:
    Code:
    	block = allocate();
    ...
    	bp = block->data;
    The multiply appears to be one of the magical low level DSP instructions that does a number of things at once:
    Code:
    // computes ((a[31:0] * b[31:16]) >> 16)
    static inline int32_t signed_multiply_32x16t(int32_t a, uint32_t b)
    {
    	int32_t out;
    	asm volatile("smulwt %0, %1, %2" : "=r" (out) : "r" (a), "r" (b));
    	return out;
    }
    In a SINGLE instruction it does:
    SMULWT R4, R5, R3 ; Multiplies R5 with the top halfword of R3, extracts top 32 bits and writes to R4
    The multiply is needed to step the given 'magnitude' value through the 'ph==phase' as the block of data it stepped through by the audio system.

    That 'magical' instruction does not exist on T_LC so the same code to do that multiple in removed "#ifdef"'s above would be:
    Code:
    static inline int32_t signed_multiply_32x16t(int32_t a, uint32_t b)
    {
    	return ((int64_t)a * (int16_t)(b >> 16)) >> 16;
    }
    Where the T_LC has to jump to 64 bit integer to preserve the high order bits in the multiply, and do two other shifts in the process to get the same result.

    Those 'magical' DSP instructions are what Paul uses throughout the Audio system to make it work so efficiently as it does :: ...\hardware\teensy\avr\libraries\Audio\utility\ds pinst.h

  3. #3
    Senior Member
    Join Date
    Jul 2014
    Posts
    2,891
    Quote Originally Posted by defragster View Post
    Those 'magical' DSP instructions are what Paul uses throughout the Audio system to make it work so efficiently as it does :: ...\hardware\teensy\avr\libraries\Audio\utility\ds pinst.h
    I would like to add, these dsp instructions are most efficient for 16 bit audio, that is why the audio-library expects 16 bit data.
    32-bit, or float audio is possible with other libraries.

  4. #4
    Junior Member
    Join Date
    Jun 2020
    Posts
    14
    Dear defragster, dear WMXZ,

    thanks for Your answers so far! I've checked out the Assembler Code, You were posting, aswell.

    Code:
    // computes ((a[31:0] * b[31:16]) >> 16)
    static inline int32_t signed_multiply_32x16t(int32_t a, uint32_t b)
    {
    	int32_t out;
    	asm volatile("smulwt %0, %1, %2" : "=r" (out) : "r" (a), "r" (b));
    	return out;
    }
    as pointed out in the top comment, it does: compute ((a[31:0] * b[31:16]) >> 16). But I was wondering if i could write my own oscillator code, where I would generate those "compute ((a[31:0] * b[31:16]) >> 16) values" by myself?



    If there would be something like:

    Code:
     for (i=0;i<AUDIO_SAMPLE_RATE;i++) {...}
    Which values for every i++ should my code write into the final audio-block pointer? Or maybe i could ask a better question:
    If there would be a 16 bit sawtooth audio-block signal, which values are transmitted for every i++?

  5. #5
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    12,663
    AFAIK - Others have done custom waveforms and other things during the code like:
    Code:
     case WAVEFORM_SAWTOOTH:
      for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
       *bp++ = signed_multiply_32x16t(magnitude, ph);
       ph += inc;
      }
      break;
    Indeed - there is a way to write compliant alternate Audio library code- though again beyond what I've ever looked into.

    Whatever ends up in that bp = block->data; memory is what will be heard.

  6. #6
    Junior Member
    Join Date
    Jun 2020
    Posts
    14
    If we look at this segment of code they used for the WAVEFORM_SAWTOOTH:

    Code:
    case WAVEFORM_SAWTOOTH:
      for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
       *bp++ = signed_multiply_32x16t(magnitude, ph);
       ph += inc;
      }
      break;


    magnitude is a constant value which is coming from the 16 bit audio block and it is 65536, because a 16-bit integer can store 65536 distinct values.

    So I was wondering, could you, instead of using this signed_mupltiply function, write something like:

    Code:
    case WAVEFORM_SAWTOOTH:
      for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
       *bp++ = ph;
       ph += inc;
      }
      break;

    I understand when you guys are telling me that the assembler code is working faster for audio, but i really don't get how I would check those *bp values if they are correct...

  7. #7
    Senior Member
    Join Date
    Jul 2020
    Posts
    463
    Yes.

    However this is not a band-limited sawtooth so it will have lots of spectral artifacts, but the standard Audio lib waveform
    generator has the same flaw. This thread might be interesting: https://forum.pjrc.com/threads/61269...-with-aliasing
    or this one: https://forum.pjrc.com/threads/62240...ited-hard-sync

  8. #8
    Junior Member
    Join Date
    Jun 2020
    Posts
    14
    Hello MarkT,

    thank You for Your reply
    I wanted to check out Your sawtooth synth, but it seems the link that You've posted in Your thread is not working anymore:
    https://github.com/MarkTillotson/Aud...quare_sawtooth

    Would be extremely interested to see some more of Your projects!

    Wishing You guys all the best,
    Mark

Posting Permissions

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