Dominic Mazzoni
2005-03-30 09:23:01 UTC
Hello Phil, Ross, Richard, Stephane, Greg, Darren, and everyone else,
For over a year I've been meaning to put some serious time into the v19
version of pa_mac_core, especially as I've spent a lot of time fiddling
with CoreAudio with several different audio devices. Finally, I found
the time, and over the last couple of weeks I rewrote pa_mac_core from
scratch. Of course I borrowed lots of code from previous versions (so,
many thanks to Phil, Darren, Gord, Stephane, Greg, and any others who
may have contributed code), but I decided it would be worth it to start
with pa_skeleton and redesign it from scratch. Among other
differences, the new version uses the PortAudio BufferProcessor, and it
has far more sophisticated code for opening the audio device that
should support everything from built-in devices to high-end
USB/Firewire audio devices quite well.
If anyone wants to try it out, here's the code. More info on how well
it works so far is below.
http://spaghetticode.org/portaudio/2005-03-29/pa_mac_core.c
http://spaghetticode.org/portaudio/2005-03-29/pa_mac_core.h
First of all, here are the tests that work! I tried most of them with
at least a couple of different devices (including different
input/output pairs).
patest_buffer
patest_clip
patest_dither
patest_hang
patest_latency
patest_leftright
patest_longsine
patest_many
patest_maxsines
patest_multi_sine
patest_pink
patest_record
patest_ringmix
patest_saw
patest_sine
patest_sine8
patest_sine_formats
patest_sine_time
patest_start_stop
patest_sync
patest_toomanysines
patest_underflow
patest_wire (one caveat, see below)
Here are the tests that don't work, and the error messages I get. It
appears that perhaps I just have one main bug, relating to what value I
return from IsStreamStopped and IsStreamActive under different
circumstances. I would appreciate it if someone could explain to me in
greater detail how this is supposed to work. I'm clearly
misinterpreting the docs.
patest_callbackstop
An error occured while using the portaudio stream
Error number: -9983
Error message: Stream is stopped
patest_in_overflow
Test failed, no input overflows detected under stress."
patest_out_underflow
Test failed, no output underflows detected under stress.
patest_prime
An error occured while using the portaudio stream
Error number: -9983
Error message: Stream is stopped
patest_read_record
(What is it supposed to do? It exits too quickly...)
patest_stop
An error occured while using the portaudio stream
Error number: -9983
Error message: Stream is stopped
Test of MODE_FINISH failed!
patest_underflow
SleepTime = 91
Try to stop stream.
An error occured while using the portaudio stream
Error number: -9983
Error message: Stream is stopped
patest_write_sine
(It exits too quickly...)
Besides the broken tests above, there are three more things that I am
aware of that need to be fixed:
* Need to handle the case where the Mac device has a different number
of channels than the user requested. For example, some mic inputs only
support 1 channel, and the main output device often only supports 2
channels. I think this would be easier to handle within pa_mac_core
with some interleaving/de-interleaving code, rather than using an
AudioConverter, as v18 did. Unfortunately it's a little bit tricky
because on the Mac, multiple channels can be split across multiple
multi-channel buffers - for example, 4 channels can arrive as 2
buffers, each with 2 channels. Worse, each buffer can either be
interleaved or noninterleaved. It would be nice if BufferProcessor
helped with this, don't you think?
Currently the code runs, but doesn't do the interleaving. So calling
patest_wire (which is mono) on a device that only supports stereo, for
example, results in a distorted, but barely recognizable, sound.
* Blocking I/O still needs to be implemented, I think. I haven't
written any code for ReadStream / WriteStream, etc. - am I supposed to?
If so, how??? I noticed that other supposedly "complete" v19
implementations leave these functions blank, too...
* I should really add listeners for important events, and if the stream
format changes, either adapt to it, or stop the stream.
For those that are interested, I implemented a platform-specific
selector that allows the client program to have far greater control
over what happens when they open an audio device (and when they call
IsFormatSupported).
The issue is that if you change an audio device's settings, this
usually disrupts other programs that are also playing audio. Some
programs want to play nice, and just open the audio device however it's
already set up. That will allow you, for example, to play some sound
effects while iTunes is still playing in the background. Other
programs want to play nice, but they'd strongly prefer to open the
device rather than not at all, so if it's necessary to modify the
device in order to make it usable, that's okay - otherwise play nice.
Yet other programs want to modify the device if it would get them
better quality (more channels, higher sample rate), but otherwise are
happy to play nice. Finally, some programs actually would prefer to
have exclusive access to the device and kick other programs off for the
duration!
All of these modes of operation, and several shades of gray in-between,
are supported, using the following new API:
typedef enum
{
paModifyToMatchMinimumReqs = 1,
paModifyToMatchLooseReqs = 2, /* Default */
paModifyToMatchStrictReqs = 4,
paModifyIfHelpful = 8,
paRequireAtLeastNumChannels = 16, /* Default */
paRequireExactNumChannels = 32,
paRequireAtLeastSampleRate = 64, /* Default */
paRequireExactSampleRate = 128,
paRequireAtLeastSampleFormat = 256,
paRequireExactSampleFormat = 512,
paExclusiveAccess = 1024
} PaMacCore_DeviceAccessFlags;
void PaMacCore_SetDeviceAccessFlags(PaMacCore_DeviceAccessFlags
newFlags);
All of the flags are fully documented in pa_mac_core.h - please check
them out and tell me what you think. I tried hard to come up with a
smaller set of flags that were just as expressive, but couldn't come up
with anything I was happy with.
I wanted to ask about PortMixer, too, but I'll put that in a separate
email, so that we can keep the dicussion threads separate.
Regards,
Dominic
For over a year I've been meaning to put some serious time into the v19
version of pa_mac_core, especially as I've spent a lot of time fiddling
with CoreAudio with several different audio devices. Finally, I found
the time, and over the last couple of weeks I rewrote pa_mac_core from
scratch. Of course I borrowed lots of code from previous versions (so,
many thanks to Phil, Darren, Gord, Stephane, Greg, and any others who
may have contributed code), but I decided it would be worth it to start
with pa_skeleton and redesign it from scratch. Among other
differences, the new version uses the PortAudio BufferProcessor, and it
has far more sophisticated code for opening the audio device that
should support everything from built-in devices to high-end
USB/Firewire audio devices quite well.
If anyone wants to try it out, here's the code. More info on how well
it works so far is below.
http://spaghetticode.org/portaudio/2005-03-29/pa_mac_core.c
http://spaghetticode.org/portaudio/2005-03-29/pa_mac_core.h
First of all, here are the tests that work! I tried most of them with
at least a couple of different devices (including different
input/output pairs).
patest_buffer
patest_clip
patest_dither
patest_hang
patest_latency
patest_leftright
patest_longsine
patest_many
patest_maxsines
patest_multi_sine
patest_pink
patest_record
patest_ringmix
patest_saw
patest_sine
patest_sine8
patest_sine_formats
patest_sine_time
patest_start_stop
patest_sync
patest_toomanysines
patest_underflow
patest_wire (one caveat, see below)
Here are the tests that don't work, and the error messages I get. It
appears that perhaps I just have one main bug, relating to what value I
return from IsStreamStopped and IsStreamActive under different
circumstances. I would appreciate it if someone could explain to me in
greater detail how this is supposed to work. I'm clearly
misinterpreting the docs.
patest_callbackstop
An error occured while using the portaudio stream
Error number: -9983
Error message: Stream is stopped
patest_in_overflow
Test failed, no input overflows detected under stress."
patest_out_underflow
Test failed, no output underflows detected under stress.
patest_prime
An error occured while using the portaudio stream
Error number: -9983
Error message: Stream is stopped
patest_read_record
(What is it supposed to do? It exits too quickly...)
patest_stop
An error occured while using the portaudio stream
Error number: -9983
Error message: Stream is stopped
Test of MODE_FINISH failed!
patest_underflow
SleepTime = 91
Try to stop stream.
An error occured while using the portaudio stream
Error number: -9983
Error message: Stream is stopped
patest_write_sine
(It exits too quickly...)
Besides the broken tests above, there are three more things that I am
aware of that need to be fixed:
* Need to handle the case where the Mac device has a different number
of channels than the user requested. For example, some mic inputs only
support 1 channel, and the main output device often only supports 2
channels. I think this would be easier to handle within pa_mac_core
with some interleaving/de-interleaving code, rather than using an
AudioConverter, as v18 did. Unfortunately it's a little bit tricky
because on the Mac, multiple channels can be split across multiple
multi-channel buffers - for example, 4 channels can arrive as 2
buffers, each with 2 channels. Worse, each buffer can either be
interleaved or noninterleaved. It would be nice if BufferProcessor
helped with this, don't you think?
Currently the code runs, but doesn't do the interleaving. So calling
patest_wire (which is mono) on a device that only supports stereo, for
example, results in a distorted, but barely recognizable, sound.
* Blocking I/O still needs to be implemented, I think. I haven't
written any code for ReadStream / WriteStream, etc. - am I supposed to?
If so, how??? I noticed that other supposedly "complete" v19
implementations leave these functions blank, too...
* I should really add listeners for important events, and if the stream
format changes, either adapt to it, or stop the stream.
For those that are interested, I implemented a platform-specific
selector that allows the client program to have far greater control
over what happens when they open an audio device (and when they call
IsFormatSupported).
The issue is that if you change an audio device's settings, this
usually disrupts other programs that are also playing audio. Some
programs want to play nice, and just open the audio device however it's
already set up. That will allow you, for example, to play some sound
effects while iTunes is still playing in the background. Other
programs want to play nice, but they'd strongly prefer to open the
device rather than not at all, so if it's necessary to modify the
device in order to make it usable, that's okay - otherwise play nice.
Yet other programs want to modify the device if it would get them
better quality (more channels, higher sample rate), but otherwise are
happy to play nice. Finally, some programs actually would prefer to
have exclusive access to the device and kick other programs off for the
duration!
All of these modes of operation, and several shades of gray in-between,
are supported, using the following new API:
typedef enum
{
paModifyToMatchMinimumReqs = 1,
paModifyToMatchLooseReqs = 2, /* Default */
paModifyToMatchStrictReqs = 4,
paModifyIfHelpful = 8,
paRequireAtLeastNumChannels = 16, /* Default */
paRequireExactNumChannels = 32,
paRequireAtLeastSampleRate = 64, /* Default */
paRequireExactSampleRate = 128,
paRequireAtLeastSampleFormat = 256,
paRequireExactSampleFormat = 512,
paExclusiveAccess = 1024
} PaMacCore_DeviceAccessFlags;
void PaMacCore_SetDeviceAccessFlags(PaMacCore_DeviceAccessFlags
newFlags);
All of the flags are fully documented in pa_mac_core.h - please check
them out and tell me what you think. I tried hard to come up with a
smaller set of flags that were just as expressive, but couldn't come up
with anything I was happy with.
I wanted to ask about PortMixer, too, but I'll put that in a separate
email, so that we can keep the dicussion threads separate.
Regards,
Dominic