diff --git a/rdpsnd_alsa.c b/rdpsnd_alsa.c index 69f8f78..5a15142 100644 --- a/rdpsnd_alsa.c +++ b/rdpsnd_alsa.c @@ -4,6 +4,7 @@ Copyright (C) Matthew Chapman 2003 Copyright (C) GuoJunBo guojunbo@ict.ac.cn 2003 Copyright (C) Michael Gernoth mike@zerfleddert.de 2006 + Copyright 2006 Pierre Ossman for Cendio AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -32,18 +33,29 @@ #define DEFAULTDEVICE "default" #define MAX_FRAMES 32 -static struct pollfd pfds[32]; -static int num_fds; +static struct pollfd pfds_out[32]; +static int num_fds_out; + +static struct pollfd pfds_in[32]; +static int num_fds_in; + +static snd_pcm_t *out_handle = NULL; +static snd_pcm_t *in_handle = NULL; -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 short samplewidth_out; +static int audiochannels_out; +static unsigned int rate_out; + +static short samplewidth_in; +static int audiochannels_in; +static unsigned int rate_in; + static char *pcm_name; void alsa_play(void); +void alsa_record(void); void alsa_add_fds(int *n, fd_set * rfds, fd_set * wfds, struct timeval *tv) @@ -51,29 +63,48 @@ alsa_add_fds(int *n, fd_set * rfds, fd_set * wfds, struct timeval *tv) 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 (out_handle && !rdpsnd_queue_empty()) { - 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; + num_fds_out = snd_pcm_poll_descriptors_count(out_handle); + + if (num_fds_out > sizeof(pfds_out) / sizeof(*pfds_out)) + return; + + err = snd_pcm_poll_descriptors(out_handle, pfds_out, num_fds_out); + if (err < 0) + return; + + for (f = pfds_out; f < &pfds_out[num_fds_out]; 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; + } + } + + if (in_handle) + { + num_fds_in = snd_pcm_poll_descriptors_count(in_handle); + + if (num_fds_in > sizeof(pfds_in) / sizeof(*pfds_in)) + return; + + err = snd_pcm_poll_descriptors(in_handle, pfds_in, num_fds_in); + if (err < 0) + return; + + for (f = pfds_in; f < &pfds_in[num_fds_in]; 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; + } } } @@ -84,36 +115,156 @@ alsa_check_fds(fd_set * rfds, fd_set * wfds) int err; unsigned short revents; - if (!pcm_handle) - return; - - for (f = pfds; f < &pfds[num_fds]; f++) + if (out_handle && !rdpsnd_queue_empty()) { - f->revents = 0; - if (f->fd != -1) + for (f = pfds_out; f < &pfds_out[num_fds_out]; f++) { - /* 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; + 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(out_handle, pfds_out, num_fds_out, &revents); + if (err < 0) + return; + + if (revents & POLLOUT) + alsa_play(); + } + + + if (in_handle) + { + for (f = pfds_in; f < &pfds_in[num_fds_in]; 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(in_handle, pfds_in, num_fds_in, &revents); + if (err < 0) + return; + + if (revents & POLLIN) + alsa_record(); + } +} + +static BOOL +alsa_set_format(snd_pcm_t * pcm, WAVEFORMATEX * pwfx) +{ + snd_pcm_hw_params_t *hwparams = NULL; + int err; + unsigned int buffertime; + short samplewidth; + int audiochannels; + unsigned int rate; + + samplewidth = pwfx->wBitsPerSample / 8; + + if ((err = snd_pcm_hw_params_malloc(&hwparams)) < 0) + { + error("snd_pcm_hw_params_malloc: %s\n", snd_strerror(err)); + return False; + } + + if ((err = snd_pcm_hw_params_any(pcm, hwparams)) < 0) + { + error("snd_pcm_hw_params_any: %s\n", snd_strerror(err)); + return False; + } + + if ((err = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) + { + error("snd_pcm_hw_params_set_access: %s\n", snd_strerror(err)); + return False; + } + + if (pwfx->wBitsPerSample == 16) + { + if ((err = snd_pcm_hw_params_set_format(pcm, hwparams, SND_PCM_FORMAT_S16_LE)) < 0) + { + error("snd_pcm_hw_params_set_format: %s\n", snd_strerror(err)); + return False; + } + } + else + { + if ((err = snd_pcm_hw_params_set_format(pcm, hwparams, SND_PCM_FORMAT_S8)) < 0) + { + error("snd_pcm_hw_params_set_format: %s\n", snd_strerror(err)); + return False; } } - err = snd_pcm_poll_descriptors_revents(pcm_handle, pfds, num_fds, &revents); - if (err < 0) - return; +#if 0 + if ((err = snd_pcm_hw_params_set_rate_resample(pcm, hwparams, 1)) < 0) + { + error("snd_pcm_hw_params_set_rate_resample: %s\n", snd_strerror(err)); + return False; + } +#endif - if (revents & POLLOUT) - alsa_play(); + rate = pwfx->nSamplesPerSec; + if ((err = snd_pcm_hw_params_set_rate_near(pcm, hwparams, &rate, 0)) < 0) + { + error("snd_pcm_hw_params_set_rate_near: %s\n", snd_strerror(err)); + return False; + } + + audiochannels = pwfx->nChannels; + if ((err = snd_pcm_hw_params_set_channels(pcm, hwparams, pwfx->nChannels)) < 0) + { + error("snd_pcm_hw_params_set_channels: %s\n", snd_strerror(err)); + return False; + } + + + buffertime = 500000; /* microseconds */ + if ((err = snd_pcm_hw_params_set_buffer_time_near(pcm, hwparams, &buffertime, 0)) < 0) + { + error("snd_pcm_hw_params_set_buffer_time_near: %s\n", snd_strerror(err)); + return False; + } + + if ((err = snd_pcm_hw_params(pcm, hwparams)) < 0) + { + error("snd_pcm_hw_params: %s\n", snd_strerror(err)); + return False; + } + + snd_pcm_hw_params_free(hwparams); + + if ((err = snd_pcm_prepare(pcm)) < 0) + { + error("snd_pcm_prepare: %s\n", snd_strerror(err)); + return False; + } + + reopened = True; + + return True; } BOOL -alsa_open(void) +alsa_open_out(void) { int err; - if ((err = snd_pcm_open(&pcm_handle, pcm_name, stream, 0)) < 0) + if ((err = snd_pcm_open(&out_handle, pcm_name, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { error("snd_pcm_open: %s\n", snd_strerror(err)); return False; @@ -125,16 +276,16 @@ alsa_open(void) } void -alsa_close(void) +alsa_close_out(void) { /* Ack all remaining packets */ while (!rdpsnd_queue_empty()) rdpsnd_queue_next(0); - if (pcm_handle) + if (out_handle) { - snd_pcm_close(pcm_handle); - pcm_handle = NULL; + snd_pcm_close(out_handle); + out_handle = NULL; } } @@ -172,98 +323,14 @@ alsa_format_supported(WAVEFORMATEX * pwfx) } BOOL -alsa_set_format(WAVEFORMATEX * pwfx) +alsa_set_format_out(WAVEFORMATEX * pwfx) { - snd_pcm_hw_params_t *hwparams = NULL; - int err; - unsigned int buffertime; - - samplewidth = pwfx->wBitsPerSample / 8; - - if ((err = snd_pcm_hw_params_malloc(&hwparams)) < 0) - { - error("snd_pcm_hw_params_malloc: %s\n", snd_strerror(err)); + if (!alsa_set_format(out_handle, pwfx)) return False; - } - if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) - { - error("snd_pcm_hw_params_any: %s\n", snd_strerror(err)); - return False; - } - - if ((err = - snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) - { - error("snd_pcm_hw_params_set_access: %s\n", snd_strerror(err)); - return False; - } - - if (pwfx->wBitsPerSample == 16) - { - if ((err = - snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE)) < 0) - { - error("snd_pcm_hw_params_set_format: %s\n", snd_strerror(err)); - return False; - } - } - else - { - if ((err = - snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S8)) < 0) - { - error("snd_pcm_hw_params_set_format: %s\n", snd_strerror(err)); - return False; - } - } - -#if 0 - if ((err = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 1)) < 0) - { - error("snd_pcm_hw_params_set_rate_resample: %s\n", snd_strerror(err)); - return False; - } -#endif - - 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; - } - - audiochannels = pwfx->nChannels; - if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, pwfx->nChannels)) < 0) - { - error("snd_pcm_hw_params_set_channels: %s\n", snd_strerror(err)); - return False; - } - - - buffertime = 500000; /* microseconds */ - if ((err = - snd_pcm_hw_params_set_buffer_time_near(pcm_handle, hwparams, &buffertime, 0)) < 0) - { - error("snd_pcm_hw_params_set_buffer_time_near: %s\n", snd_strerror(err)); - return False; - } - - if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) - { - error("snd_pcm_hw_params: %s\n", snd_strerror(err)); - return False; - } - - snd_pcm_hw_params_free(hwparams); - - if ((err = snd_pcm_prepare(pcm_handle)) < 0) - { - error("snd_pcm_prepare: %s\n", snd_strerror(err)); - return False; - } - - reopened = True; + samplewidth_out = pwfx->wBitsPerSample / 8; + audiochannels_out = pwfx->nChannels; + rate_out = pwfx->nSamplesPerSec; return True; } @@ -296,13 +363,14 @@ alsa_play(void) next_tick = rdpsnd_queue_next_tick(); - len = (out->end - out->p) / (samplewidth * audiochannels); - if ((len = snd_pcm_writei(pcm_handle, out->p, ((MAX_FRAMES < len) ? MAX_FRAMES : len))) < 0) + len = (out->end - out->p) / (samplewidth_out * audiochannels_out); + if ((len = snd_pcm_writei(out_handle, out->p, ((MAX_FRAMES < len) ? MAX_FRAMES : len))) < 0) { - snd_pcm_prepare(pcm_handle); + printf("Fooo!\n"); + snd_pcm_prepare(out_handle); len = 0; } - out->p += (len * samplewidth * audiochannels); + out->p += (len * samplewidth_out * audiochannels_out); gettimeofday(&tv, NULL); @@ -326,17 +394,80 @@ alsa_play(void) (packet->tick + duration) % 65536, next_tick % 65536)); } - if (snd_pcm_delay(pcm_handle, &delay_frames) < 0) - delay_frames = out->size / (samplewidth * audiochannels); + if (snd_pcm_delay(out_handle, &delay_frames) < 0) + delay_frames = out->size / (samplewidth_out * audiochannels_out); if (delay_frames < 0) delay_frames = 0; - delay_us = delay_frames * (1000000 / rate); + delay_us = delay_frames * (1000000 / rate_out); rdpsnd_queue_next(delay_us); } } +BOOL +alsa_open_in(void) +{ + int err; + + if ((err = + snd_pcm_open(&in_handle, pcm_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) + { + error("snd_pcm_open: %s\n", snd_strerror(err)); + return False; + } + + return True; +} + +void +alsa_close_in(void) +{ + if (in_handle) + { + snd_pcm_close(in_handle); + in_handle = NULL; + } +} + +BOOL +alsa_set_format_in(WAVEFORMATEX * pwfx) +{ + int err; + + if (!alsa_set_format(in_handle, pwfx)) + return False; + + if ((err = snd_pcm_start(in_handle)) < 0) + { + error("snd_pcm_start: %s\n", snd_strerror(err)); + return False; + } + + samplewidth_in = pwfx->wBitsPerSample / 8; + audiochannels_in = pwfx->nChannels; + rate_in = pwfx->nSamplesPerSec; + + return True; +} + +void +alsa_record(void) +{ + int len; + char buffer[32768]; + + len = snd_pcm_readi(in_handle, buffer, + sizeof(buffer) / (samplewidth_in * audiochannels_in)); + if (len < 0) + { + snd_pcm_prepare(in_handle); + len = 0; + } + + rdpsnd_record(buffer, len * samplewidth_in * audiochannels_in); +} + struct audio_driver * alsa_register(char *options) { @@ -350,12 +481,18 @@ alsa_register(char *options) 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_open = alsa_open_out; + alsa_driver.wave_out_close = alsa_close_out; alsa_driver.wave_out_format_supported = alsa_format_supported; - alsa_driver.wave_out_set_format = alsa_set_format; + alsa_driver.wave_out_set_format = alsa_set_format_out; alsa_driver.wave_out_volume = rdpsnd_dsp_softvol_set; + alsa_driver.wave_in_open = alsa_open_in; + alsa_driver.wave_in_close = alsa_close_in; + alsa_driver.wave_in_format_supported = alsa_format_supported; + alsa_driver.wave_in_set_format = alsa_set_format_in; + alsa_driver.wave_in_volume = NULL; /* FIXME */ + alsa_driver.need_byteswap_on_be = 0; alsa_driver.need_resampling = 0;