[alsa-devel] Alsa-OSS Duplex bug (revisited)
Takashi Iwai
tiwai at suse.de
Wed May 30 19:05:05 CEST 2007
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;
More information about the Alsa-devel
mailing list