Forum Rule: Always post complete source code & details to reproduce any issue!
Page 1 of 2 1 2 LastLast
Results 1 to 25 of 30

Thread: Audio Sample Rate: how to change from 44100 Hz (44117.65 Hz) to 48000?

  1. #1
    Member Revalogics's Avatar
    Join Date
    Dec 2016
    Location
    Philippines
    Posts
    78

    Audio Sample Rate: how to change from 44100 Hz (44117.65 Hz) to 48000?

    So I have made a sketch for Teensy 3.6 I have that uses a single ADC as an input and USB as an output with nothing connected between them. I tested it using Windows and it worked OK, aside from faint RF noises I'm hearing so I put a 40nF ceramic capacitor between ADC input and AGND. I tried it on Linux (Ubuntu 16.04) using JACK audio connection toolkit, a realtime low-latency audio driver, and it worked OK for the first few seconds and gets noisy and noisy as time goes by and after a while, noise comes down gradually, going to clean audio again. Maybe it is caused by the inaccuracy of the sample rate?

    I choose 48000 Hz because clock frequencies in Teensy can be divided and will arrive at 48000 accurately.

    Anyone knows how to change the sample rate, especially for ADC, USB, and any other peripherals involved?

    Code:
    #include <Audio.h>
    
    AudioInputAnalog adc1;
    AudioOutputUSB usb1;
    AudioConnection patchCord1(adc1, 0, usb1, 0);
    AudioConnection patchCord2(adc1, 0, usb1, 1);
    
    elapsedMillis fader13timer2;
    byte fader13timer = 0;
    byte fader13PWM = 0;
    boolean dir = 0;
    
    void setup() {
      AudioMemory(16);
      pinMode(13, OUTPUT);
      digitalWrite(13, HIGH);
    }
    
    void loop() {
      fader13();
    }
    
    void fader13() {
      if(fader13timer == 0) digitalWrite(13, 1);
      if(fader13timer >= fader13PWM) digitalWrite(13, 0);
      if(fader13timer2 >= 5) {
        fader13timer2 = 0;
        if(dir) {
          fader13PWM++;
          if(fader13PWM == 255) dir = 0;
        }
        if(!dir) {
          fader13PWM--;
          if(fader13PWM == 0) dir = 1;
        }
      }
      fader13timer++;
    }

  2. #2
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,108
    See this thread
    My code in message #44 in that thread has the setI2SFreq function (by @FrankB) which you can use to set the sampling frequency.
    I think it is the current version. In theory all you need is that function and then call it in setup().
    Code:
      setI2SFreq(48000);
    Pete

  3. #3
    Senior Member duff's Avatar
    Join Date
    Jan 2013
    Location
    Las Vegas
    Posts
    957
    Quote Originally Posted by Revalogics View Post
    I choose 48000 Hz because clock frequencies in Teensy can be divided and will arrive at 48000 accurately.

    Anyone knows how to change the sample rate, especially for ADC, USB, and any other peripherals involved?
    Though I haven't tried it you would need to change the PDB MOD register value in pdb.h (PDB_PERIOD). If you running at 48MHz bus then use (1000-1), there are probably many other things to change that I don't know about either especially with the USB audio interface.

  4. #4
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    5,659
    Yes, thats mentioned (and a piece of code is there, too) in the thread el-supremo mentioned.

  5. #5
    Senior Member
    Join Date
    Oct 2016
    Posts
    170
    This mod works fine but a question: How the bitcrusher object works now? The parameter hight parameter of freq is the setI2SFreq?

  6. #6
    Member Revalogics's Avatar
    Join Date
    Dec 2016
    Location
    Philippines
    Posts
    78
    Haven't used this mod anymore because I found a good USB Audio interface that replaces Teensy for my audio I/O needs.

    Looking at the sourcecode of bitcrusher, the bitcrusher.sampleRate(xsampleRate) function does the same thing, regardless of changes in sample rate using setI2Sfreq().
    However, lowering the frequency using setI2Sfreq() achieves the same thing as reducing the sample rate in bitcrusher.

    It's just my guess, maybe it works that way, maybe not.

  7. #7
    Raising this up again, because I am trying the same thing with USB audio. I have modified all the references to 44100 clock to 48000, and I have all of the audio library objects working at this new sample rate except the AudioOutputUSB.

    I have included the setI2SFreq(48000); code

    In usb_desc.c, I have modified the lines:
    LSB(44100), MSB(44100), 0, // tSamFreq

    In usb_dev.c, I have modified the line

    Click image for larger version. 

Name:	aaaa.PNG 
Views:	96 
Size:	11.7 KB 
ID:	11777

    and now the computer sets itself properly
    Click image for larger version. 

Name:	usbaudiorate.PNG 
Views:	51 
Size:	5.7 KB 
ID:	11776

    I am Running a Teensy 3.6.

    The computer is getting audio, but it sounds choppy and I suspect the sample rate is the issue.

  8. #8
    Senior Member
    Join Date
    Jul 2014
    Posts
    2,222
    Quote Originally Posted by roomtek View Post
    Raising this up again, because I am trying the same thing with USB audio. I have modified all the references to 44100 clock to 48000, and I have all of the audio library objects working at this new sample rate except the AudioOutputUSB.

    I have included the setI2SFreq(48000); code

    In usb_desc.c, I have modified the lines:
    LSB(44100), MSB(44100), 0, // tSamFreq

    In usb_dev.c, I have modified the line

    Click image for larger version. 

Name:	aaaa.PNG 
Views:	96 
Size:	11.7 KB 
ID:	11777

    and now the computer sets itself properly
    Click image for larger version. 

Name:	usbaudiorate.PNG 
Views:	51 
Size:	5.7 KB 
ID:	11776

    I am Running a Teensy 3.6.

    The computer is getting audio, but it sounds choppy and I suspect the sample rate is the issue.
    there are multiple places where you need to adjust
    not only in usb_desc.cpp, usb_dev.cpp but also in usb_audio.cpp (look for "// TODO: dynamic adjust to match USB rate")
    (I guess you must set target = 48)

  9. #9
    Quote Originally Posted by WMXZ View Post
    there are multiple places where you need to adjust
    not only in usb_desc.cpp, usb_dev.cpp but also in usb_audio.cpp (look for "// TODO: dynamic adjust to match USB rate")
    (I guess you must set target = 48)
    I did this:

    Code:
    // Called from the USB interrupt when ready to transmit another
    // isochronous packet.  If we place data into the transmit buffer,
    // the return is the number of bytes.  Otherwise, return 0 means
    // no data to transmit
    unsigned int usb_audio_transmit_callback(void)
    {
    	static uint32_t count=5;
    	uint32_t avail, num, target, offset, len=0;
    	audio_block_t *left, *right;
    
    	if (++count < 9) {   // TODO: dynamic adjust to match USB rate
    		target = 48;
    	} else {
    		count = 0;
    		target = 45;
    	}

    Changing this made it sound even more choppier.

  10. #10
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,171
    No, that's definitely wrong. You need to send 48 samples on EVERY frame, not just some of them.

    Just in case it wasn't clear, that code is responsible for sometimes sending 45 samples, but usually sending 44 samples. Done 1000 times per second, the net result turns out to be close to 44117 samples/sec.

    You also need to edit the max packet size in the descriptors. Even though to told it the sample rate in the audio descriptors, you also need to get that detail right so the host knows how big the arriving packets are supposed to be. It doesn't compute that for you, so you must put it in the endpoint descriptors too.

    If you're using the input object, you also need to deal with the sample rate feedback endpoint, to tell the host to slightly speed up or slow down, so you get the right amount of data arriving.

  11. #11
    Quote Originally Posted by PaulStoffregen View Post
    No, that's definitely wrong. You need to send 48 samples on EVERY frame, not just some of them.

    Just in case it wasn't clear, that code is responsible for sometimes sending 45 samples, but usually sending 44 samples. Done 1000 times per second, the net result turns out to be close to 44117 samples/sec.

    You also need to edit the max packet size in the descriptors. Even though to told it the sample rate in the audio descriptors, you also need to get that detail right so the host knows how big the arriving packets are supposed to be. It doesn't compute that for you, so you must put it in the endpoint descriptors too.

    If you're using the input object, you also need to deal with the sample rate feedback endpoint, to tell the host to slightly speed up or slow down, so you get the right amount of data arriving.
    Thanks a lot!!!!!

    Yes it seemed a little confusing... but finally I got it:

    in usb_desc.h

    Code:
     #define AUDIO_TX_SIZE         180
    was changed to
    Code:
     #define AUDIO_TX_SIZE         192
    because usb_audio_transmit_callback() returns (target * 4); , and


    The audio is perfect (with some very tiny buzzing) now.
    Last edited by roomtek; 10-15-2017 at 09:02 AM. Reason: code change

  12. #12
    Just for anyone curious, I now get perfect audio. These were my modifications:

    In usb_audio.cpp:
    Code:
    unsigned int usb_audio_transmit_callback(void)
    {
    	uint32_t avail, num, target = 48, offset, len=0;
    	audio_block_t *left, *right;  
    
    	while (len < target) { // fill the buffer
    		num = target - len;
    		left = AudioOutputUSB::left_1st;
    		if (left == NULL) {
    			// buffer underrun - PC is consuming too quickly
    			memset(usb_audio_transmit_buffer + len, 0, num * 4); 
    			break;
    		}
    		right = AudioOutputUSB::right_1st;
    		offset = AudioOutputUSB::offset_1st;
    
    		avail = AUDIO_BLOCK_SAMPLES - offset;
    		if (num > avail) num = avail;
    
    		copy_from_buffers((uint32_t *)usb_audio_transmit_buffer + len,
    			left->data + offset, right->data + offset, num);
    		len += num;
    		offset += num;
    		if (offset >= AUDIO_BLOCK_SAMPLES) {
    			AudioStream::release(left);
    			AudioStream::release(right);
    			AudioOutputUSB::left_1st = AudioOutputUSB::left_2nd;
    			AudioOutputUSB::left_2nd = NULL;
    			AudioOutputUSB::right_1st = AudioOutputUSB::right_2nd;
    			AudioOutputUSB::right_2nd = NULL;
    			AudioOutputUSB::offset_1st = 0;
    		} else {
    			AudioOutputUSB::offset_1st = offset;
    		}
    	}
    	return target * 4;
    }
    In usb_desc.h:
    Code:
    #define AUDIO_TX_SIZE         192

    In usb_desc.c:
    Code:
    LSB(48000), MSB(48000), 0,		// tSamFreq

    In usb_dev.c:
    Code:
     case 0x81A2: // GET_CUR (wValue=0, wIndex=interface, wLength=len)
    		if (setup.wLength >= 3) {
    			reply_buffer[0] = 48000 & 255; 
    			reply_buffer[1] = 48000 >> 8; 
    			reply_buffer[2] = 0;
    			datalen = 3;
    			data = reply_buffer;
    		} else {
    			endpoint0_stall();
    			return;
    		}
    		break;

  13. #13
    Senior Member MickMad's Avatar
    Join Date
    Feb 2013
    Location
    Italy
    Posts
    163
    Nice stuff. I was planning on doing this too.

    For what I remember from when I did the USB audio descriptors back in 2013, the best option would be to declare a series of different USB configuration descriptors for each sampling frequency (44.1, 48, 88.2, 96, 192); in this way the PC can "ask" the Teensy to reconfigure itself to support that frequency. This would also need some callbacks to modify the sampling frequency in the Audio lib dynamically, as well as resetting the I2S bus in case it's used.

  14. #14
    Senior Member MickMad's Avatar
    Join Date
    Feb 2013
    Location
    Italy
    Posts
    163
    Quote Originally Posted by MickMad View Post
    the best option would be to declare a series of different USB configuration descriptors for each sampling frequency (44.1, 48, 88.2, 96, 192)

    So yeah, I tackled this for the whole afternoon and it kinda works. I say "kinda" because now the Teensy shows two possible configurations (one for 44.1 and one for 48 KHz), it enumerates properly, it switches configuration properly without dying or crashing the host in the process, but I tried the easiest USB Passthrough to see if any sound output through the Teensy Audio USB output would come back through the Teensy Audio USB input aaaaaand it doesn't.

    Instead of copy-pasting all the mods I did, I'm attaching a zip file with the modified USB files, while explaining what I did:

    usb_audio.zip

    • First thing was to add a second alternate configuration for both AudioStreaming TX and RX interfaces, increasing the audio interfaces descriptor size accordingly (in usb_desc.c).
    • I had to drop the AUDIO_*X_SIZE defines and replace them with two separate defines, AUDIO_*X_SIZE_44_1_KHZ and AUDIO_*X_SIZE_48_KHZ (in usb_desc.h); these are used all over the USB core files.
    • The usb_audio_xxx_buffer[AUDIO_*X_SIZE/2] have been changed to usb_audio_xxx_buffer[AUDIO_*X_SIZE_48_KHZ/2]; since they are declared as aligned DMA buffers I'd rather initialize them to the maximum needed size and use a lesser part of it when switching to 44.1 KHz (in usb_audio.cpp)
    • usb_audio_transmit_callback now checks wether the current interface setting is for 44.1 or 48 KHz, and sets the uint32_t target accordingly (in usb_audio.cpp)
    • usb_setup() now initializes the BDT space for the RX endpoint to the bigger needed size (192 samples for the 48 KHz case) (in usb_dev.c)
    • when the Teensy receives a Set Interface request to change sampling frequency, it switches both interfaces to the newly requested frequency (in usb_dev.c)
    • I updated the GET_CUR request to show which sampling frequency is currently used (in usb_dev.c)
    • usb_isr(): when the routine reinitializes the BDT it checks which configuration is used to set the packets size accordingly


    I yet have to understand which of these mods is affecting the data throughput of the AudioOutputUSB object (and I yet have to test this with I2S objects too) but it's a beginning.

  15. #15
    Senior Member MickMad's Avatar
    Join Date
    Feb 2013
    Location
    Italy
    Posts
    163
    I found another way, which takes less space; I forgot that you can say how many sampling frequency an AudioStreaming endpoint supports directly in its associated Audio Format descriptor.
    As in the previous post, the Teensy enumerates properly, responds properly to the requests, changes sampling frequency etc, but still I can't get any output from the Teensy to the USB host; it's just a bunch of 0s.

    I added a usb_audio_sampling_frequency value which, guess what, holds the current sampling frequency used by the USB Audio function, and a usb_audio_change_sampling_frequency flag that's used to see if the Teensy received a request to change its USB Audio function sampling frequency; I'm using this value to empty the USB Audio function buffers in usb_audio.cpp in hope that this would fix the no audio issue but it doesn't work.

    Anyway, if you want to check it out and weigh in here's the mods: usb_audio.zip

    edit: I forgot to mention that with this method (using the set endpoint control requests, instead of using the set interface alternate setting) the Teensy takes around 5 seconds to respond to the change of sampling frequency

  16. #16
    THis is Awesome! I am about to test this right away.

    Quick question, in:

    Code:
    int usb_audio_set_feature(void *stp, uint8_t *buf) 
    {
    	struct setup_struct setup = *((struct setup_struct *)stp);
    	if (setup.bmRequestType==0x21) { // should check bRequest, bChannel and UnitID
    			if (setup.bCS==0x01) { // mute
    				if (setup.bRequest==0x01) { // SET_CUR
    					AudioInputUSB::features.mute = buf[0]; // 1=mute,0=unmute
    					AudioInputUSB::features.change = 1;
    					return 1;
    				}
    			}
    			else if (setup.bCS==0x02) { // volume
    				if (setup.bRequest==0x01) { // SET_CUR
    					AudioInputUSB::features.volume = buf[0] + (buf[1]<<8);
    					AudioInputUSB::features.change = 1;
    					return 1;
    				}
    			}
    	}else if (setup.bmRequestType==0x22) {
    			if (setup.bCS==0x01) { // sampling frequency
    				if (setup.bRequest==0x01) { // SET_CUR
    					uint32_t tmp = buf[0] + (buf[1]<<8) + (buf[2]<<16);
    					if (tmp!=44100||tmp!=48000) return 0;
    					usb_audio_sampling_frequency = tmp;
    					usb_audio_change_sampling_frequency=true;
    					return 1;
    				}
    			}
    	}
    	return 0;
    }
    It is possible to get this callback(usb_audio_change_sampling_frequency) bubble all the way up to our main code?

  17. #17
    Senior Member MickMad's Avatar
    Join Date
    Feb 2013
    Location
    Italy
    Posts
    163
    Quote Originally Posted by roomtek View Post
    THis is Awesome! I am about to test this right away.3

    Quick question, in:

    Code:
    int usb_audio_set_feature(void *stp, uint8_t *buf) 
    {
    	struct setup_struct setup = *((struct setup_struct *)stp);
    	if (setup.bmRequestType==0x21) { // should check bRequest, bChannel and UnitID
    			if (setup.bCS==0x01) { // mute
    				if (setup.bRequest==0x01) { // SET_CUR
    					AudioInputUSB::features.mute = buf[0]; // 1=mute,0=unmute
    					AudioInputUSB::features.change = 1;
    					return 1;
    				}
    			}
    			else if (setup.bCS==0x02) { // volume
    				if (setup.bRequest==0x01) { // SET_CUR
    					AudioInputUSB::features.volume = buf[0] + (buf[1]<<8);
    					AudioInputUSB::features.change = 1;
    					return 1;
    				}
    			}
    	}else if (setup.bmRequestType==0x22) {
    			if (setup.bCS==0x01) { // sampling frequency
    				if (setup.bRequest==0x01) { // SET_CUR
    					uint32_t tmp = buf[0] + (buf[1]<<8) + (buf[2]<<16);
    					if (tmp!=44100||tmp!=48000) return 0;
    					usb_audio_sampling_frequency = tmp;
    					usb_audio_change_sampling_frequency=true;
    					return 1;
    				}
    			}
    	}
    	return 0;
    }
    It is possible to get this callback(usb_audio_change_sampling_frequency) bubble all the way up to our main code?
    I guess it is already visible; the usb_audio_sampling_frequency value is defined in usb_audio.h as an extern value and declared in usb_audio.cpp so as long as the USB Audio mode is selected that should work. Oh, please note that I only modified the part relative to the Audio only USB mode in usb_desc.h, any other mode should not compile; in that case you just need to replace the AUDIO_*X_SIZE defines with the new, frequency-dependent values.

  18. #18
    Yes, I got the errors and added the defines.

    I am checking the value in loop() but it never changes.
    Code:
      if (sampling_frequency != usb_audio_sampling_frequency)
      {
        sampling_frequency = usb_audio_sampling_frequency;
        Serial.printf("NEW SamFreq %d", sampling_frequency);
      }
    I'll keep playing around with it.. I will try to receive this value and then re-configure the I2S clocks to match.

  19. #19
    Senior Member MickMad's Avatar
    Join Date
    Feb 2013
    Location
    Italy
    Posts
    163
    Quote Originally Posted by roomtek View Post
    I am checking the value in loop() but it never changes.
    Maybe there's something wrong in the set feature function. Checking this right away.

  20. #20
    it wierd, but this is the culprit:
    Code:
    	if ((tmp != 44100) || (tmp != 48000))
    					return 0;
    I am not sure why it fails there, but the I printed the value of tmp: and see it changing:

    TEmp F: 48000
    SamFreq: 44100

    TEmp F: 48000
    SamFreq: 44100

    TEmp F: 44100
    SamFreq: 44100

  21. #21
    Senior Member MickMad's Avatar
    Join Date
    Feb 2013
    Location
    Italy
    Posts
    163
    Quote Originally Posted by MickMad View Post
    Maybe there's something wrong in the set feature function. Checking this right away.
    Silly me:

    Code:
    if (tmp!=44100||tmp!=48000) return 0;
    this should be if (tmp!=44100&&tmp!=48000) return 0;



    This was also the reason why the teensy would hang for 5 seconds between any frequency change requested by the host.

    Latest version is in this attachment (I also conveniently modified the other parts of usb_desc.h): usb_audio.zip

  22. #22
    Click image for larger version. 

Name:	Capture.PNG 
Views:	56 
Size:	18.6 KB 
ID:	11805
    works great now. I added my own callback, and now I get the changes in my code


    Thanks Again!!

  23. #23
    Senior Member MickMad's Avatar
    Join Date
    Feb 2013
    Location
    Italy
    Posts
    163
    Quote Originally Posted by roomtek View Post
    Click image for larger version. 

Name:	Capture.PNG 
Views:	56 
Size:	18.6 KB 
ID:	11805
    works great now. I added my own callback, and now I get the changes in my code


    Thanks Again!!
    are you getting data in/out using the I2S objects as well? I still have to test them and as far as I'm concerned the AudioOutputUSB object is not working yet.

  24. #24
    Yes. In fact after I get the callback, I set the new MCLK and set the new sample rate to my I2S Ics and the audio resumes on the PC:

    Code:
    
    void audioformat_change() // change sample rate requests
    {
      Serial.println("audioformat_change_callback");
    
      dac.muteAudio(1, 1);
      RADIOMODES mode = radio.mode;
      Serial.printf("New samplerate: %d\n", usb_audio_sampling_frequency);
      recorder.halt(); // stop everything here
      setupI2SCLOCKS(usb_audio_sampling_frequency);
      radio.audio_sample_rate = usb_audio_sampling_frequency;
      radio.prop(P_DIGITAL_IO_OUTPUT_SAMPLE_RATE, usb_audio_sampling_frequency);
      //  if (mode != RADIO_OFF)
      //    radio.powerUp();
      dac.muteAudio(0, 0);
      audio_rate_change = false;
    }

  25. #25
    Strangely, after I sent this comment, I do not see the device appear anymore... So AudioOutputUSB may be broken.

Posting Permissions

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