kern/118395: [patch] Audio playback aborted with SNDCTL_DSP_SETTRIGGER

Henrik Gulbrandsen henrik at gulbra.net
Mon Dec 3 03:10:01 PST 2007


>Number:         118395
>Category:       kern
>Synopsis:       [patch] Audio playback aborted with SNDCTL_DSP_SETTRIGGER
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon Dec 03 11:10:00 UTC 2007
>Closed-Date:
>Last-Modified:
>Originator:     Henrik Gulbrandsen
>Release:        7.0-BETA3
>Organization:
>Environment:
FreeBSD Particle 7.0-BETA3 FreeBSD 7.0-BETA3 #2: Sat Dec  1 18:11:51 CET 2007
henrik at Particle:/usr/src/sys/i386/compile/GENERIC  i386

>Description:
Revision 1.121 of src/sys/dev/sound/pcm/channel.c introduced incorrect handling
of the SNDCTL_DSP_SETTRIGGER ioctl request. Normally, this request allows users
to delay the start of audio playback until the output buffer has been filled, as
documented at pages 104-105 of http://www.opensound.com/pguide/oss.pdf. Current
FreeBSD code interprets the blocked channel as an error and aborts playback.

This bug affects version 19 of PortAudio (the audio/portaudio2 port) and may be
the reason why the audio/audacity-devel port is explicitly configured with the
older version 18 of PortAudio.

>How-To-Repeat:
The attached shell archive includes the trigger.c example program, which fails
under FreeBSD 7.0-BETA3:

Particle$ ./trigger
Failed second SNDCTL_DSP_SETTRIGGER: Invalid argument
Particle$

>Fix:
The attached shell archive also includes the channel.c.patch file, which fixes
the problem:

Particle$ ./trigger
Everything worked.
Particle$

The patch also includes a corresponding fix for the recording case, which I have
not actually tested. Judging from code and documentation, it should be handled
in exactly the same way, though.


Patch attached with submission follows:

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	trigger.c
#	channel.c.patch
#
echo x - trigger.c
sed 's/^X//' >trigger.c << 'END-of-trigger.c'
X#include <errno.h>
X#include <fcntl.h>
X#include <stdio.h>
X#include <stdlib.h>
X#include <string.h>
X#include <sys/soundcard.h>
X
Xint main(int argc, char *argv[])
X{
X  int dev, flags, enable_bits;
X  char buffer[1024];
X
X  memset(buffer, 0, sizeof(buffer));
X
X  dev = open("/dev/dsp", O_WRONLY);
X  if (dev == -1) {
X    perror("Failed open");
X    return EXIT_FAILURE;
X  }
X
X  flags = fcntl(dev, F_GETFL);
X  if (flags == -1) {
X    perror("Failed F_GETFL");
X    return EXIT_FAILURE;
X  }
X
X  flags |= O_NONBLOCK;
X
X  if (fcntl(dev, F_SETFL, flags) == -1) {
X    perror("Failed F_SETFL");
X    return EXIT_FAILURE;
X  }
X
X  enable_bits = ~PCM_ENABLE_OUTPUT;
X  if (ioctl(dev, SNDCTL_DSP_SETTRIGGER, &enable_bits) == -1) {
X    perror("Failed first SNDCTL_DSP_SETTRIGGER");
X    return EXIT_FAILURE;
X  }
X
X  while (write(dev, buffer, sizeof(buffer)) != -1) {
X     /* Nothing */
X  }
X
X  if (errno != EAGAIN) {
X    perror("Unexpected write error");
X    return EXIT_FAILURE;
X  }
X
X  enable_bits = PCM_ENABLE_OUTPUT;
X  if (ioctl(dev, SNDCTL_DSP_SETTRIGGER, &enable_bits) == -1) {
X    perror("Failed second SNDCTL_DSP_SETTRIGGER");
X    return EXIT_FAILURE;
X  }
X
X  printf("Everything worked.\n");
X  return EXIT_SUCCESS;
X}
END-of-trigger.c
echo x - channel.c.patch
sed 's/^X//' >channel.c.patch << 'END-of-channel.c.patch'
X--- sys/dev/sound/pcm/channel.c.orig	2007-06-16 05:37:28.000000000 +0200
X+++ sys/dev/sound/pcm/channel.c	2007-12-03 10:53:48.000000000 +0100
X@@ -409,7 +409,7 @@
X 				sndbuf_acquire(bs, NULL, t);
X 			}
X 			ret = 0;
X-			if (CHN_STOPPED(c)) {
X+			if (CHN_STOPPED(c) && !(c->flags & CHN_F_NOTRIGGER)) {
X 				ret = chn_start(c, 0);
X 				if (ret != 0)
X 					c->flags |= CHN_F_DEAD;
X@@ -520,7 +520,7 @@
X 
X 	CHN_LOCKASSERT(c);
X 
X-	if (CHN_STOPPED(c)) {
X+	if (CHN_STOPPED(c) && !(c->flags & CHN_F_NOTRIGGER)) {
X 		ret = chn_start(c, 0);
X 		if (ret != 0) {
X 			c->flags |= CHN_F_DEAD;
END-of-channel.c.patch
exit



>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list