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; _______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxx http://mailman.alsa-project.org/mailman/listinfo/alsa-devel