USB Audio Clicking Noise

... which means that FrankB and myself have spent much time for nothing... :-(

Well Theremingenieur, I believe you know how I do things and how this forum generally works.

Especially with a thread like this involves (maybe) different issues that manifest with similar problems, there's always a chance something may fall through the cracks. We're all humans here, with imperfect memory and many demands on our limited time & attention. When something does get missed, just (hopefully politely) bring it up again. Sometimes if a thread has become too long and at least some subset of the problems were resolved, starting a new thread with link to the old one and a clear description of what's still unresolved is the best approach.

Unfortunately, we're in a period right now were I can't pour much engineering time into these sorts of investigations. The schedule for new Teensy hardware is approx every 2 years. When it's a major change, like we had from 2.0 to 3.0, and this transition from 3.x to 4.0, there will be a few months leading up to the release, and a couple months after, where I just can't put the usual effort into chasing down bugs. I still try to do quick tests, and I try to keep up with the forum threads. When a problem is very serious, like the recent serial lockup where Serial1.end() was called during a narrow time window where the serial fifo had an unreported framing error, I do prioritize a fix. I also try to prioritize at least looking to gauge how serious the bug is. For less serious problems, I'm afraid they're going to sit on my issue list for many months.

About the audio timing work you & Frank have done, the honest truth is I haven't had time to look at it yet. There's a very real possibility I won't manage to look at it until T4 is released and I get caught up to a ton of other stuff that's gone onto the "after T4 release" list. I know that can feel very frustrating. Believe me, I would really rather spend my time doing these sorts of things. But the reality is PJRC really *must* keep releasing new hardware to financially sustain all this work. And the new T4 hardware, at least all the non-analog stuff, is truly amazing.

On the USB stuff, in the very long term (like late-2019 or 2020) I might try to reimplement the whole thing using synchronous mode (see 3.3.2 on page 19 of the USB audio class 1.0 spec) and do all the necessary rate conversion on the Teensy side. Originally I discounted this idea as requiring too much CPU time. But the 17 Hz mismatch turned out to be a terribly thorny problem due the varying host side implementations. Then again, I'm not even 100% confident that would really be better. This whole USB isochronous rate matching properly with all operating systems is a really hard problem.

Please, if this falls through the cracks, just wait a while and then bring it up as a new thread.
 
Thanks paul for your replies !

But what about testing on WIN or OSX os ? and the "buffer overrun" in msg #35 ? :D

edit : I just saw your post. It's not a good new for me :( I just bought some audio boards and I can't use them ....
 
Last edited:
Sorry, I can't do *anything* with Mac or Windows until at least next week. Remind me after Tuesday, Nov 20th.

Usually I can do quick tests with Linux and only small hardware, because my desktop runs Linux. To do anything at all with Mac or Windows or Raspberry Pi or Jetson TX2, or anything requiring setting up much hardware, I need to clear off my workbench to set up another machine. Sometimes on the weekends I can use the work tables where Teensy is normally tested and packaged, if Robin isn't using them for inventory management stuff.
 
Paul...... I tried again following you test procedure on Lubuntu 18.04 and still get the faint clicks. I have attached screen dumps and an audio wav recording.
I am not sure that your test with the cheap USB audio adaptor is same as what I was thinking. I am not querying the quality of the playback from the PC, I was thinking of the USB audio adaptor as an alternative to recording on PC and playing back from PC ...... to try and eliminate any issues I may have on my PC because of old hardware.
The way I would intend to test with the USB adaptor is to plug teensy into the USB port of the USB audio adaptor and connect a speaker/amplifier to the lineout of the USB audio adaptor and listen directly without using a PC.
But I don't have one yet to try. I also see you are testing on a teensy 3.6 ..... I am using a Teensy 3.2.....is there likely to be any difference in the audio processing.?? ....... I will order one of these as well.... have about 6 of 3.2's but no 3.6s.
Eventually my use for all of this would be to be able to send USB audio and USB power over 1 lead from a teensy controlled gadget to a USB audio input speaker or other USB audio processing device and not needing an 3.5mm jack
 

Attachments

  • 2000hz2sec.wav.zip
    123.5 KB · Views: 175
  • audacity1.png
    audacity1.png
    181.4 KB · Views: 168
  • audacity2.png
    audacity2.png
    185.7 KB · Views: 147
The way I would intend to test with the USB adaptor is to plug teensy into the USB port of the USB audio adaptor and connect a speaker/amplifier to the lineout of the USB audio adaptor and listen directly without using a PC.

This doesn't make any sense.

That USB audio adaptor is a USB device. USB devices can only be plugged into USB hosts or hubs, where the hubs plug into a USB host. You can't plug a USB device into another USB device.

When you think of USB devices, think USB mouse & USB keyboard. When you think of USB hosts, think computers. You can only plug keyboards and mice into a computer. Plugging a keyboard into a mouse without a computer makes no sense at all!

Teensy 3.2 has only 1 USB port which only works as a USB device.

Teensy 3.6 has 2 USB ports, where the 2nd port can work as a USB host. So in some glorious but distant future where the USBHost_t36 library gets isochronous pipe support (currently only control, bulk & interrupt pipes work) and an audio driver is added, and this hypothetical driver offers whatever isochronous synchronization method that USB adaptor wants, and that driver integrates with the Audio library (no small feat to do well... as this thread shows), then you could use it with Teensy 3.6 on the host port. But that's a lot of software which doesn't exist today. USBHost_t36 offers only a small collection of USB device drivers.

There just isn't any way to use that USB adaptor connected to a Teensy, and there *never* will be any way with Teensy 3.2 because the USB on 3.2 only supports device mode.
 
Paul, thanks for the info......well thats that then......I suppose I should have read the small print somewhere and known this myself. I just thought as teensy was generating the audio and needed another object running to keep USB running that it would output audio data to what was plugged in. But that explains it, thanks.

Maybe thats why USB audio is not so popular....everybody uses 3.5mm jacks even on bluetooth speakers.???

Regards, then sending and recording to a PC, I discovered that if I set the sine frequency to 1000Hz I do not hear any clicks, at 1200Hz the clicks start to be heard faintly and similar at 800Hz the start to be heard faintly. The clicks are louder or heard more easily with higher frequency. at 4000 Hx the clicks are quite distinct. I reduced the amplitude of a 4000Hz sine to 0.01 and recorded this and the audio as well as the clicks were reduced to low 0.01 amplitude. I amplified this in Audacity and 4000Hz audio and clicks can be heard. So does that mean the clicks are coming in with the audio and not being generated in the PC audio processing...???. I have attached audio recording of the sounds.
 

Attachments

  • 1000Hzamp4.wav.zip
    146.1 KB · Views: 638
  • 1200Hzamp4.wav.zip
    84.1 KB · Views: 163
  • 4000Hzamp01.wav.zip
    13.6 KB · Views: 156
Regards, then sending and recording to a PC, I discovered that if I set the sine frequency to 1000Hz I do not hear any clicks, at 1200Hz the clicks start to be heard faintly and similar at 800Hz the start to be heard faintly. The clicks are louder or heard more easily with higher frequency. at 4000 Hx the clicks are quite distinct

Please test with another PC. I'm pretty sure this problem is happening entirely on your PC, since you're getting correct waveforms recorded from Teensy when viewed in Audacity, and I can't reproduce it here when playing the also-good waveforms through 2 different output devices (high quality speakers and a cheap USB audio adaptor with good quality headphones). Unless there's a way to reproduce it on other computers, I'm going to assume this particular problem is just poor quality audio hardware on your PC.

If the problem is on your PC and not anything to do with Teensy, all this talk is only a distraction from the rest of this thread, where playing (not recording) with Teensy is suffering problems with some computers.
 
It's just that I can exactly reproduce the same problem on my MacBook Pro: I hear that crackling, too, after recording Teensy's USB audio output in Audacity (while the waveforms are displayed nicely) when playing back the recording over the MacBook's internal speakers with the Teensy disconnected. And FrankB experienced the same. So, the conclusion that would be a problem on Teenfor3's PC seems not very logical to me, especially after I could show that the subroutine which handles buffer overruns is definitively involved.

But let's postpone that and not longer steal precious Teensy4 development time! I don't think that it's a heavy or blocking problem, so we can wait until further investigation will be possible in 2019.
 
Ok then, this thread will go on the list of stuff to investigate more at a later date.

I've tried to catch up on the forum and missed emails over the last couple days, while waiting for a T4 board rev (which allows the bootloader to control the boot mode pins - needed to recover from certain ways the board could otherwise become bricked). The PCBs arrived just now! I'm going to take them over to the contract manufacturer now!
 
Yes, I have tried again on Lubuntu laptop and on XP desktop and win 10 laptop and clicking is still there. If I compile on one and play on another, still clicks.

I listened to the 1000 Hz and 1200Hz and 2000Hz that I posted earlier, on both XP and Lubuntu with the same headphones each time turned up same loudness and clicks are there in the 1000Hz as well, I think it was masked more when listening earlier on laptop speakers not so loud and I thought clicks wern't there at 1000Hz.
I can also listen without the need to record and playback on XP by clicking "Click to Monitor" in Audacity and the clicks are still there. Also same with Win 10 in sound settings can click the tick box to listen to Teensy input device without recording and still get clicks.

I started this thread, so thank you all for your help, and I have learned that teensy3,2 is a USB device and needs plugged in to a USB host, which is not really what I was wanting on this instance. But for now teensy DAC and audio lead is still good quality simple sound.
 
I dont understand........are all the posts not available to everybody on this forum....?????

Post .... #56 ..... has 3 recordings

Post .... #54 ..... has 1 recording

There are 2 other recordings earlier.....one from Therem.... and one from worldhugo......

#54 and #56 are zipped wave files for best detail....
 
ok, to preserve every info and attachments, I'll temporarily close this thread for now and reopen it on demand when the time for it will have come.
 
re-open :)
I have a workaround that works for me:
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioSynthWaveformSine sine1;            //xy=493,348
AudioOutputI2S i2s1;                     //xy=713,128
AudioOutputUSB usb1;                     //xy=806,345
AudioConnection patchCord1(sine1, 0, usb1, 0);
AudioConnection patchCord2(sine1, 0, usb1, 1);
AudioConnection patchCord3(sine1, 0, i2s1, 0);
// GUItool: end automatically generated code

#include <AudioTiming.h>
AudioTiming audiotiming;

void setup() {
    
    while (!Serial);
    
    AudioMemory(10);
    audiotiming.init();
    audiotiming.begin();
    Serial.println(audiotiming.setI2S_freq(44112)); //<- this changes something
    
    sine1.frequency(20000);
    sine1.amplitude(0.9);

}

void loop() {}

Obviously, for MY pc (windoze 10 pro) 44100 was too slow - 44117 too fast. I played a bit with the blink-led on differnt places - 44112 seems to be the best solution for my pc.
Maybe you get other results -esp.with a MAC or Linux - but I thought, perhaps it helps? Would be interesting to hear if that solves it for you, if it works with other values, if it gets better or worse.., or ...whatever
If you need ADC or DAC with I2S, the audiotiming-lib can help, too (sync to i2s via pin-triggered dma). At least if you need both together analog+i2s) you'll need it here.

Edit: Sine-Frequency above is 20kHZ - you'll hear nothing, I hope.
 
You can try 44111.7 (<-no Typo!) , too.
Code:
Serial.println(audiotiming.readI2S_freq());
prints the real frequency - the I2S is not able to reach every frequency exactly.

@Paul: Now I'm almost sure, the only working solution would be to sync the whole audio library to usb...*somehow*
But let's think about that later.
 
Last edited:
Thanks for still trying......I tried your workaround on teensy3.2 and teensy3.6. on old WinXP. Tried both "click to Monitor" in Audacity and recording and playback in Audacity.
Not getting the original clicks several times per second, but now it plays very clearly no clicks (or maybe a random one) for about 20 seconds and then starts to click consistantly..... probably even more clicks than before. I tried it at audio frew 1000Hz and 2000Hz........still just your at sample rate 44112.
I will investigate on Linux and Win 10 tomorrow ......you must at least be on the right track anyway.......
 
Tried the workaround in my Lubuntu 18.04 linux. Get clicks, different from thee original louder and more consistant that in the WinXP. Clicks start from the beginning of record.
I recorded over 1 minute and click rate didnt increase. The clicks are now in short bursts and zooming in shows distorted waveform recorded in the area where the clicks are.

I changed your 44112 to 44111.7 and get persistent clicks and can see some distorted wave but only short bursts one or 2 together

I changed to (96000000.0/2176.0) and seem to get less clicks but maybe louder and longer bursts of distorted cycles.

the above calculation is 44117.6470588235.........????

It definitely affects the problem but not curing it...????

I put in the 44117.6470588235 and recorded 1 minute got 5 or 6 bursts of clicks each with about 20 cycles of 2000 Hz distorted
but otherwise played very clear and good sine waveform
 
Last edited:
maybe louder because the sketch above uses
sine1.amplitude(0.9);

I increased the amplitude because it's easier to hear. (btw, i don't need to record it - it can hear them "live")
Nevertheless, with 44111.7 I hear <almost> no clicks anymore. Way less that with the original, and with normal volume almost not hear. Yesterday I thought there were NO click, but today I heard them.
Well, currently there is no other way.
We can do what we want - Either the PC is too slow or the teensy. The synchonization between both does not work. Since this is not solvable at the moment, and my workaround does not solve your issue, I'll close this thread again.

Edit 1: The clicks occur, because buffers are filled with zero (0) - with underflow or overflow - both.
Edit 2: maybe try other speeds. Maybe it is very OS and PC depenended. Maybe it works better in summer or spring :). It's a workaround only, which works for me.
Edit 3: Why 44100 not works - it should - I really don't know. There must be something, I do not see.
 
Last edited:
Ok, last try: There is a little bug in usb_audio_transmit_callback. It returns a too high value when not enough data is available.
To fix it, insert the red line below.
BUT: This works only, if the PC is requesting faster than the Teensy has data. That means, it's needed to reduce the frequency to 44100 - better a bit less,if you want to be sure:.
So, a line audiotiming.setI2S_freq(44099.8) is good, too.

Tested with Windows 10 only, only output Teensy->PC.
*Note* This is *still* a workaround - but seems to work with ~44100Hz, at least.

usb_audio.cpp:
Code:
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 = 44;
    } else {
        count = 0;
        target = 45;
    }
    while (len < target) {
        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);
            //serial_print("%");
[B][COLOR=#ff0000]                        return len * 4;[/COLOR][/B]
            break;
        }
        right = AudioOutputUSB::right_1st;
        offset = AudioOutputUSB::offset_1st;
[...]
 
Okay, below's my final version for a workaround - For me it works - for Teenfor3, unfortunateley, not.
We have two problematic cases (Paul also describes them in the source code)
a) The computer demands data too fast
b) The computer demands data too slowly

I think we're pretty helpless at b) - we'd have to discard blocks - which would create "clicks" again, or we'd have to do an elaborate resampling.
a) is solvable, I think, if the speed difference is not too big.
On the contrary - for the current situation a) would even be good, if you assume that the PC can handle it - it has much more computing power than the teensy.
So my idea for the workaround is to be slower than the PC - by setting the sample rate to something below 44100: 44099.68 is feasible.
So the machine is waiting for data, and we don't have to worry about b) anymore. *(b) is the normal case with 44117.6 Hz samplingrate
We just have to tell it from time to time that we don't have enough samples.

The workaround works for me - I communicated with Teenfor3 via email, but it doesn't work for him - I don't know the reasons.
It needs the library Audiotiming, which can be found on my Github account. It can also control the PDB, which is necessary for the analog devices (But I haven't found a solution for PWM yet).

Here is the file audio_usb.cpp that needs to be swapped in [...]/hardware/teensy/avr/cores/teensy3 :


Code:
/* Teensyduino Core Library
 * http://www.pjrc.com/teensy/
 * Copyright (c) 2017 PJRC.COM, LLC.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * 1. The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * 2. If the Software is incorporated into a build system that allows
 * selection among a list of target devices, then similar target
 * devices manufactured by PJRC.COM must be included in the list of
 * target devices and selectable in the same manner.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <Arduino.h>
#include "usb_dev.h"

#ifdef AUDIO_INTERFACE // defined by usb_dev.h -> usb_desc.h
#if F_CPU >= 20000000

// Uncomment this to work around a limitation in Macintosh adaptive rates
// This is not a perfect solution.  Details here:
// https://forum.pjrc.com/threads/34855-Distorted-audio-when-using-USB-input-on-Teensy-3-1
//#define MACOSX_ADAPTIVE_LIMIT

[COLOR=#ff0000]#define FRANKB_PATCH[/COLOR]

bool AudioInputUSB::update_responsibility;
audio_block_t * AudioInputUSB::incoming_left;
audio_block_t * AudioInputUSB::incoming_right;
audio_block_t * AudioInputUSB::ready_left;
audio_block_t * AudioInputUSB::ready_right;
uint16_t AudioInputUSB::incoming_count;
uint8_t AudioInputUSB::receive_flag;

struct usb_audio_features_struct AudioInputUSB::features = {0,0,FEATURE_MAX_VOLUME/2};

#define DMABUFATTR __attribute__ ((section(".dmabuffers"), aligned (4)))
uint16_t usb_audio_receive_buffer[AUDIO_RX_SIZE/2] DMABUFATTR;
uint32_t usb_audio_sync_feedback DMABUFATTR;
uint8_t usb_audio_receive_setting=0;

static uint32_t feedback_accumulator = 185042824;

void AudioInputUSB::begin(void)
{
    incoming_count = 0;
    incoming_left = NULL;
    incoming_right = NULL;
    ready_left = NULL;
    ready_right = NULL;
    receive_flag = 0;
    // update_responsibility = update_setup();
    // TODO: update responsibility is tough, partly because the USB
    // interrupts aren't sychronous to the audio library block size,
    // but also because the PC may stop transmitting data, which
    // means we no longer get receive callbacks from usb_dev.
    update_responsibility = false;
    usb_audio_sync_feedback = feedback_accumulator >> 8;
}

static void copy_to_buffers(const uint32_t *src, int16_t *left, int16_t *right, unsigned int len)
{
    uint32_t *target = (uint32_t*) src + len; 
    while ((src < target) && (((uintptr_t) left & 0x02) != 0)) {
        uint32_t n = *src++;
        *left++ = n & 0xFFFF;
        *right++ = n >> 16;
    }

    while ((src < target - 2)) {
        uint32_t n1 = *src++;
        uint32_t n = *src++;
        *(uint32_t *)left = (n1 & 0xFFFF) | ((n & 0xFFFF) << 16);
        left+=2;
        *(uint32_t *)right = (n1 >> 16) | ((n & 0xFFFF0000)) ;
        right+=2;
    }

    while ((src < target)) {
        uint32_t n = *src++;
        *left++ = n & 0xFFFF;
        *right++ = n >> 16;
    }
}

// Called from the USB interrupt when an isochronous packet arrives
// we must completely remove it from the receive buffer before returning
//
void usb_audio_receive_callback(unsigned int len)
{
    unsigned int count, avail;
    audio_block_t *left, *right;
    const uint32_t *data;

    AudioInputUSB::receive_flag = 1;
    len >>= 2; // 1 sample = 4 bytes: 2 left, 2 right
    data = (const uint32_t *)usb_audio_receive_buffer;

    count = AudioInputUSB::incoming_count;
    left = AudioInputUSB::incoming_left;
    right = AudioInputUSB::incoming_right;
    if (left == NULL) {
        left = AudioStream::allocate();
        if (left == NULL) return;
        AudioInputUSB::incoming_left = left;
    }
    if (right == NULL) {
        right = AudioStream::allocate();
        if (right == NULL) return;
        AudioInputUSB::incoming_right = right;
    }
    while (len > 0) {
        avail = AUDIO_BLOCK_SAMPLES - count;
        if (len < avail) {
            copy_to_buffers(data, left->data + count, right->data + count, len);
            AudioInputUSB::incoming_count = count + len;
            return;
        } else if (avail > 0) {
            copy_to_buffers(data, left->data + count, right->data + count, avail);
            data += avail;
            len -= avail;
            if (AudioInputUSB::ready_left || AudioInputUSB::ready_right) {
                // buffer overrun, PC sending too fast
                AudioInputUSB::incoming_count = count + avail;
                //if (len > 0) {
                    //serial_print("!");
                    //serial_phex(len);
                //}
                return;
            }
            send:
            AudioInputUSB::ready_left = left;
            AudioInputUSB::ready_right = right;
            //if (AudioInputUSB::update_responsibility) AudioStream::update_all();
            left = AudioStream::allocate();
            if (left == NULL) {
                AudioInputUSB::incoming_left = NULL;
                AudioInputUSB::incoming_right = NULL;
                AudioInputUSB::incoming_count = 0;
                return;
            }
            right = AudioStream::allocate();
            if (right == NULL) {
                AudioStream::release(left);
                AudioInputUSB::incoming_left = NULL;
                AudioInputUSB::incoming_right = NULL;
                AudioInputUSB::incoming_count = 0;
                return;
            }
            AudioInputUSB::incoming_left = left;
            AudioInputUSB::incoming_right = right;
            count = 0;
        } else {
            if (AudioInputUSB::ready_left || AudioInputUSB::ready_right) return;
            goto send; // recover from buffer overrun
        }
    }
    AudioInputUSB::incoming_count = count;
}

void AudioInputUSB::update(void)
{
    audio_block_t *left, *right;

    __disable_irq();
    left = ready_left;
    ready_left = NULL;
    right = ready_right;
    ready_right = NULL;
    uint16_t c = incoming_count;
    uint8_t f = receive_flag;
    receive_flag = 0;
    __enable_irq();
    if (f) {
        int diff = AUDIO_BLOCK_SAMPLES/2 - (int)c;
        feedback_accumulator += diff / 3;
        uint32_t feedback = (feedback_accumulator >> 8) + diff * 100;
#ifdef MACOSX_ADAPTIVE_LIMIT
        if (feedback > 722698) feedback = 722698;
#endif
        usb_audio_sync_feedback = feedback;
        //if (diff > 0) {
            //serial_print(".");
        //} else if (diff < 0) {
            //serial_print("^");
        //}
    }
    //serial_phex(c);
    //serial_print(".");
    if (!left || !right) {
        //serial_print("#"); // buffer underrun - PC sending too slow
        //if (f) feedback_accumulator += 10 << 8;
    }
    if (left) {
        transmit(left, 0);
        release(left);
    }
    if (right) {
        transmit(right, 1);
        release(right);
    }
}







bool AudioOutputUSB::update_responsibility;
audio_block_t * AudioOutputUSB::left_1st;
audio_block_t * AudioOutputUSB::left_2nd;
audio_block_t * AudioOutputUSB::right_1st;
audio_block_t * AudioOutputUSB::right_2nd;
uint16_t AudioOutputUSB::offset_1st;


uint16_t usb_audio_transmit_buffer[AUDIO_TX_SIZE/2] DMABUFATTR;
uint8_t usb_audio_transmit_setting=0;

void AudioOutputUSB::begin(void)
{
    update_responsibility = false;
    left_1st = NULL;
    right_1st = NULL;
}

static void copy_from_buffers(uint32_t *dst, int16_t *left, int16_t *right, unsigned int len)
{
    // TODO: optimize...
    while (len > 0) {
        *dst++ = (*right++ << 16) | (*left++ & 0xFFFF);
        len--;
    }
}

void AudioOutputUSB::update(void)
{
    audio_block_t *left, *right;

    // TODO: we shouldn't be writing to these......
[COLOR=#ff0000]#ifdef FRANKB_PATCH [/COLOR]   
    left = receiveReadOnly(0); // input 0 = left channel
    right = receiveReadOnly(1); // input 1 = right channel
[COLOR=#ff0000]#else  [/COLOR]  
    left = receiveWritable(0); // input 0 = left channel
    right = receiveWritable(1); // input 1 = right channel
[COLOR=#ff0000]#endif   [/COLOR] 
    if (usb_audio_transmit_setting == 0) {
        if (left) release(left);
        if (right) release(right);
        if (left_1st) { release(left_1st); left_1st = NULL; }
        if (left_2nd) { release(left_2nd); left_2nd = NULL; }
        if (right_1st) { release(right_1st); right_1st = NULL; }
        if (right_2nd) { release(right_2nd); right_2nd = NULL; }
        offset_1st = 0;
        return;
    }
    if (left == NULL) {
        left = allocate();
        if (left == NULL) {
            if (right) release(right);
            return;
        }
        memset(left->data, 0, sizeof(left->data));
    }
    if (right == NULL) {
        right = allocate();
        if (right == NULL) {
            release(left);
            return;
        }
        memset(right->data, 0, sizeof(right->data));
    }
    __disable_irq();
    if (left_1st == NULL) {
        left_1st = left;
        right_1st = right;
        offset_1st = 0;
    } else if (left_2nd == NULL) {
        left_2nd = left;
        right_2nd = right;
    }
[COLOR=#ff0000]#ifndef FRANKB_PATCH    [/COLOR]
    else {
        // buffer overrun - PC is consuming too slowly
        //digitalWriteFast(13, !digitalReadFast(13));
        audio_block_t *discard1 = left_1st;
        left_1st = left_2nd;
        left_2nd = left;
        audio_block_t *discard2 = right_1st;
        right_1st = right_2nd;
        right_2nd = right;
        offset_1st = 0; // TODO: discard part of this data?
        //serial_print("*");
        release(discard1);
        release(discard2);
    }
[COLOR=#ff0000]#endif[/COLOR]
    __enable_irq();
}


// 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 = 44;
    } else {
        count = 0;
        target = 45;
    }
    while (len < target) {
        num = target - len;
        left = AudioOutputUSB::left_1st;
        if (left == NULL) {
            //digitalWriteFast(13, !digitalReadFast(13));
            // buffer underrun - PC is consuming too quickly
            [COLOR=#ff0000]memset(usb_audio_transmit_buffer + len * 4, 0, num * 4);[/COLOR]
            //serial_print("%");
[COLOR=#ff0000]#ifdef FRANKB_PATCH      [/COLOR]          
            return len * 4;
[COLOR=#ff0000]#endif      [/COLOR]      
            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;
}


struct setup_struct {
  union {
    struct {
    uint8_t bmRequestType;
    uint8_t bRequest;
    union {
        struct {
            uint8_t bChannel;  // 0=main, 1=left, 2=right
            uint8_t bCS;       // Control Selector
        };
        uint16_t wValue;
    };
    union {
        struct {
            uint8_t bIfEp;     // type of entity
            uint8_t bEntityId; // UnitID, TerminalID, etc.
        };
        uint16_t wIndex;
    };
    uint16_t wLength;
    };
  };
};

int usb_audio_get_feature(void *stp, uint8_t *data, uint32_t *datalen)
{
    struct setup_struct setup = *((struct setup_struct *)stp);
    if (setup.bmRequestType==0xA1) { // should check bRequest, bChannel, and UnitID
            if (setup.bCS==0x01) { // mute
                data[0] = AudioInputUSB::features.mute;  // 1=mute, 0=unmute
                *datalen = 1;
                return 1;
            }
            else if (setup.bCS==0x02) { // volume
                if (setup.bRequest==0x81) { // GET_CURR
                    data[0] = AudioInputUSB::features.volume & 0xFF;
                    data[1] = (AudioInputUSB::features.volume>>8) & 0xFF;
                }
                else if (setup.bRequest==0x82) { // GET_MIN
                    //serial_print("vol get_min\n");
                    data[0] = 0;     // min level is 0
                    data[1] = 0;
                }
                else if (setup.bRequest==0x83) { // GET_MAX
                    data[0] = FEATURE_MAX_VOLUME & 0xFF;  // max level, for range of 0 to MAX
                    data[1] = (FEATURE_MAX_VOLUME>>8) & 0x0F;
                }
                else if (setup.bRequest==0x84) { // GET_RES
                    data[0] = 1; // increment vol by by 1
                    data[1] = 0;
                }
                else { // pass over SET_MEM, etc.
                    return 0;
                }
                *datalen = 2;
                return 1;
            }
    }
    return 0;
}

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;
                }
            }
    }
    return 0;
}


#endif // F_CPU
#endif // AUDIO_INTERFACE


Using the AudioTiming lib: See above. setI2S_freq(44099.7); Or try without.

It would be interesting to hear if this works for you - or not. I only tested it with WIN10, "live" with speakers. Audacity went on strike and didn't want to record (why...?).
Most likely there is still a lot to improve on this workaround.
 
Last edited:
Back
Top