Forum Rule: Always post complete source code & details to reproduce any issue!
Page 3 of 4 FirstFirst 1 2 3 4 LastLast
Results 51 to 75 of 78

Thread: Discussion about a simple way to change the sample-rate

  1. #51
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    5,679
    I was referring to the values in the big table in my code - these sets the dividers for I2S, and are depended of F_CPU/Better F_PLL) - the speed, the teensy runs at.

  2. #52
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    9,685
    Good to know - that link to Kinetis.h does show how the PLL tracks the F_CPU, but F_BUS wouldn't apply then - unless it speeds up some other element in the system to allow the I2S to not be delayed.

  3. #53
    Junior Member
    Join Date
    Nov 2015
    Posts
    15
    Quote Originally Posted by Frank B View Post
    Here's the same for the internal DACs :

    Code:
    void setDACFreq(int freq) {
    const unsigned config = PDB_SC_TRGSEL(15) | PDB_SC_PDBEN | PDB_SC_CONT | PDB_SC_PDBIE | PDB_SC_DMAEN;
        PDB0_IDLY = 1;
        PDB0_MOD = round((float)F_BUS / freq ) - 1;    
        PDB0_SC = config | PDB_SC_LDOK;
        PDB0_SC = config | PDB_SC_SWTRIG;
        PDB0_CH0C1 = 0x0101;    
    }
    Ha, this is exactly what I was working on in this thread. As usual, your solution is much better than mine (run-time adjustments to AUDIO_SAMPLE_RATE). Your solution works flawlessly, and it warps the audio if the sample rates don't match, like I was looking for. There have a been a number of threads around the web and this forum asking how to do this, so I just want to put in some keywords to help people find this later.


    change tempo, change playback speed, speed up a song, slow down a song, use other sample rates, pitch shifting, change audio speed, tempo changer

  4. #54
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    5,679
    Glad, that it works for you

  5. #55
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,154
    Executive Summary: The problem of volume control not working (at least at 8000) appears to have been caused by the use of the Wire library. If you change the include of Wire.h to i2c_t3.h in the four files listed by Michael in this message the volume control will work at 8000Hz.

    I have, quite by accident, found a cure for the loss of volume control when using a sampling rate of 8000. I haven't tested the other rates yet. I've been working on a sketch which detects and decodes the AFSK data (Bell 103) transmitted by the Canadian time station CHU and had got it printing out dates/times that it decoded successfully. The next step was to add code to support a DS3231 RTC so that I could set the RTC from the CHU information. To do that I had to add the Wire library and then I ran into problems. The first was that the audio board uses I2C0 to control the SGTL5000 so I had to switch to i2c_t3 so that I could switch the RTC to use I2C1. Then I ran into a compiler problem similar to that here. Michael's suggested fix in msg #2 there solved my compiler problems [Many thanks Michael!] but to my surprise the volume control also started working again while I had the sampling rate at 8000Hz.

    Pete

  6. #56
    First - thanks Frank B for doing the variable rate.

    I am using the T3.6 with the SGTL5000. I have two interests, one is the Audio VNA and the other is communications "circuits." Both of these need quiet ADC floors, but not necessarily across the full BW. I ran across an interesting test case that I wanted to report for comments. The T3.6 runs with F_PLL=180000000. For this case, one can move up slightly from a 96 kHz sample rate to 100kHz. It turns out that a number of combinations of mult and div produce exactly 100Khz. I picked two extremes and plotted the ADC spectrum. The first, {32, 225} gave a good spectrum that looked much like previous 96 kHz results that used much bigger numbers, {219, 1604}. Here is the 100 kHz plot:
    Click image for larger version. 

Name:	FFTScreen100KHz32_225.gif 
Views:	135 
Size:	36.4 KB 
ID:	9477
    I next picked the largest mult and div that still produced exactly 100kHz, which is {224, 1575} and not surprisingly they are both 7 times the lowest numbers. The non-signal ADC spectrum is quite different:
    Click image for larger version. 

Name:	FFTScreen100KHz224_1575.gif 
Views:	114 
Size:	35.2 KB 
ID:	9478
    There is a lot of spreading of the spurs, something that has been seen with other mult-div combinations. This would seem to be a lot more than just part-cycle clock jitter. Ignoring the causes, it would seem to be a feature that could be added or removed, depending on the application. The {32,225} case has five or more dB less noise between the spikes, but the {224,1575} case has perhaps 20 dB lower spikes in the upper regions, above 22 kHz. For communications applications, it may be that the region 22 to 42 kHz is a good i-f frequency, if that is desired.

    In any event, it would seem to be worth looking at this kind of spectra as part of system design. Also, more info is available on these measurements in a different thread.

    Finally, I found it useful to modify the return to the sample rate subroutine to report back the exact sample frequency, for altering other calls:
    Code:
    return  (double)F_PLL * (double)clkArr[f].mult / (256.0 * (double)clkArr[f].div);
    Of course, the function is typed double.

    Bob

  7. #57
    Senior Member
    Join Date
    Oct 2015
    Location
    Vermont, USA
    Posts
    189
    I've been trying to FrankB's example code to slow down my SGTL5000 (Teensy Audio Board). I'm running it at 22050. When I'm synthesizing a sign wave using the Audio library, I clearly hear that the output is lower in pitch that it was at the normal I2S rate, so it's clearly doing something like what I'd expect.

    What surprises me is that the AudioProcessorUsage() doesn't change at all (I've got lots of other processing going on as well). By running everything at half the sample rate, I would have expected to have way more time to get work done. Yet, the processor load doesn't seem to be affected.

    Thinking that it was just how the AudioProcessorUsage() macro was defined, I looked and saw that it had no idea what the I2S rate was doing to the sample rate. So, if I halved the sample rate (and the Teensy didn't know about it), I would have expected to go to CPU loads well in excess of 100%...perhaps even near 200%.

    Unfortunately, this is not the case. It chokes and dies near 100%, just like when it's at the default I2S rate.

    What's happening? Why don't I get more time to do processing?

    Chip

  8. #58
    Senior Member DD4WH's Avatar
    Join Date
    Oct 2015
    Location
    Central Europe
    Posts
    448
    However, when not using the audio lib except for a queue-in and queue-out object (which I do with the Teensy Convolution SDR), the sample rate affects the time available for processing as expected (half the sample rate doubles the time available for processing until the next block of sample arrives).

  9. #59
    Senior Member
    Join Date
    Oct 2015
    Location
    Vermont, USA
    Posts
    189
    Quote Originally Posted by DD4WH View Post
    However, when not using the audio lib except for a queue-in and queue-out object (which I do with the Teensy Convolution SDR), the sample rate affects the time available for processing as expected (half the sample rate doubles the time available for processing until the next block of sample arrives).
    So you're suggesting that if I *am* processing within the audio lib (I have my own classes that plug in via AudioConnection) I do *not* get more processing time even though the sample rate is slower? Why not? Is there something I can do about that?

    Or, alternatively, is there a better way to change the code to change the sample rate for real, rather than just fooling it by changing the I2S speed? Changes in AudioStream.h or something?

    Chip

  10. #60
    Chip, in my observations it sure seems like all audio processing is being tied to the sample rate. I added the external 10 KHz signal to check that.

    On the light side, if the processing load doesn't go down at 22050, maybe it won't go up at 96 KHz---we'll take that

    Bob

  11. #61
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    5,679
    Quote Originally Posted by chipaudette View Post
    Or, alternatively, is there a better way to change the code to change the sample rate for real, rather than just fooling it by changing the I2S speed? Changes in AudioStream.h or something?

    Chip
    It changes it for real.
    What does NOT change, is the time to process an audio-block, of course. It has the same size, so changing the samplerate has no influence, of course (but there are ways to change this, too).
    What DOES change, is the number of processed blocks per second (the half, if you choose the half sample-rate) - so "long-term" (i.e. a second), the cpu-usage changes.

    I did'nt bother with the measured numbers of "audioprocessorusage" - maybe they are wrong ? I know that it measures the cpu-cycles per block (might be, that it does not take the number of blocks per second into account?) , but for more details, i have to dig into the code. At the moment, i have other interests ;-) It's somewhere in audiostream.h

    But anyway, it was'nt the goal to change the displayed cpu-usage.. if you want this, maybe it's better to search for your own, better, solution. I guess, a combination of a) reducing the samplerate PLUS b) reducing the blocksize will do what you want. I worked on b), too, it's easy now for you, just edit the #define. (perhaps with my "defs.h" "hack")

    edit: halving the blocksize means doubling the blocks per second, since the amount of data stays the same - unless you reduce the sample-rate, too.
    Last edited by Frank B; 01-25-2017 at 06:16 PM.

  12. #62
    Senior Member bmillier's Avatar
    Join Date
    Apr 2016
    Location
    Halifax, N.S. Canada
    Posts
    156
    @Bob Larkin. Your results using the various divider values is interesting. Since the Teensy I2S MCLK signal is being generated by a fractional-N divider, I assume there is no way to completely eliminate the jitter that such dividers produce when not dividing by an exact integer. I am guessing that this is what is causing those spurs. I was wondering though if it might be worth investigating one or two alternatives that might eliminate this.
    1) The Teensy I2S MCLK can be fed from an external clock source,instead of generated by it's internal fractional-N divider. The 96 KHz sample rate would need a 24.576 MHz clock, and you can easily get crystal osc. modules at this freq. for a few dollars. This would provide the SGTL5000 with a jitter-free MCLK signal.(The 100 KHz SR needs 25.600 MHz: this freq osc. is not so easy to find.)
    2) The SGTL5000 has its own internal PLL which can be locked to an 8-27 MHZ signal coming in on its MCK pin. It runs at 196.608 MHz for the 96 KHz S.rate. The SGTL5000 is not set up to use it by the normal Audio library- instead the Teensy feeds it the actual 256 x S.Rate on the MCLK line. If you fed the SGTL5000 with a jitter-free clock, coming from one of the Teensy's integer counter/timers, you will still have to use the fractional-N divider within the SGTL5000's PLL block to achieve this 196.608 MHz clock. But, the analog nature of the PLL might smooth out the jitter from its own fractional-N divider, and eliminate the ADC spurs you are seeing ??
    I was also curious to see whether adopting a 102.4 KHz sample rate (good match for a 1024 FFT, yielding 100 Hz/bin) might lead to an MCLK frequency that could easily be produced by an integer divider off the Teensy F_CLK, but this doesn't divide out evenly either.
    Brian

  13. #63
    Senior Member
    Join Date
    Oct 2015
    Location
    Vermont, USA
    Posts
    189
    Quote Originally Posted by Frank B View Post
    It changes it for real.
    Sorry if offense was given. That wasn't my intention.

    By "for real", I meant that the rest of the Audio library would know that the sample rate had changed. With the example code given, the sample rate is changed, but the Audio Library objects know about it (as is freely admittted in the post). In my notion of "for real", the rest of the Audio Library *would* know about the change in rate. As a result, the sine wave synthesis, tone sweep, and other functions would make tones at the correct (asked-for) frequency.

    It appears that this is only possible by editing AudioStream.h (https://github.com/PaulStoffregen/co.../AudioStream.h), which is a bummer. I hoped that there was a way to do it programmatically from one's sketch without altering underlying library files. Unless there's some pre-processor directive that I can use? (I'm no master of the C preprocessor, that's for sure).

    Even if there is such a pre-processor command, it still would define the sample rate at compile time and not at runtime. Runtime gives more flexibility for wacky effects. But, if I really want that, I should just dive in and make it happen, right? That's why it's open source, right? (Such deep plumbing is hard to do well, sadly...and my architecting skills are not up to making a *good*, general solution)


    BTW, I repeated my sample rate experiments (per the new dividers provided earlier) and whatever problem that I was having has gone away. Switching to a slower sample rate definitely gives more time (per sample) to do calculations, as one would expect. Therefore, my audio processing that wasn't quite fast enough at 44100 Hz is easily achieved at 22050 (which is all I need). Great! Also, since I really wanted 24000, I expanded the table to include 24000 (same denominator as 48000, but I cut the numerator in half). Having this example code really made it easy to get the sample rate that I wanted. Thanks so much!

    Chip
    Last edited by chipaudette; 01-27-2017 at 06:13 PM.

  14. #64
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    5,679
    Quote Originally Posted by chipaudette View Post

    By "for real", I meant that the rest of the Audio library would know that the sample rate had changed.
    Agreed - yes, would be better.
    At compile-time, this needs not too many changes and is doable.
    Testing needs a bit more time.

  15. #65
    Brian, those would be interesting experiments, such as using an external clock. Such things always seem to add insight worthwhile rates. Maybe somebody wants to pursue that, but I won't for a while, as I have my needs met for the VNA, and I need to finish that.

    BUT, based on the experiments, so far, I believe the square wave is directly associated with the SGTL5000 ADC. In part, that is from observing that the base rate of the square wave is always 1/24 of the sample rate, no matter what the mult-div combination is. The spreading of the higher harmonics is associated with the mult-div combination, though. That would seem to be a jitter issue. Or, maybe I got it wrong!

    Bob

  16. #66
    Senior Member
    Join Date
    Oct 2015
    Location
    Vermont, USA
    Posts
    189
    I've had great luck using Frank B's solution in #5. But, I did have a question about the particular MULT and DIV factors shown in the solution. Specifically, don't some of the factors exceed the values allowed by the I2S registers?

    Being interested mostly in the Teensy 3.6, I'm looking at the K66 technical manual: http://www.nxp.com/assets/documents/...180SF5RMV2.pdf

    If you jump to section "61.4.20 SAI MCLK Divide Register (I2Sx_MDR)" it shows how to configure the register where the MULT and DIV factors are passed to the hardware. They call these values "FRACT" and "DIVIDE", but they appear to be the same values defined as "MULT" and "DIV" in Frank B's example code.

    What catches my eye is that DIVIDE is only 11-bits, implying a range of 0-2047. Frank B's example code has DIV factors tat are over 3000.

    Do the 3000+ DIV values work correctly? Do you get the sample rate that you want?

    [If they do work correctly, I'm clearly misunderstanding the K66 tech docs, which is really the purpose for my question...I'm trying to understand the tech docs.]

    Thanks,

    Chip

  17. #67
    Junior Member
    Join Date
    Aug 2017
    Posts
    19
    Hello Guys, Im trying this code to change the sampling rate to 48k but Im receiving the following errors:

    T3.6

    In function 'void setI2SFreq(int)':

    error: narrowing conversion of '4.41176484e+4f' from 'float' to 'int' inside { } [-Wnarrowing]

    const int samplefreqs[numfreqs] = { 8000, 11025, 16000, 22050, 32000, 44100, 44117.64706 , 48000, 88200, 44117.64706 * 2, 96000, 176400, 44117.64706 * 4, 192000};

    ^
    error: narrowing conversion of '8.82352969e+4f' from 'float' to 'int' inside { } [-Wnarrowing]

    narrowing conversion of '1.76470594e+5f' from 'float' to 'int' inside { } [-Wnarrowing]


    Does anybody know how to fix this and why it is happening?

    Thanks



    Code:
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <SerialFlash.h>
    
    
    
    void setI2SFreq(int freq) {
      typedef struct {
        uint8_t mult;
        uint16_t div;
      } __attribute__((__packed__)) tmclk;
      const int numfreqs = 14;
      const int samplefreqs[numfreqs] = { 8000, 11025, 16000, 22050, 32000, 44100, 44117.64706 , 48000, 88200, 44117.64706 * 2, 96000, 176400, 44117.64706 * 4, 192000};
    
    #if (F_PLL==16000000)
      const tmclk clkArr[numfreqs] = {{16, 125}, {148, 839}, {32, 125}, {145, 411}, {64, 125}, {151, 214}, {12, 17}, {96, 125}, {151, 107}, {24, 17}, {192, 125}, {127, 45}, {48, 17}, {255, 83} };
    #elif (F_PLL==72000000)
      const tmclk clkArr[numfreqs] = {{32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {128, 1125}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375}, {249, 397}, {32, 51}, {185, 271} };
    #elif (F_PLL==96000000)
      const tmclk clkArr[numfreqs] = {{8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {32, 375}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125}, {151, 321}, {8, 17}, {64, 125} };
    #elif (F_PLL==120000000)
      const tmclk clkArr[numfreqs] = {{32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {128, 1875}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625}, {178, 473}, {32, 85}, {145, 354} };
    #elif (F_PLL==144000000)
      const tmclk clkArr[numfreqs] = {{16, 1125}, {49, 2500}, {32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {4, 51}, {32, 375}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375} };
    #elif (F_PLL==180000000)
      const tmclk clkArr[numfreqs] = {{46, 4043}, {49, 3125}, {73, 3208}, {98, 3125}, {183, 4021}, {196, 3125}, {16, 255}, {128, 1875}, {107, 853}, {32, 255}, {219, 1604}, {214, 853}, {64, 255}, {219, 802} };
    #elif (F_PLL==192000000)
      const tmclk clkArr[numfreqs] = {{4, 375}, {37, 2517}, {8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {1, 17}, {8, 125}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125} };
    #elif (F_PLL==216000000)
      const tmclk clkArr[numfreqs] = {{32, 3375}, {49, 3750}, {64, 3375}, {49, 1875}, {128, 3375}, {98, 1875}, {8, 153}, {64, 1125}, {196, 1875}, {16, 153}, {128, 1125}, {226, 1081}, {32, 153}, {147, 646} };
    #elif (F_PLL==240000000)
      const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} };
    #endif
    
      for (int f = 0; f < numfreqs; f++) {
        if ( freq == samplefreqs[f] ) {
          while (I2S0_MCR & I2S_MCR_DUF) ;
          I2S0_MDR = I2S_MDR_FRACT((clkArr[f].mult - 1)) | I2S_MDR_DIVIDE((clkArr[f].div - 1));
          return;
        }
      }
    }
    
    
    // GUItool: begin automatically generated code
    AudioInputI2S            i2s1;           //xy=168,145
    AudioRecordQueue         queue1;         //xy=360,62
    AudioRecordQueue         queue2;         //xy=389,145
    AudioConnection          patchCord1(i2s1, 0, queue1, 0);
    AudioConnection          patchCord2(i2s1, 1, queue2, 0);
    // GUItool: end automatically generated code
    
    AudioControlSGTL5000     sgtl5000_1;     //xy=265,212
    
    
    // which input on the audio shield will be used?
    //const int myInput = AUDIO_INPUT_LINEIN;
    const int myInput = AUDIO_INPUT_MIC;
    
    
    // Use these with the Teensy Audio Shield
    #define SDCARD_CS_PIN    10
    #define SDCARD_MOSI_PIN  7
    #define SDCARD_SCK_PIN   14
    
    // Use these with the Teensy 3.5 & 3.6 SD card
    //#define SDCARD_CS_PIN    BUILTIN_SDCARD // 254?
    //#define SDCARD_MOSI_PIN  11  // not actually used
    //#define SDCARD_SCK_PIN   13  // not actually used
    
    // Remember which mode we're doing
    int mode = 0;  // 0=stopped, 1=recording, 2=playing
    
    
    
    // The file where data is recorded
    File frec;
    
    void setup() {
      // record queue uses this memory to buffer incoming audio.
      AudioMemory(120); // 60
      const int samplefreq = 48000;
      // Enable the audio shield, select input, and enable output
      sgtl5000_1.enable();
      sgtl5000_1.inputSelect(myInput);
      sgtl5000_1.volume(0.5);
      setI2SFreq(samplefreq);
      
      // Initialize the SD card
      SPI.setMOSI(SDCARD_MOSI_PIN);
      SPI.setSCK(SDCARD_SCK_PIN);
      if (!(SD.begin(SDCARD_CS_PIN))) {
        // stop here if no SD card, but print a message
        while (1) {
          Serial.println("Unable to access the SD card");
          delay(500);
        }
      }
    
      startRecording();
    }
    
    
    void loop() {
      if (millis() > (0.2*60000) && mode == 1) {
        stopRecording();
      }
      else {
        if (mode == 1) continueRecording();
      }
    }
    
    void startRecording() {
      Serial.println("StartRecording");
      if (SD.exists("RECORD.RAW")) {
        SD.remove("RECORD.RAW");
      }
      frec = SD.open("RECORD.RAW", FILE_WRITE);
      if (frec) {
        Serial.println("File Open");
        queue1.begin();
        queue2.begin();
        mode = 1;
      }
    
    }
    
    // write all 512 bytes to the SD card   
    void continueRecording() {
      if (queue1.available() >= 2 && queue2.available() >= 2) {
        byte buffer[1024];
        byte bufferL[256];
        byte bufferR[256];
        memcpy(bufferL, queue1.readBuffer(), 256);
        memcpy(bufferR, queue2.readBuffer(), 256);
        queue1.freeBuffer();
        queue2.freeBuffer();
        int b = 0;
        for (int i = 0; i < 1024; i += 8) {
          buffer[i] = bufferL[b];
          buffer[i + 1] = bufferL[b + 1];
          buffer[i + 2] = bufferR[b];
          buffer[i + 3] = bufferR[b + 1];
          buffer[i+4] = bufferL[b];
          buffer[i + 5] = bufferL[b + 1];
          buffer[i + 6] = bufferR[b];
          buffer[i + 7] = bufferR[b + 1];
          b = b+2;
        }
        elapsedMicros usec = 0;
        frec.write(buffer, 1024);  //256 or 512 (dudes code)
        Serial.print("SD write, us=");
        Serial.println(usec);
      }
    }
    
    void stopRecording() {
      Serial.println("StopRecording");
      queue1.end();
      queue2.end();
      // flush buffer
      while (queue1.available() > 0 && queue2.available() > 0) {
        queue1.readBuffer();
        queue1.freeBuffer();
        queue2.readBuffer();
        queue2.freeBuffer();
      }
      frec.close(); // close file
      mode = 4;
    }
    Last edited by arturzorzo; 06-11-2018 at 04:24 PM.

  18. #68
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,154
    An integer quantity is being initialized to a floating point value. I think this used to be just a warning that the value would be truncated.
    Change "44117.64706 * 2" to "(int)(44117.64706 * 2 + 0.5)"
    and change "44117.64706 * 4" to "(int)(44117.64706 * 4 + 0.5)"

    Pete

  19. #69
    You can change the values in samplefreq[] vector that are floats, like 44117.64706 to just 44117. Then if you want that speed, call setI2SFreq[44117]. It is just used to find the right entry in the table. This works at all because int in the Teensy 3.x ARM processor is 32 bit.

    Here is an example where the values were explicitly made int. It also has a restricted selection of speeds, but that depends on the project. This is from the AVNA (Network Analyzer) project:
    Code:
    /* Variable sample rate from Frank B., see
       https://forum.pjrc.com/threads/38753-Discussion-about-a-simple-way-to-change-the-sample-rate
       As pointed out by Frank, the various routines, like waveform generator, are calibrated
       for 44.11765 and need to be corrected when other sample rates. See factorFreq below.
    
       K12 Peripheral manual:
       MCLD Divide sets the MCLK divide ratio such that: MCLK output = MCLK input * ( (FRACT + 1) / (DIVIDE + 1) ).
       FRACT must be set equal or less than the value in the DIVIDE field.
       The MCLK divide ratio
       can be altered while an SAI is using that master clock, although the change in the divide
       ratio takes several cycles. MCR[DUF] can be polled to determine when the divide ratio
       change has completed.
    */
    const uint16_t numFreqs = 5;
    // sampleFreqs[] is of limited utility, as the index nSampleRate describes the selection,
    // and sampleRateExact is the true rate, sometimes slightly different.
    const int sampleFreqs[numFreqs] = {44100, 44117, 48000, 96000, 100000};
    // Note Teensy 3.6:  F_CPU == 180000000, F_PLL == 180000000
    // setI2SFreq(if) returns exact sample frequency, that may differ very slightly from sampleFreqs[]
    double setI2SFreq(uint16_t iFreq)
    {
      typedef struct
      {
        uint8_t mult;
        uint16_t div;
      } __attribute__((__packed__)) tmclk;
      // 44117 is nickname for 44117.64706
    #if   (F_PLL==72000000)
      const tmclk clkArr[numFreqs] = {{98, 625}, {8, 51}, {64, 375}, {128, 375}, {16, 45} };
    #elif (F_PLL==96000000)
      const tmclk clkArr[numFreqs] = {{147, 1250}, {2, 17}, {16, 125}, {32, 125}, {20, 75} };
    #elif (F_PLL==120000000)
      const tmclk clkArr[numFreqs] = {{205, 2179}, {8, 85}, {64, 625}, {128, 625}, {16,75} };
    #elif (F_PLL==144000000)
      const tmclk clkArr[numFreqs] = {{49, 625}, {4, 51}, {32, 375}, {64, 375}, {8, 45} };
    #elif (F_PLL==16000000)
      const tmclk clkArr[numFreqs] = {{151, 214}, {12, 17}, {96, 125}, {192, 125}, {20,125} };
    #elif (F_PLL==168000000)
      const tmclk clkArr[numFreqs] = {{42, 625}, {8, 119}, {64, 875}, {128, 875}, {16, 185} };
    #elif (F_PLL==180000000)
      const tmclk clkArr[numFreqs] = {{196, 3125}, {16, 255}, {128, 1875}, {219, 1604}, {32, 225} };
    #elif (F_PLL==192000000)
      const tmclk clkArr[numFreqs] = {{147, 2500}, {1, 17}, {8, 125}, {16, 125}, {2, 15} };
    #elif (F_PLL==216000000)
      const tmclk clkArr[numFreqs] = {{98, 1875}, {8, 153}, {64, 1125}, {128, 1125}, {16, 135} };
    #elif (F_PLL==240000000)
      const tmclk clkArr[numfreqs] = {{147, 3125}, {4, 85}, {32, 625}, {64, 625}, {8, 75} };
    #endif
      while (I2S0_MCR & I2S_MCR_DUF) ;  // This is to make sure I2S controller is up to speed NEEDED??
      I2S0_MDR = I2S_MDR_FRACT((clkArr[iFreq].mult - 1)) | I2S_MDR_DIVIDE((clkArr[iFreq].div - 1));
      return  (double)F_PLL * (double)clkArr[iFreq].mult / (256.0 * (double)clkArr[iFreq].div);
    }
    Note, BTW, the slow speeds, below 44.1 are prone to spurs and noise, and faster sampling and decimation can give better results unless you are in the unusual case of not having enough processor.

  20. #70
    Senior Member MickMad's Avatar
    Join Date
    Feb 2013
    Location
    Italy
    Posts
    163
    Quote Originally Posted by bmillier View Post
    1) The Teensy I2S MCLK can be fed from an external clock source,instead of generated by it's internal fractional-N divider. The 96 KHz sample rate would need a 24.576 MHz clock, and you can easily get crystal osc. modules at this freq. for a few dollars. This would provide the SGTL5000 with a jitter-free MCLK signal.(The 100 KHz SR needs 25.600 MHz: this freq osc. is not so easy to find.)
    Unfortunately, the technical datasheet (not the manual) says at page 18 that the max I2S MCLK frequency is 12.5 MHz.

    Click image for larger version. 

Name:	forum 1.png 
Views:	50 
Size:	40.1 KB 
ID:	14111

    At page 66 it also says that the "I2S MCLK cycle time" in master mode is 40 ns, which equals to 25 MHz; I don't know if the datasheet is wrong (and indeed the MCLK can accept up to 25 MHz), or if "cycle time" means half a period (confirming the 12.5 MHz max freq. stated before).

    BUT, if you keep looking at the tech datasheet, page 67 shows a timing diagram for I2S Slave operation and there's no MCLK in it (unlike the timing diagram for the Master operation in the previous page).

    Click image for larger version. 

Name:	forum 2.png 
Views:	40 
Size:	21.1 KB 
ID:	14112

    So, unless I got this wrong, it could be possible to generate BCLK and FS signals from a CODEC that has an internal PLL (like the SGTL) and just ditch the MCLK signal altogether. I can't find explicit reference to this in the K66 manual, but it seems plausible; the diagram at page 2139 of the manual shows that the MCLK becomes useless when the Bit Clock is externally generated.

    Click image for larger version. 

Name:	forum 3.png 
Views:	41 
Size:	18.2 KB 
ID:	14113

  21. #71
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,556
    I can confirm when Teensy runs in I2S slave mode, no MCLK signal is needed, as far as Teensy is concerned.

  22. #72
    Senior Member MickMad's Avatar
    Join Date
    Feb 2013
    Location
    Italy
    Posts
    163
    Quote Originally Posted by PaulStoffregen View Post
    I can confirm when Teensy runs in I2S slave mode, no MCLK signal is needed, as far as Teensy is concerned.
    This is very good news they make clocks that are very small, in classic 4 pin SMD crystal packages, that require no more than a pullup and a 100nF cap to work reliably. As long as the I2S chip has an internal PLL, this solution could be used for jitter-free clocking.

  23. #73
    Junior Member
    Join Date
    Jun 2018
    Posts
    11
    Regarding what components behave different when setting another sample rate this way, the sine generator was mentioned earlier. Also the AudioEffectDelay
    behaves different, because it uses AUDIO_SAMPLE_RATE_EXACT in its code.

    Koen

  24. #74
    Senior Member MickMad's Avatar
    Join Date
    Feb 2013
    Location
    Italy
    Posts
    163
    Quote Originally Posted by koen View Post
    Regarding what components behave different when setting another sample rate this way, the sine generator was mentioned earlier. Also the AudioEffectDelay
    behaves different, because it uses AUDIO_SAMPLE_RATE_EXACT in its code.

    Koen
    https://github.com/PaulStoffregen/Au...PLE_RATE_EXACT

    This query gives you all the files that contain the words AUDIO_SAMPLE_RATE_EXACT.

    After going thru the 3 pages list of results, these are the names of the files that are affected in one way or another by the sample rate value:

    synth_sine
    synth_pwm
    filter_variable
    synth_simple_drum
    synth_tonesweep
    effect_granular
    filter_biquad
    synth_waveform
    analyze_tonedetect
    play_memory
    effect_delay_ext
    effect_bitcrusher
    synth_karplusstrong
    play_serialflash_raw
    effect_flange
    play_sd_raw
    synth_dc
    effect_delay
    effect_envelope
    play_sd_wav
    analyze_notefreq
    output_dac

    This does not include the USB Audio stack, which of course should change accordingly.

  25. #75
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    5,679
    For reference, this is a fast way to calculate the fract/div without table:

    https://github.com/FrankBoesing/Audi...iming.cpp#L176

    Thanks to WMXZ!
    Last edited by Frank B; 10-14-2018 at 11:39 AM.

Posting Permissions

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