--- sourceforge.net/trunk/rdesktop/rdpsnd_oss.c 2003/10/22 12:16:03 509 +++ sourceforge.net/trunk/rdesktop/rdpsnd_oss.c 2006/09/17 11:42:22 1256 @@ -1,4 +1,4 @@ -/* +/* -*- c-basic-offset: 8 -*- rdesktop: A Remote Desktop Protocol client. Sound Channel Process Functions - Open Sound System Copyright (C) Matthew Chapman 2003 @@ -19,50 +19,50 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* + This is a workaround for Esound bug 312665. + FIXME: Remove this when Esound is fixed. +*/ +#ifdef _FILE_OFFSET_BITS +#undef _FILE_OFFSET_BITS +#endif + #include "rdesktop.h" +#include "rdpsnd.h" #include #include #include +#include #include #include -#define MAX_QUEUE 10 +#define DEFAULTDEVICE "/dev/dsp" +#define MAX_LEN 512 -int g_dsp_fd; -BOOL g_dsp_busy = False; - -static struct audio_packet -{ - struct stream s; - uint16 tick; - uint8 index; -} packet_queue[MAX_QUEUE]; -static unsigned int queue_hi, queue_lo; +static int snd_rate; +static short samplewidth; +static char *dsp_dev; BOOL -wave_out_open(void) +oss_open(void) { - char *dsp_dev = "/dev/dsp"; - - if ((g_dsp_fd = open(dsp_dev, O_WRONLY | O_NONBLOCK)) == -1) + if ((g_dsp_fd = open(dsp_dev, O_WRONLY)) == -1) { perror(dsp_dev); return False; } - /* Non-blocking so that user interface is responsive */ - fcntl(g_dsp_fd, F_SETFL, fcntl(g_dsp_fd, F_GETFL) | O_NONBLOCK); return True; } void -wave_out_close(void) +oss_close(void) { close(g_dsp_fd); } BOOL -wave_out_format_supported(WAVEFORMATEX * pwfx) +oss_format_supported(WAVEFORMATEX * pwfx) { if (pwfx->wFormatTag != WAVE_FORMAT_PCM) return False; @@ -75,9 +75,10 @@ } BOOL -wave_out_set_format(WAVEFORMATEX * pwfx) +oss_set_format(WAVEFORMATEX * pwfx) { - int speed, channels, format; + int stereo, format, fragments; + static BOOL driver_broken = False; ioctl(g_dsp_fd, SNDCTL_DSP_RESET, NULL); ioctl(g_dsp_fd, SNDCTL_DSP_SYNC, NULL); @@ -87,6 +88,8 @@ else if (pwfx->wBitsPerSample == 16) format = AFMT_S16_LE; + samplewidth = pwfx->wBitsPerSample / 8; + if (ioctl(g_dsp_fd, SNDCTL_DSP_SETFMT, &format) == -1) { perror("SNDCTL_DSP_SETFMT"); @@ -94,27 +97,61 @@ return False; } - channels = pwfx->nChannels; - if (ioctl(g_dsp_fd, SNDCTL_DSP_CHANNELS, &channels) == -1) + if (pwfx->nChannels == 2) + { + stereo = 1; + samplewidth *= 2; + } + else + { + stereo = 0; + } + + if (ioctl(g_dsp_fd, SNDCTL_DSP_STEREO, &stereo) == -1) { perror("SNDCTL_DSP_CHANNELS"); close(g_dsp_fd); return False; } - speed = pwfx->nSamplesPerSec; - if (ioctl(g_dsp_fd, SNDCTL_DSP_SPEED, &speed) == -1) + snd_rate = pwfx->nSamplesPerSec; + if (ioctl(g_dsp_fd, SNDCTL_DSP_SPEED, &snd_rate) == -1) { perror("SNDCTL_DSP_SPEED"); close(g_dsp_fd); return False; } + /* try to get 12 fragments of 2^12 bytes size */ + fragments = (12 << 16) + 12; + ioctl(g_dsp_fd, SNDCTL_DSP_SETFRAGMENT, &fragments); + + if (!driver_broken) + { + audio_buf_info info; + + memset(&info, 0, sizeof(info)); + if (ioctl(g_dsp_fd, SNDCTL_DSP_GETOSPACE, &info) == -1) + { + perror("SNDCTL_DSP_GETOSPACE"); + close(g_dsp_fd); + return False; + } + + if (info.fragments == 0 || info.fragstotal == 0 || info.fragsize == 0) + { + fprintf(stderr, + "Broken OSS-driver detected: fragments: %d, fragstotal: %d, fragsize: %d\n", + info.fragments, info.fragstotal, info.fragsize); + driver_broken = True; + } + } + return True; } void -wave_out_volume(uint16 left, uint16 right) +oss_volume(uint16 left, uint16 right) { static BOOL use_dev_mixer = False; uint32 volume; @@ -125,7 +162,7 @@ if (use_dev_mixer) { - if ((fd_mix = open( "/dev/mixer", O_RDWR|O_NONBLOCK )) == -1 ) + if ((fd_mix = open("/dev/mixer", O_RDWR | O_NONBLOCK)) == -1) { perror("open /dev/mixer"); return; @@ -149,65 +186,102 @@ } void -wave_out_write(STREAM s, uint16 tick, uint8 index) +oss_play(void) { - struct audio_packet *packet = &packet_queue[queue_hi]; - unsigned int next_hi = (queue_hi + 1) % MAX_QUEUE; + struct audio_packet *packet; + ssize_t len; + STREAM out; + static long startedat_us; + static long startedat_s; + static BOOL started = False; + struct timeval tv; - if (next_hi == queue_lo) + if (rdpsnd_queue_empty()) { - error("No space to queue audio packet\n"); + g_dsp_busy = 0; return; } - queue_hi = next_hi; + packet = rdpsnd_queue_current_packet(); + out = &packet->s; - packet->s = *s; - packet->tick = tick; - packet->index = index; - packet->s.p += 4; - - /* we steal the data buffer from s, give it a new one */ - s->data = malloc(s->size); + if (!started) + { + gettimeofday(&tv, NULL); + startedat_us = tv.tv_usec; + startedat_s = tv.tv_sec; + started = True; + } - if (!g_dsp_busy) - wave_out_play(); -} + len = out->end - out->p; -void -wave_out_play(void) -{ - struct audio_packet *packet; - ssize_t len; - STREAM out; + len = write(g_dsp_fd, out->p, (len > MAX_LEN) ? MAX_LEN : len); + if (len == -1) + { + if (errno != EWOULDBLOCK) + perror("write audio"); + g_dsp_busy = 1; + return; + } - while (1) + out->p += len; + if (out->p == out->end) { - if (queue_lo == queue_hi) - { - g_dsp_busy = 0; - return; - } + long long duration; + long elapsed; - packet = &packet_queue[queue_lo]; - out = &packet->s; + gettimeofday(&tv, NULL); + duration = (out->size * (1000000 / (samplewidth * snd_rate))); + elapsed = (tv.tv_sec - startedat_s) * 1000000 + (tv.tv_usec - startedat_us); - len = write(g_dsp_fd, out->p, out->end - out->p); - if (len == -1) + if (elapsed >= (duration * 85) / 100) + { + /* We need to add 50 to tell windows that time has passed while + * playing this packet */ + rdpsnd_send_completion(packet->tick + 50, packet->index); + rdpsnd_queue_next(); + started = False; + } + else { - if (errno != EWOULDBLOCK) - perror("write audio"); g_dsp_busy = 1; return; } + } + g_dsp_busy = 1; + return; +} + +struct audio_driver * +oss_register(char *options) +{ + static struct audio_driver oss_driver; + + oss_driver.wave_out_write = rdpsnd_queue_write; + oss_driver.wave_out_open = oss_open; + oss_driver.wave_out_close = oss_close; + oss_driver.wave_out_format_supported = oss_format_supported; + oss_driver.wave_out_set_format = oss_set_format; + oss_driver.wave_out_volume = oss_volume; + oss_driver.wave_out_play = oss_play; + oss_driver.name = xstrdup("oss"); + oss_driver.description = + xstrdup("OSS output driver, default device: " DEFAULTDEVICE " or $AUDIODEV"); + oss_driver.next = NULL; + + if (options) + { + dsp_dev = xstrdup(options); + } + else + { + dsp_dev = getenv("AUDIODEV"); - out->p += len; - if (out->p == out->end) + if (dsp_dev == NULL) { - rdpsnd_send_completion(packet->tick, packet->index); - free(out->data); - queue_lo = (queue_lo + 1) % MAX_QUEUE; + dsp_dev = xstrdup(DEFAULTDEVICE); } } + return &oss_driver; }