At Wed, 30 May 2007 08:47:14 +0200, Clemens Ladisch wrote:
Alan Horstmann wrote:
On Tuesday 29 May 2007 17:55, you wrote:
While I see the advantage by your hack (small and backward compatible), I feel that it's too hackish -- it introduces an incompatible way of the existing ioctl.
Do you mean in that ioctls in general are not meant to work like that, in effect passing 2 numbers rather than one?
No, packing two values into one numbers is done with other ioctls too, e.g., SNDCTL_DSP_SETFRAGMENT.
The problem I see is that your patch changes the OSS API in a way that is incompatible with every implementation other than ALSA's. The whole purpose of providing the OSS API in the first place is to be compatible with other implementations.
Exactly.
After a quick thought, another possible fix would be to let apps open each direction separately. For that,
- add some way to make the given PCM stream to non-fullduplex (proc or module options?)
Are you thinking that if an option were set, Alsa-OSS would create separate devices for capture and playback rather than a single duplex device?
No; just a way to disable DSP_CAP_DUPLEX and/or SNDCTL_DSP_SETDUPLEX. When these do not work, applications are forced to open the playback and capture devices separately, e.g.:
fd_playback = open("/dev/dsp", O_WRONLY); fd_capture = open("/dev/dsp", O_RDONLY);
I thought of a similar hack but it seems that many apps don't check / set DUPLEX capability. So, this might not work on many apps.
- change portaudio to open each direction separately, O_RDONLY and O_WRONLY at first, then use O_RDWR as fallback
This would be the preferred way. After all, this is the only way that is possible with the existing OSS API when you want to use different sample formats, and it is recommended in all cases (see http://manuals.opensound.com/developer/full_duplex.html).
Right. For example, in the case of audacity, the change would be like the following patch (untested).
Takashi
--- lib-src/portaudio/pa_unix_oss/pa_unix.c-dist 2007-05-30 17:52:10.000000000 +0200 +++ lib-src/portaudio/pa_unix_oss/pa_unix.c 2007-05-30 18:22:09.000000000 +0200 @@ -720,12 +720,45 @@ int Pa_GetMinNumBuffers( int framesPerBu }
/*******************************************************************/ + +static int do_open(internalPortAudioDevice *pad, int flags, int verbose) +{ + int fd = open(pad->pad_DeviceName, flags); + if (fd < 0) + { + if (verbose) + ERR_RPT(("PaHost_OpenStream: could not open %s for %s\n", + pad->pad_DeviceName, + (flags == O_RDONLY ? "O_RDONLY" : + (flags == O_WRONLY ? "O_WRONLY" : "O_RDWR")))); + } + return fd; +} + +static int open_device(internalPortAudioDevice *pad, int flags, int verbose) +{ + int fd; + + /* dmazzoni: test it first in nonblocking mode to + make sure the device is not busy */ + fd = do_open(pad->pad_DeviceName, flags|O_NONBLOCK, verbose); + if (fd < 0) + return BAD_DEVICE_ID; + close(fd); + + fd = do_open(pad->pad_DeviceName, flags, verbose); + if (fd < 0) + return BAD_DEVICE_ID; + return fd; +} + PaError PaHost_OpenStream( internalPortAudioStream *past ) { PaError result = paNoError; PaHostSoundControl *pahsc; unsigned int minNumBuffers; internalPortAudioDevice *pad; + int full_duplex; DBUG(("PaHost_OpenStream() called.\n" ));
/* Allocate and initialize host data. */ @@ -778,102 +811,89 @@ PaError PaHost_OpenStream( internalPortA
/* ------------------------- OPEN DEVICE -----------------------*/
- /* just output */ - if (past->past_OutputDeviceID == past->past_InputDeviceID) - { - - if ((past->past_NumOutputChannels > 0) && (past->past_NumInputChannels > 0) ) - { - pad = Pa_GetInternalDevice( past->past_OutputDeviceID ); - DBUG(("PaHost_OpenStream: attempt to open %s for O_RDWR\n", pad->pad_DeviceName )); - - /* dmazzoni: test it first in nonblocking mode to - make sure the device is not busy */ - pahsc->pahsc_InputHandle = open(pad->pad_DeviceName,O_RDWR|O_NONBLOCK); - if(pahsc->pahsc_InputHandle==-1) - { - ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDWR\n", pad->pad_DeviceName )); - result = paHostError; - goto error; - } - close(pahsc->pahsc_InputHandle); - - pahsc->pahsc_OutputHandle = pahsc->pahsc_InputHandle = - open(pad->pad_DeviceName,O_RDWR); - if(pahsc->pahsc_InputHandle==-1) - { - ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDWR\n", pad->pad_DeviceName )); - result = paHostError; - goto error; - } - Pa_SetLatency( pahsc->pahsc_OutputHandle, - past->past_NumUserBuffers, past->past_FramesPerUserBuffer, - past->past_NumOutputChannels ); - result = Pa_SetupDeviceFormat( pahsc->pahsc_OutputHandle, - past->past_NumOutputChannels, (int)past->past_SampleRate ); - } - } + if (past->past_OutputDeviceID == past->past_InputDeviceID && + past->past_NumOutputChannels > 0 && + past->past_NumInputChannels > 0) + full_duplex = 1; else + full_duplex = 0; + + /* try to open each direction separately, at first */ + if (past->past_NumOutputChannels > 0) { - if (past->past_NumOutputChannels > 0) - { - pad = Pa_GetInternalDevice( past->past_OutputDeviceID ); - DBUG(("PaHost_OpenStream: attempt to open %s for O_WRONLY\n", pad->pad_DeviceName )); - /* dmazzoni: test it first in nonblocking mode to - make sure the device is not busy */ - pahsc->pahsc_OutputHandle = open(pad->pad_DeviceName,O_WRONLY|O_NONBLOCK); - if(pahsc->pahsc_OutputHandle==-1) - { - ERR_RPT(("PaHost_OpenStream: could not open %s for O_WRONLY\n", pad->pad_DeviceName )); - result = paHostError; - goto error; - } - close(pahsc->pahsc_OutputHandle); - - pahsc->pahsc_OutputHandle = open(pad->pad_DeviceName,O_WRONLY); - if(pahsc->pahsc_OutputHandle==-1) - { - ERR_RPT(("PaHost_OpenStream: could not open %s for O_WRONLY\n", pad->pad_DeviceName )); - result = paHostError; - goto error; - } - Pa_SetLatency( pahsc->pahsc_OutputHandle, - past->past_NumUserBuffers, past->past_FramesPerUserBuffer, - past->past_NumOutputChannels ); - result = Pa_SetupOutputDeviceFormat( pahsc->pahsc_OutputHandle, - past->past_NumOutputChannels, (int)past->past_SampleRate ); - } + pad = Pa_GetInternalDevice( past->past_OutputDeviceID ); + DBUG(("PaHost_OpenStream: attempt to open %s for O_WRONLY\n", pad->pad_DeviceName )); + pahsc->pahsc_OutputHandle = open_device(pad, O_WRONLY, + !full_duplex); + if (pahsc->pahsc_OutputHandle < 0) + { + result = paHostError; + goto check_duplex; + } + Pa_SetLatency( pahsc->pahsc_OutputHandle, + past->past_NumUserBuffers, past->past_FramesPerUserBuffer, + past->past_NumOutputChannels ); + result = Pa_SetupOutputDeviceFormat( pahsc->pahsc_OutputHandle, + past->past_NumOutputChannels, (int)past->past_SampleRate ); + if (result < 0) + { + close(pahsc->pahsc_OutputHandle); + pahsc->pahsc_OutputHandle = BAD_DEVICE_ID; + goto check_duplex; + } + } + + if (past->past_NumInputChannels > 0) + { + pad = Pa_GetInternalDevice( past->past_InputDeviceID ); + + DBUG(("PaHost_OpenStream: attempt to open %s for O_RDONLY\n", pad->pad_DeviceName )); + pahsc->pahsc_InputHandle = open_device(pad, O_RDONLY, + !full_duplex); + if (pahsc->pahsc_InputHandle < 0) + { + result = paHostError; + goto check_duplex; + } + Pa_SetLatency( pahsc->pahsc_InputHandle, /* DH20010115 - was OutputHandle! */ + past->past_NumUserBuffers, past->past_FramesPerUserBuffer, + past->past_NumInputChannels ); + result = Pa_SetupInputDeviceFormat( pahsc->pahsc_InputHandle, + past->past_NumInputChannels, (int)past->past_SampleRate ); + if (result < 0) + { + close(pahsc->pahsc_InputHandle); + pahsc->pahsc_InputHandle = BAD_DEVICE_ID; + goto check_duplex; + } + } + + check_duplex: + /* if opening one of the direction fails for full-duplex, + * try to open a single device with O_RDWR + */ + if (full_duplex && + (phasc->phasc_OutputHandle < 0 || phasc->phasc_InputHandle < 0)) + { + pad = Pa_GetInternalDevice( past->past_OutputDeviceID ); + DBUG(("PaHost_OpenStream: attempt to open %s for O_RDWR\n", pad->pad_DeviceName ));
- if (past->past_NumInputChannels > 0) - { - pad = Pa_GetInternalDevice( past->past_InputDeviceID ); - DBUG(("PaHost_OpenStream: attempt to open %s for O_RDONLY\n", pad->pad_DeviceName )); - /* dmazzoni: test it first in nonblocking mode to - make sure the device is not busy */ - pahsc->pahsc_InputHandle = open(pad->pad_DeviceName,O_RDONLY|O_NONBLOCK); - if(pahsc->pahsc_InputHandle==-1) - { - ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDONLY\n", pad->pad_DeviceName )); - result = paHostError; - goto error; - } - close(pahsc->pahsc_InputHandle); - - pahsc->pahsc_InputHandle = open(pad->pad_DeviceName,O_RDONLY); - if(pahsc->pahsc_InputHandle==-1) - { - ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDONLY\n", pad->pad_DeviceName )); - result = paHostError; - goto error; - } - Pa_SetLatency( pahsc->pahsc_InputHandle, /* DH20010115 - was OutputHandle! */ - past->past_NumUserBuffers, past->past_FramesPerUserBuffer, - past->past_NumInputChannels ); - result = Pa_SetupInputDeviceFormat( pahsc->pahsc_InputHandle, - past->past_NumInputChannels, (int)past->past_SampleRate ); - } + pahsc->pahsc_OutputHandle = open_device(pad, O_RDWR, 1); + if (phasc->pahsc_OutputHandle < 0) + { + result = paHostError; + goto error; + } + phasc->phasc_InputHandle = phasc->phasc_OutputHandle; + Pa_SetLatency( pahsc->pahsc_OutputHandle, + past->past_NumUserBuffers, past->past_FramesPerUserBuffer, + past->past_NumOutputChannels ); + result = Pa_SetupDeviceFormat( pahsc->pahsc_OutputHandle, + past->past_NumOutputChannels, (int)past->past_SampleRate ); + } - + if (result < 0) + goto error;
DBUG(("PaHost_OpenStream: SUCCESS - result = %d\n", result )); return result;