• AVR Freaks

useless struggle for audio quality

Page: 123 > Showing page 1 of 3
Author
stefanopod
Super Member
  • Total Posts : 1285
  • Reward points : 0
  • Joined: 2007/06/25 02:33:59
  • Location: Bologna,Italy
  • Status: offline
2008/03/01 09:36:29 (permalink)
0

useless struggle for audio quality

I share with the forum an experience that has given me a lot of headache and has also a funny side.

To me now it looks rather silly and probably it'll look silly to many, but before I was puzzled indeed.
 
I tried to achieve an audio sampling, using a low level tool as generic isochronous device.
 
I had some results, but I confronted them with other audio (much more sophisticated) experiencies found on the forum and the comparison was this: I achieved 24 Khz with a sampling of a single byte per period; others (scp6o and mzoran using audio class) almost the same frequency over a sampling of 4 bytes (many channels or higher adc resolution).
 
I was pretty ashamed of the poorness of my result (or one may say dying with envy): throughput=1/4!

So I tried a lot of tricks (software buffering and ping pong buffering) to increase ISO speed, but with very slight improvements in the sampling rate.

But (looking better in the examples of scp6o and mzoran) I discovered  that the clue of the issue is this : passing from a 20kHzX1 byte sampling to a 20kHzX2 bytes sampling is quite easy, passing from a a 20kHzX1 byte sampling to a 40kHzX1 byte sampling is almost impossible.

In few words the bottleneck isn't usb transmission speed that is quite sufficient; so my struggle to improve ISO routines was pointless.

My wrong idea came from putting sample rate=USB throughput.

The point is making the  sampling timer routine as fast as possible and, if needed,feeding more than one byte per routine; isochronous transmission will do its job without problems.
#1

49 Replies Related Threads

    stefanopod
    Super Member
    • Total Posts : 1285
    • Reward points : 0
    • Joined: 2007/06/25 02:33:59
    • Location: Bologna,Italy
    • Status: offline
    RE: useless struggle for audio quality 2008/03/06 11:59:01 (permalink)
    0
    I add something about my experience in recording/playing audio samples, that I had some pain to understand fully. Now I see  a bit more clear what I had to see clear long ago;  but I have seen in the forum very good examples and very poor explainations.

    To achieve the best results it's important to focus on having a fast sampling/playing function.

    USB isochronous speed isn't a problem.

    To have a fast function one has not to employ the built-in write/read functions of Microchip stack :
    these functions copy at every call tha data of a packet from usb ram to user ram, and waiting for the end
     of this job in a single interrupt function , before processing a byte, is too slow (in this way I had a best of 20kHz).

    One has to perform usb read/write and user processing of the incoming/outcoming bytes in separate moments:
    usb operations are clocked by the PC and executed by the PIC in batches when needed (polling Stat.Uown bit); user operations are performed byte by byte on every timer routine.

    So one has to build a FIFO where data are stored in packets in usb read-from-pc operation and fetched as bytes in user consuming operations (or the opposite in the write-to-pc of user produced bytes).

    Obviously the FIFO is quite useless if usb transfer and byte processing are performed in the same interrupt function.
     
    I have a 18F4550 at 48MHz; focusing on the interrupt function speed I achieved a 40KHz sampling/playing, but sparing machine cycles with assembler programming would yield better results (an interesting challenge for assembler fans).

    As for the problem of clock drift, that is matching pc-clocked iso speed and pic-clocked timer frequency,
     a simple adjusting of the timer frequency to the start-of-frame rate (multiplied by packetlength), is doing (I think) a good job.

    But that I will test later in real life experiments.
    #2
    nsxt
    Super Member
    • Total Posts : 198
    • Reward points : 0
    • Joined: 2006/11/04 21:23:04
    • Location: Albuquerque
    • Status: offline
    RE: useless struggle for audio quality 2008/03/06 20:18:59 (permalink)
    0
    I believe you are recognizing the misunderstanding of USB: It is simply not real time transfers. 
    To achieve fluidity, data must be packetized and shipped in bursts, and let the end processors sort out the timing.

    Using HID interrupt transfers, my throughput is easily 64KBps, which is max speed for that protocol. It is 64 bytes per transfer, and the host and contoller ends are required to retime everything back to normal.
    #3
    stefanopod
    Super Member
    • Total Posts : 1285
    • Reward points : 0
    • Joined: 2007/06/25 02:33:59
    • Location: Bologna,Italy
    • Status: offline
    RE: useless struggle for audio quality 2008/03/07 00:35:44 (permalink)
    0
    I agree that the key problem is the packetizing of USB transfers.
    But as for throughput in itself I maintain that the problem isn't transmission speed: I achieved 200kHz in loopback tests with iso transfer, but that speed was useless for audio purposes.
    The problem is the  efficient way one writes the sampling/playing routine to make it the shortest possible.
    As for HID devices, I not considered them, not being granted a definite bandwidth.
    #4
    Olin Lathrop
    Super Member
    • Total Posts : 7463
    • Reward points : 0
    • Joined: 2004/02/26 17:59:01
    • Location: Littleton Massachusetts
    • Status: offline
    RE: useless struggle for audio quality 2008/03/07 06:25:45 (permalink)
    -1 (1)
    But as for throughput in itself I maintain that the problem isn't transmission speed: I achieved 200kHz in loopback tests with iso transfer, but that speed was useless for audio purposes.

    Why?  Let's say you have two channels of audio sampled at 44Khz using 16 bit samples.  With the right reconstruction filter, that's going to be better than the speakers can reproduce.  (44K samples/second/channel) x (2 bytes/sample/channel) x (2 channels) = 176K bytes/second, which is less than the 200K bytes/second you quote.
     
    I've also found that for most cases bulk transfers are adequate for audio, and certainly a lot easier to deal with.  You can have a fixed timer in the PIC output the next sample to the audio driver hardware, and let the inherent flow control mechanism of USB automatically adjust the rate the host sends the data to the rate the PIC consumes it.  While the sustained rate is not absolutely guranteed by the USB, you are going to get it unless you have a very loaded bus.  It depends on how important it is that you have absolutely no dropouts  in the audio combined with the probability that they will happen in the target system.  If you know the USB won't be used for lots of isochronous transfers or many devices requesting bulk transfers simultaneously, you're not going to have a problem.
    #5
    stefanopod
    Super Member
    • Total Posts : 1285
    • Reward points : 0
    • Joined: 2007/06/25 02:33:59
    • Location: Bologna,Italy
    • Status: offline
    RE: useless struggle for audio quality 2008/03/07 12:18:43 (permalink)
    0
    I've also found that for most cases bulk transfers are adequate for audio

     
    I agree that often bulk is ok; but I wouldn't say it's an elegant solution. It depends on what one needs.
    In my case - a learning project -  the *elegant* is important.
     
    And I wouldn't like to hear voice dying away while the HD works.
     
    I agree that sometimes one could sell a bulk project.
     
     

    quote:

    But as for throughput in itself I maintain that the problem isn't transmission speed: I achieved 200kHz in loopback tests with iso transfer, but that speed was useless for audio purposes.


    Why?

     
    I'm a teacher and a part of a teacher's job is trying to explain without being understood.
    (Let's put the blame also on my bad english, it's only my third language)
     
    As I said in my posts the problem is not the transmission speed, that is quite adequate, but the interrupt function, that has to be fast:
    you have an amount of cycles to employ; if you can employ these cycles to:
     
    1)receive/transmit data to/from usb
     
    2)maintain the queue
     
    3)produce/consume data
     
    you  are fine.
     
    If you can't do that transmission speed is useless.
     
    That's why I challenged assembler programmers.
    #6
    mzoran
    Super Member
    • Total Posts : 683
    • Reward points : 0
    • Status: offline
    RE: useless struggle for audio quality 2008/03/07 13:20:11 (permalink)
    0
    I agree the problem isn't the USB bus speed, it's the interrupt function to load the DAC/CODEC.   48KHz is simply too many interrupts for a 48MHz pic to do.  48KHz leaves only 250 instructions for the interrupt plus what other work the CPU is doing at the same time such as servicing standard USB requests.  250 instructions is not very much.
     
    Part of the problem is that I've only tried traditional mode.  Extended mode is probably faster.  In traditional mode, dereferencing a pointer + index is very expensive at least compared to other architectures such as X86.  I suspect a PIC24 or PIC32 with USB would be able to do a much better job.  Non debuggable optimizations in C18 don't seem to make a whole lot of difference in terms of execution time.
     
    I was able to clean up the soundcard code quite abit but I have not yet posted it to my website.  I was still unable to obtain 48KHz.
     
    I haven't taken one apart, but it wouldn't surprise me if commercial USB soundcards were ARM based or even custom chips.
    #7
    stefanopod
    Super Member
    • Total Posts : 1285
    • Reward points : 0
    • Joined: 2007/06/25 02:33:59
    • Location: Bologna,Italy
    • Status: offline
    RE: useless struggle for audio quality 2008/03/07 14:18:15 (permalink)
    0
    Hi mzoran,  I learned a lot from your example, thanks a lot.
    Unluckily I don't know anything about extended mode.
    I suppose one has to pay for it....
    But the issue sounds interesting.
    If you dig something in that direction, tell us .
    #8
    Olin Lathrop
    Super Member
    • Total Posts : 7463
    • Reward points : 0
    • Joined: 2004/02/26 17:59:01
    • Location: Littleton Massachusetts
    • Status: offline
    RE: useless struggle for audio quality 2008/03/08 06:32:53 (permalink)
    0
    48KHz leaves only 250 instructions for the interrupt plus what other work the CPU is doing at the same time such as servicing standard USB requests.  250 instructions is not very much.

    Sounds like a decent amount, actually.  I'm assuming that the PIC is basically reading data from the USB and writing it to some audio output means.  If there is anything else going on it takes a tiny fraction of the CPU and can tolerate latency so that the firmware can get around to it when it gets around to it.
     
    In traditional mode, dereferencing a pointer + index is very expensive at least compared to other architectures such as X86.

    First we're not talking about other architectures, we're talking about versus extended mode.  In traditional mode you either do the add and load the whole result into a FSR, or you load the base address into a FSR and the offset into W and use the PLUSW addressing mode.  I don't remember exactly how enhanced mode streamlines this, but let's say it makes this process 2 cycles faster somehow.  So this will decrease the total number of cycles to grab two bytes from the USB and output them to the audio device a bit, but not substantially decrease the requirement compared to 250 cycles.
     
    Good architecture and careful coding will get you a lot more cycles.
    #9
    stefanopod
    Super Member
    • Total Posts : 1285
    • Reward points : 0
    • Joined: 2007/06/25 02:33:59
    • Location: Bologna,Italy
    • Status: offline
    RE: useless struggle for audio quality 2008/03/08 07:32:56 (permalink)
    0
    If I remember well, passing from

    if (Record<NumberOfBytes/BytesPerRecord)//... in timer handler

    to:

    NumBerofRecords=NumberOfBytes/BytesPerRecord; //(in inzialization)
    if (Record<NumberOfRecords) //.....in timer handler

    I spared 8 microsecs and passed from 28 to 35 kHZ or something like.
     
    At the end I arrived to 40kHz, but the code is so tight that the slightest modification makes it to crash.
     
    I wish I were a bit of an assembler programmer.
     
    An interesting exercise, anyway.

     
    #10
    mzoran
    Super Member
    • Total Posts : 683
    • Reward points : 0
    • Status: offline
    RE: useless struggle for audio quality 2008/03/08 19:02:57 (permalink)
    0
    You might be right.  Perhaps extended mode is only better if the offset is a constant.   I need to review the documents again.  At any case, pointer deferences are expensive with 8 bit PICs.
     
    Here is my HighISR code.  It is about 120 instructions for the ISR not counting the time that gets spent in the loops.   The data needs to be preprocessed in another section of the code to convert it from 16bit signed to 12bit unsigned and put the data in the format that the Microchip DACs require.
     
    At any case, I think my whole point that I'm not convienced a single 8 bit PIC can do 48KHz.  If anybody has gotten it to work please let me know because I'm very interested.
     

    #pragma interrupt HighISR
    void HighISR (void)
    {
     if ( PIE1bits.TMR2IE && PIR1bits.TMR2IF )
     {
      static unsigned char Drain;
      static unsigned char * CurrentByte;
      PIR1bits.TMR2IF = 0;
      TotalSamplesPlayed++;
      CurrentByte = (unsigned char*)&audiostream_data_out[CurrentPlayBuffer];
      CurrentByte += PlayBufferBytesPlayed;
      LDAC = 1;
      DACCS = 0;
     
      PIR1bits.SSPIF = 0;
      SSPBUF = *CurrentByte++;
      while( !PIR1bits.SSPIF );
      Drain = SSPBUF;
      PIR1bits.SSPIF = 0;
      SSPBUF = *CurrentByte++;
      while( !PIR1bits.SSPIF );
      Drain = SSPBUF;
      DACCS = 1;
      DACCS = 0;
     
      PIR1bits.SSPIF = 0;
      SSPBUF = *CurrentByte++;
      while( !PIR1bits.SSPIF );
      Drain = SSPBUF;
      PIR1bits.SSPIF = 0;
      SSPBUF = *CurrentByte++;
      while( !PIR1bits.SSPIF );
      Drain = SSPBUF;
      DACCS = 1;
      LDAC = 0;
      PlayBufferBytesPlayed += 4;
      if ( PlayBufferBytesPlayed >= AUDIOSTREAM_DATA_EP_SIZE )
      {
       PIE1bits.TMR2IE = 0; 
       T2CON = 0;
      }
     }    
    }

     
    My Low ISR is also small.   It does the preprocessing of the data. 

    #pragma interruptlow LowISR
    void LowISR(void)
    {
     if ( PIR2bits.USBIF && PIE2bits.USBIE )
     {
      if ( EndpointInitalized )
      {

       if (UIRbits.SOFIF)
       {
        UIRbits.SOFIF = 0;
        PIE1bits.TMR2IE = 0;
                 // stop old frame
       
        T2CON = 0;
        if ( PlayBufferBytesPlayed )
        {
         Nop();
         PlayBufferBytesPlayed = 0;
         if ( PlayBufferSize[CurrentPlayBuffer] )
         {
          PlayBufferSize[CurrentPlayBuffer] = 0;
          CurrentPlayBuffer++;
          if (CurrentPlayBuffer >= 3)
           CurrentPlayBuffer = 0;
         }
        }
        // start new frame
        if ( PlayBufferSize[CurrentPlayBuffer] )
        {
         PIE1bits.TMR2IE = 1;
    #if defined(S32KHZ_SAMPLE)
         PR2  = 187;
         TMR2 = 187;
    #else
         PR2  = 249;
         TMR2 = 249;
    #endif
    #if defined(S48KHZ_SAMPLE)
         T2CON  = 0b00000100;     
    #else
         T2CON  = 0b00001100;
    #endif
        }
       }
        if ( !AUDIOSTREAM_DATA_OUT.Stat.UOWN )
       {
              //
              // Note that left and right channels
              // are swapped on the audiostreamer 1.0 board
              // DAC 0 is right and DAC 1 is left
        static unsigned char * BufferPointer;
        static unsigned char BufferPos;
        static signed short RawSample = 0;
        static unsigned short UnsignedSample;
        static unsigned char DACByte1;
        static unsigned char DACByte2;
        FramesReceived++;
        BufferPos = 0;
        BufferPointer = (byte*)&audiostream_data_out[CurrentReadBuffer];
        while( BufferPos < AUDIOSTREAM_DATA_OUT.Cnt )
        {
         RawSample = *(signed short*)BufferPointer;
         
               if ( RawSample >= 0 )
         {
          UnsignedSample = 0x8000;
          UnsignedSample += (unsigned short)RawSample;
               }
         else
         {
          UnsignedSample = 0x8000;
          UnsignedSample += RawSample;   
         }
         DACByte1  = 0b10010000;
         DACByte1 |= (unsigned char)(UnsignedSample >> 12);
         DACByte2 = 0;
         DACByte2 |= (unsigned char)((UnsignedSample >> 4) & 0xFF);     
         *BufferPointer++ = DACByte1;
         *BufferPointer++ = DACByte2;
         RawSample = *(signed short*)BufferPointer;
         
               if ( RawSample >= 0 )
         {
          UnsignedSample = 0x8000;
          UnsignedSample += (unsigned short)RawSample;
               }
         else
         {
          UnsignedSample = 0x8000;
          UnsignedSample += RawSample;   
         }
         DACByte1  = 0b00010000;
         DACByte1 |= (unsigned char)(UnsignedSample >> 12);
         DACByte2 = 0;
         DACByte2 |= (unsigned char)((UnsignedSample >> 4) & 0xFF);     
         *BufferPointer++ = DACByte1;
         *BufferPointer++ = DACByte2;
         BufferPos += 4;
        
        }
        
        PlayBufferSize[CurrentReadBuffer] = AUDIOSTREAM_DATA_OUT.Cnt;
        CurrentReadBuffer++;
        if (CurrentReadBuffer >= 3 )
         CurrentReadBuffer = 0;
        AUDIOSTREAM_DATA_OUT.Cnt = AUDIOSTREAM_DATA_EP_SIZE;     // Set buffer size
           AUDIOSTREAM_DATA_OUT.ADR = (byte*)&audiostream_data_out[CurrentReadBuffer];     // Set buffer address
           AUDIOSTREAM_DATA_OUT.Stat._byte &= _DTSMASK;          /* Save only DTS bit */     \
           AUDIOSTREAM_DATA_OUT.Stat.DTS = !AUDIOSTREAM_DATA_OUT.Stat.DTS; /* Toggle DTS bit    */     \
           AUDIOSTREAM_DATA_OUT.Stat._byte |= _USIE;      /* Turn ownership to SIE */ \
    //    mUSBBufferReady(AUDIOSTREAM_DATA_OUT);
       }
      }
      USBDriverService();
     }
    }

    #11
    stefanopod
    Super Member
    • Total Posts : 1285
    • Reward points : 0
    • Joined: 2007/06/25 02:33:59
    • Location: Bologna,Italy
    • Status: offline
    RE: useless struggle for audio quality 2008/03/09 01:28:08 (permalink)
    0
    Hi mzoran,
     you are always very kind in sharing.
    I see you have changed the structure of your program.
    You want me to keep on studying, but I love to be a bit of a student, though ignorantSmile.
    You put in the low interrupt routine a lot of stuff and that is interesting.
    In your former release, I hadn't found so useful to put in the low interrupt routine  USBDriverService, removing it from main loop; harmless but not so important or at least not clear to me.
    Now your taking away a lot of burden from timer  routine and handling it elsewhere asynchronously is  very interesting, promising  and clear in its purpose.
    As a first tryal, I'll put something more in my SOF handler; if that idea fails (SOF in my program is synchronized with timer) I'll try to copy shamelessly your structure.
    #12
    stefanopod
    Super Member
    • Total Posts : 1285
    • Reward points : 0
    • Joined: 2007/06/25 02:33:59
    • Location: Bologna,Italy
    • Status: offline
    RE: useless struggle for audio quality 2008/03/09 11:59:58 (permalink)
    0
    I tried almost everything.
    1) Putting usb r/w operation in SOF handler : bad, a lot of packets lost
    2)putting asynchronous usb r/w operation in main loop : not so  bad, but max frequency decreased ; a couple KHz worse.
    3)putting every usb operation in low interrupts, sampling in high : i loose some packets, not many but I can see them ( I don't know why it happens).
     
    Definitely my best is 41kHz for a bare minimum 8bit sampling/playing, putting synchronous and asynchronous(if ready) operations in the timer routine.
     One could steal a few cycles more with a more flat file structure eliminating all unnecessary functon calls and embedding *all* instructions in a single function. I made almost all this work , horrible for a C programmer.
    One thing is sure: to gain a kHz you loose at least an afternoon.
    #13
    mzoran
    Super Member
    • Total Posts : 683
    • Reward points : 0
    • Status: offline
    RE: useless struggle for audio quality 2008/03/09 14:34:45 (permalink)
    0
    I would be interested in seeing some of the code for your trials.
     
    I'm perfectly happy to swap ideas offline if you want.  We can share thoughts.   E-mail me at armag1234@comcast.net if you are interested.
    #14
    stefanopod
    Super Member
    • Total Posts : 1285
    • Reward points : 0
    • Joined: 2007/06/25 02:33:59
    • Location: Bologna,Italy
    • Status: offline
    RE: useless struggle for audio quality 2008/03/10 00:50:13 (permalink)
    0
    Good idea.
    Thanks

    provando e riprovando
    #15
    appleWiz
    Super Member
    • Total Posts : 191
    • Reward points : 0
    • Joined: 2005/09/24 13:17:59
    • Status: offline
    RE: useless struggle for audio quality 2008/03/10 04:56:42 (permalink)
    0
    Hi Group,

    I'm looking at an audio sampling project  - but at the opposite end of the spectrum - with communications bandwidth [:'(] and 8kHz sampling. Anyway, staying roughly on topic, I'm using extended mode. The USB stack compiles fine with it on.

    Was there a conclusion that the USB stack runs faster under ext mode?

    aW.

    #16
    stefanopod
    Super Member
    • Total Posts : 1285
    • Reward points : 0
    • Joined: 2007/06/25 02:33:59
    • Location: Bologna,Italy
    • Status: offline
    RE: useless struggle for audio quality 2008/03/10 09:34:06 (permalink)
    0
    A fast answer please, my compiler is going to expire....
    When I try extended mode I get
    Error - mixing extended and non-extended mode modules not allowed.
    Errors    : 1
    I removed from project my .lib files. What am I mixing?
    #17
    DarioG
    Allmächtig.
    • Total Posts : 54081
    • Reward points : 0
    • Joined: 2006/02/25 08:58:22
    • Location: Oesterreich
    • Status: offline
    RE: useless struggle for audio quality 2008/03/10 09:36:17 (permalink)
    0
    I post reply and forum ate it...

    Yes, after Expire, you can no longer use Extended Mode.

    To use it, you have to either Set the "Extended Mode" into the Project Build Options, and use "_e" LKR file.
    post edited by DarioG - 2008/03/10 09:39:58

    GENOVA :D :D ! GODO
    #18
    stefanopod
    Super Member
    • Total Posts : 1285
    • Reward points : 0
    • Joined: 2007/06/25 02:33:59
    • Location: Bologna,Italy
    • Status: offline
    RE: useless struggle for audio quality 2008/03/10 10:49:16 (permalink)
    0
    My question was only to test your being alert.
    And you forgot to mention the config bits!!!


    ThanksSmile
    post edited by stefanopod - 2008/03/10 10:54:18
    #19
    DarioG
    Allmächtig.
    • Total Posts : 54081
    • Reward points : 0
    • Joined: 2006/02/25 08:58:22
    • Location: Oesterreich
    • Status: offline
    RE: useless struggle for audio quality 2008/03/10 10:56:02 (permalink)
    0
    ORIGINAL: stefanopod
    My question was only to test your being alert.


    Here I am, as usual Smile


    And you forgot to mention the config bits!!!


    Yep, true Smile

    Glad it helped, Stefano!

    GENOVA :D :D ! GODO
    #20
    Page: 123 > Showing page 1 of 3
    Jump to:
    © 2019 APG vNext Commercial Version 4.5