• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: Noisy DefaultOutputUnit
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Noisy DefaultOutputUnit


  • Subject: Re: Noisy DefaultOutputUnit
  • From: Kurt Revis <email@hidden>
  • Date: Sat, 26 Jan 2008 16:44:44 -0800

On Jan 26, 2008, at 12:57 PM, Paul Scott wrote:

Now, while experimenting with DefaultOutputUnit, I discovered some interesting problems. Running the program, unmodified, produces nasty clicks in the speaker (on two iMacs and a MacBook Pro, and presumably on all Macs) when switching sampling rates. The clicks seem to arise when CFRunLoopRunInMode() times out.

That's not the root cause, though. I don't think this program was ever intended to be click-free; there are at least two things that are causing them. I can fix one but maybe not the other.


Also, I expect that this would never actually happen in your app. Why would you need to change the sample rate of the data you're playing? Just pick a reasonable value and stick with it. In other words, I'd pay attention to the code that sets up, starts, and stops the AU, and how data is provided to it. The outer wrapper, which that does all of that multiple times with different settings, is not really relevant.

Let's look at what's going on in TestDefaultAU(). This all happens each time that main() runs a given configuration (bit depth, channel count, and sample rate).

1) It sets up the AU's input format with the current sample rate, encoding, etc.
2) and calls AudioUnitInitialize() on it
3) and calls AudioOutputUnitStart() on it.


Sometime after this, the AU will start calling MyRenderer(), *on a separate thread*.

4) CFRunLoopRunInMode(kCFRunLoopDefaultMode, 2, false) is called.

For your purposes, it sits and does nothing for two seconds. (It may be servicing notifications from CoreAudio during those two seconds, but you don't have to worry about that.)

5) Calls AudioOutputUnitStop()

The AU will stop calling MyRenderer() and stop playing sound. This call will not return until things are completely stopped (that is, after it returns, we're guaranteed that MyRenderer() is done and won't be called again).

6) Calls AudioUnitUninitialize().


Moving or removing AudioUnitStop(), AudioUnitStart(), AudioUnitUninitialize(), etc. had no effect.

In my tests, they actually are causing one of the clicks.

Try changing sampleRateList[] to have several instances of the same rate in a row (e.g. 8000.0, 8000.0, 8000.0,...). You'll still get a click between each one, even though the rate hasn't actually changed. This is because stopping and restarting the AU is a heavyweight operation.

Now comment out the calls to AudioOutputUnitStop, AudioUnitUninitialize, and AudioUnitReset. Also, change the code that calls AudioUnitInitialize and AudioOutputUnitStart, so it only calls them once (the first time through). You'll get no clicks between subsequent calls to TestDefaultAU() which use the same sample rate.

So, in other words: if you don't want clicks, just start the output AU once. Don't expect to stop and start it without some discontinuity.

Now: We're still getting clicks when the sample rate is changed. I don't know how to fix that one, if it is fixable. (Is it expected to be able to change the AUs sample rate w/o a discontinuity?)

(Also, note that the way sSampleRate is set is not thread-safe. It is changed before telling the AU about the new rate, so in the meantime, on the audio thread, MyRender() could pick up the new value of sSampleRate while the AU is still using the old rate. This can't happen of course if you only change sSampleRate while the AU is stopped.)


3) If my application is Cocoa based, can I run this Core Audio code with CFRunLoopRun() on its own thread?

Yes, but it isn't necessary. You can start and stop the AU on the main thread. There is no need to call CFRunLoopRun() yourself, since Cocoa already does it for you. (Keep in mind that MyRenderer() will always get called on another thread.)


Let's imagine you have two buttons, one to start the audio and one to stop. Your code would look like this:

- (void) start: (id) sender
{
// set up the AU's input format with the current sample rate, encoding, etc.
// call AudioUnitInitialize() on it
// call AudioOutputUnitStart() on it


// Then just return. Cocoa will run the run loop for you. No need to call CFRunLoopRun() yourself.
}


- (void) stop: (id) sender
{
	// Call AudioOutputUnitStop().
	// Call AudioUnitUninitialize().
}

Does this help at all?

--
Kurt Revis
email@hidden

_______________________________________________
Do not post admin requests to the list. They will be ignored.
Coreaudio-api mailing list      (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden


  • Follow-Ups:
    • Re: Noisy DefaultOutputUnit
      • From: Paul Scott <email@hidden>
    • Re: Noisy DefaultOutputUnit
      • From: Paul Scott <email@hidden>
References: 
 >Noisy DefaultOutputUnit (From: Paul Scott <email@hidden>)

  • Prev by Date: Re: Noisy DefaultOutputUnit
  • Next by Date: Re: Noisy DefaultOutputUnit
  • Previous by thread: Re: Noisy DefaultOutputUnit
  • Next by thread: Re: Noisy DefaultOutputUnit
  • Index(es):
    • Date
    • Thread