--- sourceforge.net/trunk/rdesktop/rdpsnd_alsa.c 2006/09/17 10:32:18 1254 +++ sourceforge.net/trunk/rdesktop/rdpsnd_alsa.c 2007/01/02 11:39:56 1356 @@ -22,6 +22,7 @@ #include "rdesktop.h" #include "rdpsnd.h" +#include "rdpsnd_dsp.h" #include #include #include @@ -31,19 +32,86 @@ #define DEFAULTDEVICE "default" #define MAX_FRAMES 32 +static struct pollfd pfds[32]; +static int num_fds; + static snd_pcm_t *pcm_handle = NULL; static snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK; static BOOL reopened; static short samplewidth; static int audiochannels; +static unsigned int rate; +static char *pcm_name; -BOOL -wave_out_open(void) +void alsa_play(void); + +void +alsa_add_fds(int *n, fd_set * rfds, fd_set * wfds, struct timeval *tv) { - char *pcm_name; int err; + struct pollfd *f; + + if (!pcm_handle) + return; + + if (rdpsnd_queue_empty()) + return; + + num_fds = snd_pcm_poll_descriptors_count(pcm_handle); + + if (num_fds > sizeof(pfds) / sizeof(*pfds)) + return; + + err = snd_pcm_poll_descriptors(pcm_handle, pfds, num_fds); + if (err < 0) + return; + + for (f = pfds; f < &pfds[num_fds]; f++) + { + if (f->events & POLLIN) + FD_SET(f->fd, rfds); + if (f->events & POLLOUT) + FD_SET(f->fd, wfds); + if (f->fd > *n && (f->events & (POLLIN | POLLOUT))) + *n = f->fd; + } +} + +void +alsa_check_fds(fd_set * rfds, fd_set * wfds) +{ + struct pollfd *f; + int err; + unsigned short revents; + + if (!pcm_handle) + return; + + for (f = pfds; f < &pfds[num_fds]; f++) + { + f->revents = 0; + if (f->fd != -1) + { + /* Fixme: This doesn't properly deal with things like POLLHUP */ + if (FD_ISSET(f->fd, rfds)) + f->revents |= POLLIN; + if (FD_ISSET(f->fd, wfds)) + f->revents |= POLLOUT; + } + } + + err = snd_pcm_poll_descriptors_revents(pcm_handle, pfds, num_fds, &revents); + if (err < 0) + return; + + if (revents & POLLOUT) + alsa_play(); +} - pcm_name = xstrdup(DEFAULTDEVICE); +BOOL +alsa_open(void) +{ + int err; if ((err = snd_pcm_open(&pcm_handle, pcm_name, stream, 0)) < 0) { @@ -51,35 +119,27 @@ return False; } - g_dsp_fd = 0; - rdpsnd_queue_init(); - reopened = True; return True; } void -wave_out_close(void) +alsa_close(void) { /* Ack all remaining packets */ while (!rdpsnd_queue_empty()) - { - rdpsnd_send_completion(rdpsnd_queue_current_packet()->tick, - rdpsnd_queue_current_packet()->index); - rdpsnd_queue_next(); - } - + rdpsnd_queue_next(0); if (pcm_handle) { - snd_pcm_drop(pcm_handle); snd_pcm_close(pcm_handle); + pcm_handle = NULL; } } BOOL -wave_out_format_supported(WAVEFORMATEX * pwfx) +alsa_format_supported(WAVEFORMATEX * pwfx) { #if 0 int err; @@ -112,10 +172,9 @@ } BOOL -wave_out_set_format(WAVEFORMATEX * pwfx) +alsa_set_format(WAVEFORMATEX * pwfx) { snd_pcm_hw_params_t *hwparams = NULL; - unsigned int rate, exact_rate; int err; unsigned int buffertime; @@ -167,8 +226,8 @@ } #endif - exact_rate = rate = pwfx->nSamplesPerSec; - if ((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_rate, 0)) < 0) + rate = pwfx->nSamplesPerSec; + if ((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &rate, 0)) < 0) { error("snd_pcm_hw_params_set_rate_near: %s\n", snd_strerror(err)); return False; @@ -210,19 +269,7 @@ } void -wave_out_volume(uint16 left, uint16 right) -{ - static int warned = 0; - - if (!warned) - { - warning("volume changes currently not supported with experimental alsa-output\n"); - warned = 1; - } -} - -void -wave_out_play(void) +alsa_play(void) { struct audio_packet *packet; STREAM out; @@ -240,11 +287,9 @@ prev_us = tv.tv_usec; } + /* We shouldn't be called if the queue is empty, but still */ if (rdpsnd_queue_empty()) - { - g_dsp_busy = 0; return; - } packet = rdpsnd_queue_current_packet(); out = &packet->s; @@ -268,6 +313,9 @@ if ((out->p == out->end) || duration > next_tick - packet->tick + 500) { + snd_pcm_sframes_t delay_frames; + unsigned long delay_us; + prev_s = tv.tv_sec; prev_us = tv.tv_usec; @@ -278,10 +326,47 @@ (packet->tick + duration) % 65536, next_tick % 65536)); } - rdpsnd_send_completion(((packet->tick + duration) % 65536), packet->index); - rdpsnd_queue_next(); + if (snd_pcm_delay(pcm_handle, &delay_frames) < 0) + delay_frames = out->size / (samplewidth * audiochannels); + if (delay_frames < 0) + delay_frames = 0; + + delay_us = delay_frames * (1000000 / rate); + + rdpsnd_queue_next(delay_us); + } +} + +struct audio_driver * +alsa_register(char *options) +{ + static struct audio_driver alsa_driver; + + memset(&alsa_driver, 0, sizeof(alsa_driver)); + + alsa_driver.name = "alsa"; + alsa_driver.description = "ALSA output driver, default device: " DEFAULTDEVICE; + + alsa_driver.add_fds = alsa_add_fds; + alsa_driver.check_fds = alsa_check_fds; + + alsa_driver.wave_out_open = alsa_open; + alsa_driver.wave_out_close = alsa_close; + alsa_driver.wave_out_format_supported = alsa_format_supported; + alsa_driver.wave_out_set_format = alsa_set_format; + alsa_driver.wave_out_volume = rdpsnd_dsp_softvol_set; + + alsa_driver.need_byteswap_on_be = 0; + alsa_driver.need_resampling = 0; + + if (options) + { + pcm_name = xstrdup(options); + } + else + { + pcm_name = DEFAULTDEVICE; } - g_dsp_busy = 1; - return; + return &alsa_driver; }