diff --git a/aserver/aserver.c b/aserver/aserver.c index 57d67531..8f05587b 100644 --- a/aserver/aserver.c +++ b/aserver/aserver.c @@ -609,6 +609,9 @@ static int ctl_shm_cmd(client_t *client) case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE: ctrl->result = snd_ctl_pcm_next_device(ctl, &ctrl->u.device); break; + case SND_CTL_IOCTL_PCM_SURROUND_NEXT_DEVICE: + ctrl->result = snd_ctl_pcm_surround_next_device(ctl, &ctrl->u.surround.type, &ctrl->u.surround.device); + break; case SNDRV_CTL_IOCTL_PCM_INFO: ctrl->result = snd_ctl_pcm_info(ctl, &ctrl->u.pcm_info); break; diff --git a/doc/Makefile.am b/doc/Makefile.am index 7b9c876a..9334c741 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,3 +1,6 @@ EXTRA_DIST= INCLUDES=-I$(top_srcdir)/include + +doc: + doxygen doxygen.cfg diff --git a/include/aserver.h b/include/aserver.h index 8f900416..9b97d150 100644 --- a/include/aserver.h +++ b/include/aserver.h @@ -89,6 +89,7 @@ typedef struct { #define SND_CTL_IOCTL_CLOSE _IO ('U', 0xf2) #define SND_CTL_IOCTL_POLL_DESCRIPTOR _IO ('U', 0xf3) #define SND_CTL_IOCTL_ASYNC _IO ('U', 0xf4) +#define SND_CTL_IOCTL_PCM_SURROUND_NEXT_DEVICE _IO ('U', 0xf5) typedef struct { int result; @@ -99,6 +100,10 @@ typedef struct { pid_t pid; } async; int device; + struct { + int type; + int device; + } surround; int subscribe_events; snd_ctl_card_info_t card_info; snd_ctl_elem_list_t element_list; diff --git a/include/control.h b/include/control.h index 418b1ce9..740e337b 100644 --- a/include/control.h +++ b/include/control.h @@ -272,6 +272,7 @@ int snd_ctl_elem_write(snd_ctl_t *ctl, snd_ctl_elem_value_t *value); int snd_ctl_hwdep_next_device(snd_ctl_t *ctl, int * device); int snd_ctl_hwdep_info(snd_ctl_t *ctl, snd_hwdep_info_t * info); int snd_ctl_pcm_next_device(snd_ctl_t *ctl, int *device); +int snd_ctl_pcm_surround_next_device(snd_ctl_t *ctl, snd_pcm_surround_type_t type, int *device); int snd_ctl_pcm_info(snd_ctl_t *ctl, snd_pcm_info_t * info); int snd_ctl_pcm_prefer_subdevice(snd_ctl_t *ctl, int subdev); int snd_ctl_rawmidi_next_device(snd_ctl_t *ctl, int * device); diff --git a/include/pcm.h b/include/pcm.h index 3308fafc..241d2093 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -12,7 +12,7 @@ typedef struct _snd_pcm_hw_params snd_pcm_hw_params_t; /** PCM software configuration container */ typedef struct _snd_pcm_sw_params snd_pcm_sw_params_t; /** PCM status container */ -typedef struct _snd_pcm_status snd_pcm_status_t; + typedef struct _snd_pcm_status snd_pcm_status_t; /** PCM access types mask */ typedef struct _snd_pcm_access_mask snd_pcm_access_mask_t; /** PCM formats mask */ @@ -196,6 +196,15 @@ typedef enum _snd_pcm_tstamp { SND_PCM_TSTAMP_LAST = SNDRV_PCM_TSTAMP_LAST, } snd_pcm_tstamp_t; +/** Surround type */ +typedef enum _snd_pcm_surround_type { + /** 4.0 speakers */ + SND_PCM_SURROUND_40 = 0, + /** 5.1 speakers */ + SND_PCM_SURROUND_51 = 1, + SND_PCM_SURROUND_LAST = SND_PCM_SURROUND_51 +} snd_pcm_surround_type_t; + /** Unsigned frames quantity */ typedef sndrv_pcm_uframes_t snd_pcm_uframes_t; /** Signed frames quantity */ @@ -266,6 +275,8 @@ enum _snd_pcm_type { SND_PCM_TYPE_DROUTE, /** Loopback server plugin (not yet implemented) */ SND_PCM_TYPE_LBSERVER, + /** Surround plugin */ + SND_PCM_TYPE_SURROUND, }; /** PCM type */ diff --git a/src/control/control.c b/src/control/control.c index 5b0e4b02..90a63542 100644 --- a/src/control/control.c +++ b/src/control/control.c @@ -265,6 +265,19 @@ int snd_ctl_pcm_next_device(snd_ctl_t *ctl, int * device) return ctl->ops->pcm_next_device(ctl, device); } +/** + * \brief Get next PCM surround device number + * \param ctl CTL handle + * \param type surround type + * \param device current device on entry and next device on return + * \return 0 on success otherwise a negative error code + */ +int snd_ctl_pcm_surround_next_device(snd_ctl_t *ctl, snd_pcm_surround_type_t type, int * device) +{ + assert(ctl && device); + return ctl->ops->pcm_surround_next_device(ctl, type, device); +} + /** * \brief Get info about a PCM device * \param ctl CTL handle diff --git a/src/control/control_hw.c b/src/control/control_hw.c index b07cdd93..5d6d4add 100644 --- a/src/control/control_hw.c +++ b/src/control/control_hw.c @@ -187,6 +187,12 @@ static int snd_ctl_hw_pcm_next_device(snd_ctl_t *handle, int * device) return 0; } +static int snd_ctl_hw_pcm_surround_next_device(snd_ctl_t *handle, snd_pcm_surround_type_t type, int * device) +{ + snd_ctl_hw_t *hw = handle->private_data; + return 0; +} + static int snd_ctl_hw_pcm_info(snd_ctl_t *handle, snd_pcm_info_t * info) { snd_ctl_hw_t *hw = handle->private_data; @@ -251,6 +257,7 @@ snd_ctl_ops_t snd_ctl_hw_ops = { hwdep_next_device: snd_ctl_hw_hwdep_next_device, hwdep_info: snd_ctl_hw_hwdep_info, pcm_next_device: snd_ctl_hw_pcm_next_device, + pcm_surround_next_device: snd_ctl_hw_pcm_surround_next_device, pcm_info: snd_ctl_hw_pcm_info, pcm_prefer_subdevice: snd_ctl_hw_pcm_prefer_subdevice, rawmidi_next_device: snd_ctl_hw_rawmidi_next_device, diff --git a/src/control/control_local.h b/src/control/control_local.h index 2e2f14d2..0246d5a3 100644 --- a/src/control/control_local.h +++ b/src/control/control_local.h @@ -36,6 +36,7 @@ typedef struct _snd_ctl_ops { int (*hwdep_next_device)(snd_ctl_t *handle, int *device); int (*hwdep_info)(snd_ctl_t *handle, snd_hwdep_info_t * info); int (*pcm_next_device)(snd_ctl_t *handle, int *device); + int (*pcm_surround_next_device)(snd_ctl_t *handle, snd_pcm_surround_type_t type, int *device); int (*pcm_info)(snd_ctl_t *handle, snd_pcm_info_t * info); int (*pcm_prefer_subdevice)(snd_ctl_t *handle, int subdev); int (*rawmidi_next_device)(snd_ctl_t *handle, int *device); diff --git a/src/control/control_shm.c b/src/control/control_shm.c index 5188e977..f525f178 100644 --- a/src/control/control_shm.c +++ b/src/control/control_shm.c @@ -249,6 +249,21 @@ static int snd_ctl_shm_pcm_next_device(snd_ctl_t *ctl, int * device) return err; } +static int snd_ctl_shm_pcm_surround_next_device(snd_ctl_t *ctl, snd_pcm_surround_type_t type, int * device) +{ + snd_ctl_shm_t *shm = ctl->private_data; + volatile snd_ctl_shm_ctrl_t *ctrl = shm->ctrl; + int err; + ctrl->u.surround.type = type; + ctrl->u.surround.device = *device; + ctrl->cmd = SND_CTL_IOCTL_PCM_SURROUND_NEXT_DEVICE; + err = snd_ctl_shm_action(ctl); + if (err < 0) + return err; + *device = ctrl->u.device; + return err; +} + static int snd_ctl_shm_pcm_info(snd_ctl_t *ctl, snd_pcm_info_t * info) { snd_ctl_shm_t *shm = ctl->private_data; @@ -350,6 +365,7 @@ snd_ctl_ops_t snd_ctl_shm_ops = { hwdep_next_device: snd_ctl_shm_hwdep_next_device, hwdep_info: snd_ctl_shm_hwdep_info, pcm_next_device: snd_ctl_shm_pcm_next_device, + pcm_surround_next_device: snd_ctl_shm_pcm_surround_next_device, pcm_info: snd_ctl_shm_pcm_info, pcm_prefer_subdevice: snd_ctl_shm_pcm_prefer_subdevice, rawmidi_next_device: snd_ctl_shm_rawmidi_next_device, diff --git a/src/pcm/Makefile.am b/src/pcm/Makefile.am index 971fab72..e7b6dcea 100644 --- a/src/pcm/Makefile.am +++ b/src/pcm/Makefile.am @@ -6,7 +6,7 @@ libpcm_la_SOURCES = atomic.c mask.c interval.c \ pcm_route.c pcm_mulaw.c pcm_alaw.c pcm_adpcm.c \ pcm_rate.c pcm_plug.c pcm_misc.c pcm_mmap.c pcm_multi.c \ pcm_shm.c pcm_file.c pcm_null.c pcm_share.c \ - pcm_meter.c pcm_params.c + pcm_meter.c pcm_params.c pcm_surr.c noinst_HEADERS = atomic.h pcm_local.h pcm_plugin.h mask.h mask_inline.h \ interval.h interval_inline.h plugin_ops.h diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 1187fdac..38c4ed5b 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -986,6 +986,18 @@ int snd_pcm_open(snd_pcm_t **pcmp, const char *name, } if (strcmp(name1, "null") == 0) return snd_pcm_null_open(pcmp, name, stream, mode); + err = sscanf(name1, "surround40:%d,%d", &card, &dev); + if (err == 2) + return snd_pcm_surround_open(pcmp, name, card, dev, SND_PCM_SURROUND_40, stream, mode); + err = sscanf(name1, "surround40:%d", &card); + if (err == 1) + return snd_pcm_surround_open(pcmp, name, card, 0, SND_PCM_SURROUND_40, stream, mode); + err = sscanf(name1, "surround51:%d,%d", &card, &dev); + if (err == 2) + return snd_pcm_surround_open(pcmp, name, card, dev, SND_PCM_SURROUND_51, stream, mode); + err = sscanf(name1, "surround51:%d", &card); + if (err == 1) + return snd_pcm_surround_open(pcmp, name, card, 0, SND_PCM_SURROUND_51, stream, mode); SNDERR("Unknown PCM %s", name1); return -ENOENT; } diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index f660351b..49a3df3f 100644 --- a/src/pcm/pcm_local.h +++ b/src/pcm/pcm_local.h @@ -195,6 +195,7 @@ int snd_pcm_plug_open_hw(snd_pcm_t **pcm, const char *name, int card, int device int snd_pcm_shm_open(snd_pcm_t **pcmp, const char *name, const char *sockname, const char *sname, snd_pcm_stream_t stream, int mode); int snd_pcm_file_open(snd_pcm_t **pcmp, const char *name, const char *fname, int fd, const char *fmt, snd_pcm_t *slave, int close_slave); int snd_pcm_null_open(snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode); +int snd_pcm_surround_open(snd_pcm_t **pcmp, const char *name, int card, int device, snd_pcm_surround_type_t type, snd_pcm_stream_t stream, int mode); void snd_pcm_areas_from_buf(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, void *buf); diff --git a/src/pcm/pcm_surr.c b/src/pcm/pcm_surr.c new file mode 100644 index 00000000..b70b74c6 --- /dev/null +++ b/src/pcm/pcm_surr.c @@ -0,0 +1,474 @@ +/* + * PCM - Surround plugin + * Copyright (c) 2001 by Jaroslav Kysela + * + * This plugin offers 4.0 and 5.1 surround devices with these routing: + * + * 1st channel - front left speaker + * 2nd channel - front rear speaker + * 3rd channel - rear left speaker + * 4th channel - rear right speaker + * 5th channel - center speaker + * 6th channel - LFE channel (woofer) + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include "pcm_local.h" +#include "pcm_plugin.h" + +typedef struct { + int card; /* card number */ + int device; /* device number */ + unsigned int channels; /* count of channels (4 or 6) */ + int pcms; /* count of PCM channels */ + snd_pcm_t *pcm[3]; /* up to three PCM stereo streams */ +} snd_pcm_surround_t; + +static int snd_pcm_surround_close(snd_pcm_t *pcm) +{ + int i; + snd_pcm_surround_t *surr = pcm->private_data; + for (i = 0; i < surr->pcms; i++) + snd_pcm_close(surr->pcm[i]); + free(surr); + return 0; +} + +static int snd_pcm_surround_nonblock(snd_pcm_t *pcm, int nonblock) +{ + int i, err = 0, err1; + snd_pcm_surround_t *surr = pcm->private_data; + for (i = 0; i < surr->pcms; i++) { + err1 = snd_pcm_nonblock(surr->pcm[i], nonblock); + if (err1 && !err) + err = err1; + } + return err; +} + +static int snd_pcm_surround_async(snd_pcm_t *pcm, int sig, pid_t pid) +{ + snd_pcm_surround_t *surr = pcm->private_data; + return snd_pcm_async(surr->pcm[0], sig, pid); +} + +static int snd_pcm_surround_info(snd_pcm_t *pcm, snd_pcm_info_t * info) +{ + snd_pcm_surround_t *surr = pcm->private_data; + memset(info, 0, sizeof(*info)); + info->stream = snd_enum_to_int(pcm->stream); + info->card = surr->card; + strncpy(info->id, "Surround", sizeof(info->id)); + strncpy(info->name, "Surround", sizeof(info->name)); + strncpy(info->subname, "Surround", sizeof(info->subname)); + info->subdevices_count = 1; + return 0; +} + +static int snd_pcm_surround_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info) +{ + snd_pcm_surround_t *surr = pcm->private_data; + if (surr->pcms == 1 || info->channel == 0 || info->channel == 1) + return snd_pcm_channel_info(surr->pcm[0], info); + if (surr->pcms > 1 && (info->channel == 2 || info->channel == 3)) + return snd_pcm_channel_info(surr->pcm[1], info); + if (surr->pcms > 2 && (info->channel == 3 || info->channel == 4)) + return snd_pcm_channel_info(surr->pcm[2], info); + return -EINVAL; +} + +static int snd_pcm_surround_status(snd_pcm_t *pcm, snd_pcm_status_t * status) +{ + snd_pcm_surround_t *surr = pcm->private_data; + return snd_pcm_status(surr->pcm[0], status); +} + +static snd_pcm_state_t snd_pcm_surround_state(snd_pcm_t *pcm) +{ + snd_pcm_surround_t *surr = pcm->private_data; + return snd_pcm_state(surr->pcm[0]); +} + +static int snd_pcm_surround_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp) +{ + snd_pcm_surround_t *surr = pcm->private_data; + return snd_pcm_delay(surr->pcm[0], delayp); +} + +static int snd_pcm_surround_prepare(snd_pcm_t *pcm) +{ + snd_pcm_surround_t *surr = pcm->private_data; + return snd_pcm_prepare(surr->pcm[0]); +} + +static int snd_pcm_surround_reset(snd_pcm_t *pcm) +{ + snd_pcm_surround_t *surr = pcm->private_data; + return snd_pcm_reset(surr->pcm[0]); +} + +static int snd_pcm_surround_start(snd_pcm_t *pcm) +{ + snd_pcm_surround_t *surr = pcm->private_data; + return snd_pcm_start(surr->pcm[0]); +} + +static int snd_pcm_surround_drop(snd_pcm_t *pcm) +{ + snd_pcm_surround_t *surr = pcm->private_data; + return snd_pcm_drop(surr->pcm[0]); +} + +static int snd_pcm_surround_drain(snd_pcm_t *pcm) +{ + snd_pcm_surround_t *surr = pcm->private_data; + return snd_pcm_drain(surr->pcm[0]); +} + +static int snd_pcm_surround_pause(snd_pcm_t *pcm, int enable) +{ + snd_pcm_surround_t *surr = pcm->private_data; + return snd_pcm_pause(surr->pcm[0], enable); +} + +static snd_pcm_sframes_t snd_pcm_surround_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames) +{ + snd_pcm_surround_t *surr = pcm->private_data; + return snd_pcm_rewind(surr->pcm[0], frames); +} + +static snd_pcm_sframes_t snd_pcm_surround_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size) +{ + snd_pcm_surround_t *surr = pcm->private_data; + if (surr->pcms == 1) + return snd_pcm_writei(pcm, buffer, size); + /* TODO: convert interleaved stream to two or three stereo streams */ + return -EIO; +} + +static snd_pcm_sframes_t snd_pcm_surround_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size) +{ + int i; + snd_pcm_sframes_t res = -1, res1; + snd_pcm_surround_t *surr = pcm->private_data; + for (i = 0; i < surr->pcms; i++) { + res1 = snd_pcm_writen(pcm, bufs, size); + if (res1 < 0) + return res1; + if (res < 0) + res = res1; + else if (res != res1) + return -EPIPE; + } + return res; +} + +static snd_pcm_sframes_t snd_pcm_surround_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size) +{ + snd_pcm_surround_t *surr = pcm->private_data; + if (surr->pcms == 1) + return snd_pcm_readi(pcm, buffer, size); + /* TODO: convert two or three stereo streams to one interleaved stream */ + return -EIO; +} + +static snd_pcm_sframes_t snd_pcm_surround_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size) +{ + int i; + snd_pcm_sframes_t res = -1, res1; + snd_pcm_surround_t *surr = pcm->private_data; + for (i = 0; i < surr->pcms; i++) { + res1 = snd_pcm_writen(pcm, bufs, size); + if (res1 < 0) + return res1; + if (res < 0) { + res = size = res1; + } else if (res != res1) + return -EPIPE; + } + return res; +} + +static snd_pcm_sframes_t snd_pcm_surround_mmap_forward(snd_pcm_t *pcm, snd_pcm_uframes_t size) +{ + int i; + snd_pcm_sframes_t res = -1, res1; + snd_pcm_surround_t *surr = pcm->private_data; + for (i = 0; i < surr->pcms; i++) { + res1 = snd_pcm_mmap_forward(surr->pcm[i], size); + if (res1 < 0) + return res1; + if (res < 0) { + res = size = res1; + } else if (res != res1) + return -EPIPE; + } + return res; +} + +static snd_pcm_sframes_t snd_pcm_surround_avail_update(snd_pcm_t *pcm) +{ + snd_pcm_surround_t *surr = pcm->private_data; + return snd_pcm_avail_update(surr->pcm[0]); +} + +static int snd_pcm_surround_interval_channels(snd_pcm_surround_t *surr, + snd_pcm_hw_params_t *params, + int refine) +{ + snd_interval_t *interval; + interval = ¶ms->intervals[SND_PCM_HW_PARAM_CHANNELS-SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; + if (interval->empty) + return -EINVAL; + if (interval->openmin) { + if (!refine) + return -EINVAL; + interval->min = surr->channels; + interval->openmin = 0; + } + if (interval->openmax) { + if (!refine) + return -EINVAL; + interval->max = surr->channels; + interval->openmax = 0; + } + if (refine && interval->min <= surr->channels && interval->max >= surr->channels) + interval->min = interval->max = surr->channels; + if (interval->min != interval->max || interval->min != surr->channels) + return -EINVAL; + if (surr->pcms != 1) + interval->min = interval->max = 2; + return 0; +} + +static int snd_pcm_surround_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) +{ + snd_pcm_surround_t *surr = pcm->private_data; + int i, err = snd_pcm_surround_interval_channels(surr, params, 1); + if (err < 0) + return err; + if (surr->pcms == 1) + return snd_pcm_hw_refine(surr->pcm[0], params); + for (i = 0; i < surr->pcms; i++) { + err = snd_pcm_hw_refine(surr->pcm[i], params); + if (err < 0) + return err; + } + return 0; +} + +static int snd_pcm_surround_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) +{ + snd_pcm_surround_t *surr = pcm->private_data; + int i, err = snd_pcm_surround_interval_channels(surr, params, 0); + if (err < 0) + return err; + if (surr->pcms == 1) + return snd_pcm_hw_params(surr->pcm[0], params); + for (i = 0; i < surr->pcms; i++) { + err = snd_pcm_hw_params(surr->pcm[i], params); + if (err < 0) + return err; + } + return 0; +} + +static int snd_pcm_surround_hw_free(snd_pcm_t *pcm ATTRIBUTE_UNUSED) +{ + int i, err; + snd_pcm_surround_t *surr = pcm->private_data; + for (i = 0; i < surr->pcms; i++) { + err = snd_pcm_hw_free(surr->pcm[i]); + if (err < 0) + return err; + } + return 0; +} + +static int snd_pcm_surround_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params) +{ + int i, err; + snd_pcm_surround_t *surr = pcm->private_data; + for (i = 0; i < surr->pcms; i++) { + err = snd_pcm_sw_params(surr->pcm[i], params); + if (err < 0) + return err; + } + return 0; +} + +static int snd_pcm_surround_mmap(snd_pcm_t *pcm) +{ + snd_pcm_surround_t *surr = pcm->private_data; + return 0; +} + +static int snd_pcm_surround_munmap(snd_pcm_t *pcm) +{ + snd_pcm_surround_t *surr = pcm->private_data; + return 0; +} + +static void snd_pcm_surround_dump(snd_pcm_t *pcm, snd_output_t *out) +{ + snd_output_printf(out, "Surround PCM\n"); + if (pcm->setup) { + snd_output_printf(out, "Its setup is:\n"); + snd_pcm_dump_setup(pcm, out); + } +} + +snd_pcm_ops_t snd_pcm_surround_ops = { + close: snd_pcm_surround_close, + info: snd_pcm_surround_info, + hw_refine: snd_pcm_surround_hw_refine, + hw_params: snd_pcm_surround_hw_params, + hw_free: snd_pcm_surround_hw_free, + sw_params: snd_pcm_surround_sw_params, + channel_info: snd_pcm_surround_channel_info, + dump: snd_pcm_surround_dump, + nonblock: snd_pcm_surround_nonblock, + async: snd_pcm_surround_async, + mmap: snd_pcm_surround_mmap, + munmap: snd_pcm_surround_munmap, +}; + +snd_pcm_fast_ops_t snd_pcm_surround_fast_ops = { + status: snd_pcm_surround_status, + state: snd_pcm_surround_state, + delay: snd_pcm_surround_delay, + prepare: snd_pcm_surround_prepare, + reset: snd_pcm_surround_reset, + start: snd_pcm_surround_start, + drop: snd_pcm_surround_drop, + drain: snd_pcm_surround_drain, + pause: snd_pcm_surround_pause, + rewind: snd_pcm_surround_rewind, + writei: snd_pcm_surround_writei, + writen: snd_pcm_surround_writen, + readi: snd_pcm_surround_readi, + readn: snd_pcm_surround_readn, + avail_update: snd_pcm_surround_avail_update, + mmap_forward: snd_pcm_surround_mmap_forward, +}; + +int snd_pcm_surround_open(snd_pcm_t **pcmp, const char *name, int card, int dev, + snd_pcm_surround_type_t type, + snd_pcm_stream_t stream, int mode) +{ + snd_pcm_t *pcm; + snd_pcm_surround_t *surr; + assert(pcmp); + if (stream == SND_PCM_STREAM_CAPTURE) + return -EINVAL; /* not supported at the time */ + surr = calloc(1, sizeof(snd_pcm_surround_t)); + if (!surr) { + return -ENOMEM; + } + + pcm = calloc(1, sizeof(snd_pcm_t)); + if (!pcm) { + free(surr); + return -ENOMEM; + } + if (name) + pcm->name = strdup(name); + pcm->type = SND_PCM_TYPE_SURROUND; + pcm->stream = stream; + pcm->mode = mode; + pcm->ops = &snd_pcm_surround_ops; + pcm->op_arg = pcm; + pcm->fast_ops = &snd_pcm_surround_fast_ops; + pcm->fast_op_arg = pcm; + pcm->private_data = surr; + pcm->poll_fd = -1; /* FIXME */ + pcm->hw_ptr = NULL; /* FIXME */ + pcm->appl_ptr = NULL; /* FIXME */ + *pcmp = pcm; + + return 0; +} + +int _snd_pcm_surround_open(snd_pcm_t **pcmp, const char *name, snd_config_t *conf, + snd_pcm_stream_t stream, int mode) +{ + snd_config_iterator_t i, next; + long card = -1, device = 0; + const char *str; + int err; + snd_pcm_surround_type_t type = SND_PCM_SURROUND_40; + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id = snd_config_get_id(n); + if (strcmp(id, "comment") == 0) + continue; + if (strcmp(id, "type") == 0) + continue; + if (strcmp(id, "card") == 0) { + err = snd_config_get_integer(n, &card); + if (err < 0) { + err = snd_config_get_string(n, &str); + if (err < 0) { + SNDERR("Invalid type for %s", id); + return -EINVAL; + } + card = snd_card_get_index(str); + if (card < 0) { + SNDERR("Invalid value for %s", id); + return card; + } + } + continue; + } + if (strcmp(id, "device") == 0) { + err = snd_config_get_integer(n, &device); + if (err < 0) { + SNDERR("Invalid type for %s", id); + return err; + } + continue; + } +#if 0 + if (strcmp(id, "subdevice") == 0) { + err = snd_config_get_integer(n, &subdevice); + if (err < 0) { + SNDERR("Invalid type for %s", id); + return err; + } + continue; + } +#endif + if (strcmp(id, "type") == 0) { + err = snd_config_get_string(n, &str); + if (strcmp(str, "40") == 0 || strcmp(str, "4.0") == 0) + type = SND_PCM_SURROUND_40; + else if (strcmp(str, "51") == 0 || strcmp(str, "5.1") == 0) + type = SND_PCM_SURROUND_51; + } + SNDERR("Unknown field %s", id); + return -EINVAL; + } + if (card < 0) { + SNDERR("card is not defined"); + return -EINVAL; + } + return snd_pcm_surround_open(pcmp, name, card, device, type, stream, mode); +}