diff options
author | Jaroslav Kysela <perex@perex.cz> | 2000-11-20 20:10:46 +0000 |
---|---|---|
committer | Jaroslav Kysela <perex@perex.cz> | 2000-11-20 20:10:46 +0000 |
commit | 41bb7068f2e8b54c73874f40efcc89204f9992e0 (patch) | |
tree | 849db082c98e4442e480d5b673bd42c2ab0ec339 | |
parent | 3cc2b957fbe73dcdf34f34ce70e6b85e06d915d0 (diff) |
Merged pcmfinal branch.
57 files changed, 5100 insertions, 2999 deletions
@@ -1,3 +1,3 @@ M think about xrun recovery helpers -M add abstraction layer to timer, rawmidi, hwdep, seq +M add abstraction layer to timer, hwdep L move OSS emulation to user space? (pseudo device driver and daemon) diff --git a/aserver/aserver.c b/aserver/aserver.c index 5e54d0b0..3b326ff8 100644 --- a/aserver/aserver.c +++ b/aserver/aserver.c @@ -379,14 +379,20 @@ int pcm_shm_cmd(client_t *client) case SND_PCM_IOCTL_INFO: ctrl->result = snd_pcm_info(pcm, (snd_pcm_info_t *) &ctrl->u.info); break; - case SND_PCM_IOCTL_PARAMS: - ctrl->result = snd_pcm_params(pcm, (snd_pcm_params_t *) &ctrl->u.params); + case SND_PCM_IOCTL_HW_INFO: + ctrl->result = snd_pcm_hw_info(pcm, (snd_pcm_hw_info_t *) &ctrl->u.hw_info); break; - case SND_PCM_IOCTL_PARAMS_INFO: - ctrl->result = snd_pcm_params_info(pcm, (snd_pcm_params_info_t *) &ctrl->u.params_info); + case SND_PCM_IOCTL_HW_PARAMS: + ctrl->result = snd_pcm_hw_params(pcm, (snd_pcm_hw_params_t *) &ctrl->u.hw_params); break; - case SND_PCM_IOCTL_SETUP: - ctrl->result = snd_pcm_setup(pcm, (snd_pcm_setup_t *) &ctrl->u.setup); + case SND_PCM_IOCTL_SW_PARAMS: + ctrl->result = snd_pcm_sw_params(pcm, (snd_pcm_sw_params_t *) &ctrl->u.sw_params); + break; + case SND_PCM_IOCTL_DIG_PARAMS: + ctrl->result = snd_pcm_dig_params(pcm, (snd_pcm_dig_params_t *) &ctrl->u.dig_params); + break; + case SND_PCM_IOCTL_DIG_INFO: + ctrl->result = snd_pcm_dig_info(pcm, (snd_pcm_dig_info_t *) &ctrl->u.dig_info); break; case SND_PCM_IOCTL_STATUS: ctrl->result = snd_pcm_status(pcm, (snd_pcm_status_t *) &ctrl->u.status); @@ -422,12 +428,9 @@ int pcm_shm_cmd(client_t *client) break; case SND_PCM_IOCTL_CHANNEL_INFO: ctrl->result = snd_pcm_channel_info(pcm, (snd_pcm_channel_info_t *) &ctrl->u.channel_info); - break; - case SND_PCM_IOCTL_CHANNEL_PARAMS: - ctrl->result = snd_pcm_channel_params(pcm, (snd_pcm_channel_params_t *) &ctrl->u.channel_params); - break; - case SND_PCM_IOCTL_CHANNEL_SETUP: - ctrl->result = snd_pcm_channel_setup(pcm, (snd_pcm_channel_setup_t *) &ctrl->u.channel_setup); + if (ctrl->result >= 0 && + ctrl->u.channel_info.type == SND_PCM_AREA_MMAP) + return shm_ack_fd(client, ctrl->u.channel_info.u.mmap.fd); break; case SND_PCM_IOCTL_REWIND: ctrl->result = snd_pcm_rewind(pcm, ctrl->u.rewind.frames); @@ -444,27 +447,7 @@ int pcm_shm_cmd(client_t *client) break; case SND_PCM_IOCTL_MMAP: { - err = snd_pcm_mmap(pcm); - if (err < 0) - ctrl->result = err; - else - ctrl->result = pcm->mmap_info_count; - break; - } - case SND_PCM_IOCTL_MMAP_INFO: - { - unsigned int index = ctrl->u.mmap_info.index; - snd_pcm_mmap_info_t *i = &pcm->mmap_info[index]; - if (index >= pcm->mmap_info_count) { - ctrl->result = -EINVAL; - break; - } - ctrl->u.mmap_info = *i; - ctrl->u.mmap_info.index = index; - ctrl->result = 0; - if (i->type == SND_PCM_MMAP_USER) - break; - return shm_ack_fd(client, i->u.kernel.fd); + ctrl->result = snd_pcm_mmap(pcm); } case SND_PCM_IOCTL_MUNMAP: { diff --git a/include/aserver.h b/include/aserver.h index 85823fd8..6106d43a 100644 --- a/include/aserver.h +++ b/include/aserver.h @@ -28,7 +28,6 @@ #define SND_PCM_IOCTL_AVAIL_UPDATE _IO ('A', 0xf8) #define SND_PCM_IOCTL_ASYNC _IO ('A', 0xf9) #define SND_PCM_IOCTL_CLOSE _IO ('A', 0xfa) -#define SND_PCM_IOCTL_MMAP_INFO _IO ('A', 0xfb) #define SND_PCM_IOCTL_POLL_DESCRIPTOR _IO ('A', 0xfc) #define SND_PCM_IOCTL_SET_AVAIL_MIN _IO ('A', 0xfd) @@ -42,11 +41,12 @@ typedef struct { int sig; pid_t pid; } async; - snd_pcm_mmap_info_t mmap_info; snd_pcm_info_t info; - snd_pcm_params_t params; - snd_pcm_params_info_t params_info; - snd_pcm_setup_t setup; + snd_pcm_hw_info_t hw_info; + snd_pcm_hw_params_t hw_params; + snd_pcm_sw_params_t sw_params; + snd_pcm_dig_params_t dig_params; + snd_pcm_dig_info_t dig_info; snd_pcm_status_t status; struct { ssize_t frames; @@ -55,8 +55,6 @@ typedef struct { int enable; } pause; snd_pcm_channel_info_t channel_info; - snd_pcm_channel_params_t channel_params; - snd_pcm_channel_setup_t channel_setup; struct { ssize_t frames; } rewind; diff --git a/include/conf.h b/include/conf.h index d2858380..00ae6f99 100644 --- a/include/conf.h +++ b/include/conf.h @@ -1,14 +1,14 @@ -typedef enum { +typedef enum _snd_config_type { SND_CONFIG_TYPE_INTEGER, SND_CONFIG_TYPE_REAL, SND_CONFIG_TYPE_STRING, SND_CONFIG_TYPE_COMPOUND, } snd_config_type_t; -typedef struct snd_config snd_config_t; +typedef struct _snd_config snd_config_t; -struct snd_config { +struct _snd_config { char *id; snd_config_type_t type; union { diff --git a/include/control.h b/include/control.h index 864a39e4..f786b014 100644 --- a/include/control.h +++ b/include/control.h @@ -5,14 +5,14 @@ * * ****************************************************************************/ -typedef struct snd_ctl snd_ctl_t; +typedef struct _snd_ctl snd_ctl_t; -typedef enum { SND_CTL_TYPE_HW, +typedef enum _snd_ctl_type { SND_CTL_TYPE_HW, SND_CTL_TYPE_SHM, SND_CTL_TYPE_INET } snd_ctl_type_t; -typedef struct snd_ctl_callbacks { +typedef struct _snd_ctl_callbacks { void *private_data; /* may be used by an application */ void (*rebuild) (snd_ctl_t *handle, void *private_data); void (*value) (snd_ctl_t *handle, void *private_data, snd_control_id_t * id); @@ -40,8 +40,6 @@ int snd_defaults_pcm_device(void); int snd_defaults_rawmidi_card(void); int snd_defaults_rawmidi_device(void); -int snd_ctl_hw_open(snd_ctl_t **handle, char *name, int card); -int snd_ctl_shm_open(snd_ctl_t **handlep, char *name, char *socket, char *sname); snd_ctl_type_t snd_ctl_type(snd_ctl_t *handle); int snd_ctl_open(snd_ctl_t **handle, char *name); int snd_ctl_close(snd_ctl_t *handle); @@ -82,10 +80,10 @@ struct list_head { ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) -typedef struct snd_hcontrol_list_stru snd_hcontrol_list_t; -typedef struct snd_hcontrol_stru snd_hcontrol_t; +typedef struct _snd_hcontrol_list snd_hcontrol_list_t; +typedef struct _snd_hcontrol snd_hcontrol_t; -struct snd_hcontrol_list_stru { +struct _snd_hcontrol_list { unsigned int controls_offset; /* W: first control ID to get */ unsigned int controls_request; /* W: count of control IDs to get */ unsigned int controls_count; /* R: count of available (set) controls */ @@ -93,7 +91,7 @@ struct snd_hcontrol_list_stru { snd_control_id_t *pids; /* W: IDs */ }; -struct snd_hcontrol_stru { +struct _snd_hcontrol { snd_control_id_t id; /* must be always on top */ struct list_head list; /* links for list of all hcontrols */ int change: 1, /* structure change */ diff --git a/include/header.h b/include/header.h index 6875e256..8ce91f67 100644 --- a/include/header.h +++ b/include/header.h @@ -46,3 +46,5 @@ #define SND_TRANSPORT_TYPE_SHM 0 #define SND_TRANSPORT_TYPE_TCP 1 +extern void snd_lib_error(const char *file, int line, const char *function, int err, const char *fmt, ...) __attribute__ ((weak, format (printf, 5, 6))); + diff --git a/include/hwdep.h b/include/hwdep.h index 6446543b..ecd79581 100644 --- a/include/hwdep.h +++ b/include/hwdep.h @@ -14,7 +14,7 @@ extern "C" { #endif -typedef struct snd_hwdep snd_hwdep_t; +typedef struct _snd_hwdep snd_hwdep_t; int snd_hwdep_open(snd_hwdep_t **handle, int card, int device, int mode); int snd_hwdep_close(snd_hwdep_t *handle); diff --git a/include/instr.h b/include/instr.h index a9922d76..c1c29315 100644 --- a/include/instr.h +++ b/include/instr.h @@ -40,7 +40,7 @@ int snd_instr_simple_free(snd_instr_simple_t *simple); /* InterWave FFFF support */ typedef void snd_instr_iwffff_t; -typedef struct snd_iwffff_handle snd_iwffff_handle_t; +typedef struct _snd_iwffff_handle snd_iwffff_handle_t; #ifdef __cplusplus extern "C" { diff --git a/include/mixer.h b/include/mixer.h index f7eab69b..c53de78c 100644 --- a/include/mixer.h +++ b/include/mixer.h @@ -5,7 +5,7 @@ * * ****************************************************************************/ -typedef struct snd_mixer snd_mixer_t; +typedef struct _snd_mixer snd_mixer_t; #ifdef __cplusplus extern "C" { @@ -23,7 +23,7 @@ int snd_mixer_poll_descriptor(snd_mixer_t *handle); * Simple (legacy) mixer API */ -typedef enum { +typedef enum _snd_mixer_channel_id { SND_MIXER_CHN_FRONT_LEFT = 0, SND_MIXER_CHN_FRONT_RIGHT, SND_MIXER_CHN_FRONT_CENTER, @@ -51,12 +51,12 @@ typedef enum { #define SND_MIXER_SCTCAP_JOINTLY_CAPTURE (1<<5) #define SND_MIXER_SCTCAP_EXCL_CAPTURE (1<<6) -typedef struct snd_mixer_sid { +typedef struct _snd_mixer_sid { unsigned char name[60]; unsigned int index; } snd_mixer_sid_t; -typedef struct snd_mixer_simple_control_list { +typedef struct _snd_mixer_simple_control_list { unsigned int controls_offset; /* W: first control ID to get */ unsigned int controls_request; /* W: count of control IDs to get */ unsigned int controls_count; /* R: count of available (set) IDs */ @@ -65,7 +65,7 @@ typedef struct snd_mixer_simple_control_list { char reserved[50]; } snd_mixer_simple_control_list_t; -typedef struct snd_mixer_simple_control { +typedef struct _snd_mixer_simple_control { snd_mixer_sid_t sid; /* WR: simple control identification */ unsigned int caps; /* RO: capabilities */ unsigned int channels; /* RO: bitmap of active channels */ @@ -88,7 +88,7 @@ typedef struct snd_mixer_simple_control { } volume; /* RW */ } snd_mixer_simple_control_t; -typedef struct snd_mixer_simple_callbacks { +typedef struct _snd_mixer_simple_callbacks { void *private_data; /* may be used by an application */ void (*rebuild) (snd_mixer_t *handle, void *private_data); void (*value) (snd_mixer_t *handle, void *private_data, snd_mixer_sid_t *id); diff --git a/include/pcm.h b/include/pcm.h index 364ec5c4..c0ecee92 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -12,94 +12,9 @@ extern "C" { #endif -typedef unsigned int bitset_t; +typedef struct _snd_pcm snd_pcm_t; -static inline size_t bitset_size(size_t nbits) -{ - return (nbits + sizeof(bitset_t) * 8 - 1) / (sizeof(bitset_t) * 8); -} - -static inline bitset_t *bitset_alloc(size_t nbits) -{ - return (bitset_t*) calloc(bitset_size(nbits), sizeof(bitset_t)); -} - -static inline void bitset_set(bitset_t *bitmap, unsigned int pos) -{ - size_t bits = sizeof(*bitmap) * 8; - bitmap[pos / bits] |= 1U << (pos % bits); -} - -static inline void bitset_reset(bitset_t *bitmap, unsigned int pos) -{ - size_t bits = sizeof(*bitmap) * 8; - bitmap[pos / bits] &= ~(1U << (pos % bits)); -} - -static inline int bitset_get(bitset_t *bitmap, unsigned int pos) -{ - size_t bits = sizeof(*bitmap) * 8; - return !!(bitmap[pos / bits] & (1U << (pos % bits))); -} - -static inline void bitset_copy(bitset_t *dst, bitset_t *src, size_t nbits) -{ - memcpy(dst, src, bitset_size(nbits) * sizeof(bitset_t)); -} - -static inline void bitset_and(bitset_t *dst, bitset_t *bs, size_t nbits) -{ - bitset_t *end = dst + bitset_size(nbits); - while (dst < end) - *dst++ &= *bs++; -} - -static inline void bitset_or(bitset_t *dst, bitset_t *bs, size_t nbits) -{ - bitset_t *end = dst + bitset_size(nbits); - while (dst < end) - *dst++ |= *bs++; -} - -static inline void bitset_zero(bitset_t *dst, size_t nbits) -{ - bitset_t *end = dst + bitset_size(nbits); - while (dst < end) - *dst++ = 0; -} - -static inline void bitset_one(bitset_t *dst, size_t nbits) -{ - bitset_t *end = dst + bitset_size(nbits); - while (dst < end) - *dst++ = ~(bitset_t)0; -} - -static inline size_t hweight32(bitset_t v) -{ - v = (v & 0x55555555) + ((v >> 1) & 0x55555555); - v = (v & 0x33333333) + ((v >> 2) & 0x33333333); - v = (v & 0x0F0F0F0F) + ((v >> 4) & 0x0F0F0F0F); - v = (v & 0x00FF00FF) + ((v >> 8) & 0x00FF00FF); - return (v & 0x0000FFFF) + ((v >> 16) & 0x0000FFFF); -} - -/* Count bits set */ -static inline size_t bitset_count(bitset_t *bitset, size_t nbits) -{ - bitset_t *end = bitset + bitset_size(nbits) - 1; - size_t bits = sizeof(*bitset) * 8; - size_t count = 0; - while (bitset < end) - count += hweight32(*bitset++); - count += hweight32(*bitset & ((1U << (nbits % bits)) - 1)); - return count; -} - -typedef struct snd_pcm snd_pcm_t; -typedef struct snd_pcm_loopback snd_pcm_loopback_t; - -typedef enum { +typedef enum _snd_pcm_type { SND_PCM_TYPE_HW, SND_PCM_TYPE_MULTI, SND_PCM_TYPE_FILE, @@ -119,16 +34,28 @@ typedef enum { SND_PCM_TYPE_LBSERVER, } snd_pcm_type_t; -extern void snd_pcm_error(const char *file, int line, const char *function, int err, const char *fmt, ...) __attribute__ ((weak, format (printf, 5, 6))); +enum { + SND_PCM_RULE_PAR_MASK = 0x00ff, + SND_PCM_RULE_REL_LT = 0x100, + SND_PCM_RULE_REL_GT = 0x200, + SND_PCM_RULE_REL_EQ = 0x300, + SND_PCM_RULE_REL_LE = 0x400, + SND_PCM_RULE_REL_GE = 0x500, + SND_PCM_RULE_REL_NEAR = 0x600, + SND_PCM_RULE_REL_BITS = 0x700, + SND_PCM_RULE_REL_MASK = 0xff00 +}; + +typedef struct _snd_pcm_channel_area { + void *addr; /* base address of channel samples */ + unsigned int first; /* offset to first sample in bits */ + unsigned int step; /* samples distance in bits */ +} snd_pcm_channel_area_t; int snd_pcm_open(snd_pcm_t **pcm, char *name, int stream, int mode); /* Obsolete functions */ -int snd_pcm_hw_open_subdevice(snd_pcm_t **pcm, int card, int device, int subdevice, int stream, int mode); -int snd_pcm_hw_open_device(snd_pcm_t **pcm, int card, int device, int stream, int mode); -int snd_pcm_plug_open_subdevice(snd_pcm_t **pcm, int card, int device, int subdevice, int stream, int mode); -int snd_pcm_plug_open_device(snd_pcm_t **pcm, int card, int device, int stream, int mode); #define snd_pcm_write snd_pcm_writei #define snd_pcm_read snd_pcm_readi ssize_t snd_pcm_writev(snd_pcm_t *pcm, const struct iovec *vector, int count); @@ -141,12 +68,11 @@ int snd_pcm_poll_descriptor(snd_pcm_t *pcm); int snd_pcm_nonblock(snd_pcm_t *pcm, int nonblock); int snd_pcm_async(snd_pcm_t *pcm, int sig, pid_t pid); int snd_pcm_info(snd_pcm_t *pcm, snd_pcm_info_t *info); -int snd_pcm_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info); -int snd_pcm_params(snd_pcm_t *pcm, snd_pcm_params_t *params); -int snd_pcm_setup(snd_pcm_t *pcm, snd_pcm_setup_t *setup); -int snd_pcm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info); -int snd_pcm_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params); -int snd_pcm_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup); +int snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info); +int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); +int snd_pcm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params); +int snd_pcm_dig_info(snd_pcm_t *pcm, snd_pcm_dig_info_t *info); +int snd_pcm_dig_params(snd_pcm_t *pcm, snd_pcm_dig_params_t *params); int snd_pcm_status(snd_pcm_t *pcm, snd_pcm_status_t *status); int snd_pcm_prepare(snd_pcm_t *pcm); int snd_pcm_start(snd_pcm_t *pcm); @@ -160,23 +86,35 @@ ssize_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, size_t size); ssize_t snd_pcm_readi(snd_pcm_t *pcm, void *buffer, size_t size); ssize_t snd_pcm_writen(snd_pcm_t *pcm, void **bufs, size_t size); ssize_t snd_pcm_readn(snd_pcm_t *pcm, void **bufs, size_t size); + +int snd_pcm_dump_hw_setup(snd_pcm_t *pcm, FILE *fp); +int snd_pcm_dump_sw_setup(snd_pcm_t *pcm, FILE *fp); int snd_pcm_dump_setup(snd_pcm_t *pcm, FILE *fp); +int snd_pcm_dump_hw_params_fail(snd_pcm_hw_params_t *params, FILE *fp); +int snd_pcm_dump_sw_params_fail(snd_pcm_sw_params_t *params, FILE *fp); int snd_pcm_dump(snd_pcm_t *pcm, FILE *fp); int snd_pcm_dump_status(snd_pcm_status_t *status, FILE *fp); int snd_pcm_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2); int snd_pcm_unlink(snd_pcm_t *pcm); -int snd_pcm_channels_mask(snd_pcm_t *pcm, bitset_t *cmask); int snd_pcm_wait(snd_pcm_t *pcm, int timeout); ssize_t snd_pcm_avail_update(snd_pcm_t *pcm); int snd_pcm_set_avail_min(snd_pcm_t *pcm, size_t size); - +int snd_pcm_hw_params_rules(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + unsigned int count, int *rules); +int snd_pcm_hw_params_rulesv(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, ...); +int snd_pcm_hw_info_rules(snd_pcm_t *pcm, + snd_pcm_hw_info_t *info, + snd_pcm_hw_params_t *params, + unsigned int count, int *rules); +int snd_pcm_hw_info_rulesv(snd_pcm_t *pcm, + snd_pcm_hw_info_t *info, + snd_pcm_hw_params_t *params, ...); /* mmap */ -int snd_pcm_mmap(snd_pcm_t *pcm); -int snd_pcm_munmap(snd_pcm_t *pcm); snd_pcm_channel_area_t *snd_pcm_mmap_areas(snd_pcm_t *pcm); -int snd_pcm_mmap_get_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *stopped_areas, snd_pcm_channel_area_t *running_areas); +snd_pcm_channel_area_t *snd_pcm_mmap_running_areas(snd_pcm_t *pcm); +snd_pcm_channel_area_t *snd_pcm_mmap_stopped_areas(snd_pcm_t *pcm); ssize_t snd_pcm_mmap_forward(snd_pcm_t *pcm, size_t size); size_t snd_pcm_mmap_offset(snd_pcm_t *pcm); size_t snd_pcm_mmap_xfer(snd_pcm_t *pcm, size_t size); diff --git a/include/rawmidi.h b/include/rawmidi.h index 2d4a65b7..f75ef0c2 100644 --- a/include/rawmidi.h +++ b/include/rawmidi.h @@ -5,31 +5,37 @@ * * ****************************************************************************/ -#define SND_RAWMIDI_OPEN_OUTPUT (O_WRONLY) -#define SND_RAWMIDI_OPEN_OUTPUT_APPEND (O_WRONLY|O_APPEND|O_NONBLOCK) -#define SND_RAWMIDI_OPEN_INPUT (O_RDONLY) -#define SND_RAWMIDI_OPEN_DUPLEX (O_RDWR) -#define SND_RAWMIDI_OPEN_DUPLEX_APPEND (O_RDWR|O_APPEND|O_NONBLOCK) -#define SND_RAWMIDI_OPEN_NONBLOCK (O_NONBLOCK) +#define SND_RAWMIDI_OPEN_OUTPUT (1<<SND_RAWMIDI_STREAM_OUTPUT) +#define SND_RAWMIDI_OPEN_INPUT (1<<SND_RAWMIDI_STREAM_INPUT) +#define SND_RAWMIDI_OPEN_DUPLEX (SND_RAWMIDI_OPEN_OUTPUT|SND_RAWMIDI_OPEN_INPUT) + +#define SND_RAWMIDI_APPEND 1 +#define SND_RAWMIDI_NONBLOCK 2 #ifdef __cplusplus extern "C" { #endif -typedef struct snd_rawmidi snd_rawmidi_t; +typedef struct _snd_rawmidi snd_rawmidi_t; + +typedef enum _snd_rawmidi_type { + SND_RAWMIDI_TYPE_HW, + SND_RAWMIDI_TYPE_SHM, + SND_RAWMIDI_TYPE_INET, +} snd_rawmidi_type_t; -int snd_rawmidi_open_subdevice(snd_rawmidi_t **handle, int card, int device, int subdevice, int mode); -int snd_rawmidi_open(snd_rawmidi_t **handle, int card, int device, int mode); +int snd_rawmidi_open(snd_rawmidi_t **handle, char *name, int streams, int mode); int snd_rawmidi_close(snd_rawmidi_t *handle); int snd_rawmidi_poll_descriptor(snd_rawmidi_t *handle); -int snd_rawmidi_block_mode(snd_rawmidi_t *handle, int enable); +int snd_rawmidi_nonblock(snd_rawmidi_t *handle, int nonblock); int snd_rawmidi_info(snd_rawmidi_t *handle, snd_rawmidi_info_t * info); int snd_rawmidi_params(snd_rawmidi_t *handle, snd_rawmidi_params_t * params); int snd_rawmidi_status(snd_rawmidi_t *handle, snd_rawmidi_status_t * status); int snd_rawmidi_output_drop(snd_rawmidi_t *handle); int snd_rawmidi_output_drain(snd_rawmidi_t *handle); int snd_rawmidi_input_drain(snd_rawmidi_t *handle); -int snd_rawmidi_stream_drain(snd_rawmidi_t *handle, int channel); +int snd_rawmidi_drain(snd_rawmidi_t *handle, int channel); +int snd_rawmidi_drop(snd_rawmidi_t *handle, int channel); ssize_t snd_rawmidi_write(snd_rawmidi_t *handle, const void *buffer, size_t size); ssize_t snd_rawmidi_read(snd_rawmidi_t *handle, void *buffer, size_t size); diff --git a/include/seq.h b/include/seq.h index 2ba07c6c..b2a566f5 100644 --- a/include/seq.h +++ b/include/seq.h @@ -5,20 +5,28 @@ * * ****************************************************************************/ -#define SND_SEQ_OPEN_OUT (O_WRONLY) -#define SND_SEQ_OPEN_IN (O_RDONLY) -#define SND_SEQ_OPEN (O_RDWR) +#define SND_SEQ_OPEN_OUTPUT 1 +#define SND_SEQ_OPEN_INPUT 2 +#define SND_SEQ_OPEN_DUPLEX (SND_SEQ_OPEN_OUTPUT|SND_SEQ_OPEN_INPUT) + +#define SND_SEQ_NONBLOCK 1 #ifdef __cplusplus extern "C" { #endif -typedef struct snd_seq snd_seq_t; +typedef enum _snd_seq_type { + SND_SEQ_TYPE_HW, + SND_SEQ_TYPE_SHM, + SND_SEQ_TYPE_INET, +} snd_seq_type_t; + +typedef struct _snd_seq snd_seq_t; -int snd_seq_open(snd_seq_t **handle, int mode); +int snd_seq_open(snd_seq_t **handle, char *name, int streams, int mode); int snd_seq_close(snd_seq_t *handle); int snd_seq_poll_descriptor(snd_seq_t *handle); -int snd_seq_block_mode(snd_seq_t *handle, int enable); +int snd_seq_nonblock(snd_seq_t *handle, int nonblock); int snd_seq_client_id(snd_seq_t *handle); int snd_seq_output_buffer_size(snd_seq_t *handle); int snd_seq_input_buffer_size(snd_seq_t *handle); diff --git a/include/timer.h b/include/timer.h index a406d9c0..d01c57eb 100644 --- a/include/timer.h +++ b/include/timer.h @@ -9,7 +9,7 @@ extern "C" { #endif -typedef struct snd_timer snd_timer_t; +typedef struct _snd_timer snd_timer_t; int snd_timer_open(snd_timer_t **handle); int snd_timer_close(snd_timer_t *handle); @@ -18,7 +18,6 @@ int snd_timer_general_info(snd_timer_t *handle, snd_timer_general_info_t * info) int snd_timer_select(snd_timer_t *handle, snd_timer_select_t *tselect); int snd_timer_info(snd_timer_t *handle, snd_timer_info_t *timer); int snd_timer_params(snd_timer_t *handle, snd_timer_params_t *params); -int snd_timer_setup(snd_timer_t *handle, snd_timer_setup_t *setup); int snd_timer_status(snd_timer_t *handle, snd_timer_status_t *status); int snd_timer_start(snd_timer_t *handle); int snd_timer_stop(snd_timer_t *handle); diff --git a/src/control/cards.c b/src/control/cards.c index b139e0c8..064e6dbf 100644 --- a/src/control/cards.c +++ b/src/control/cards.c @@ -27,6 +27,7 @@ #include <ctype.h> #include <fcntl.h> #include <sys/ioctl.h> +#include "control_local.h" #include "asoundlib.h" #define SND_FILE_CONTROL "/dev/snd/controlC%i" diff --git a/src/control/control.c b/src/control/control.c index 40c90075..2aa84f2e 100644 --- a/src/control/control.c +++ b/src/control/control.c @@ -21,6 +21,7 @@ #include <stdio.h> #include <stdlib.h> +#include <stdarg.h> #include <unistd.h> #include <string.h> #include <errno.h> @@ -170,8 +171,16 @@ int snd_ctl_open(snd_ctl_t **ctlp, char *name) return err; err = snd_config_searchv(snd_config, &ctl_conf, "ctl", name, 0); if (err < 0) { - int cardno = snd_card_get_index(name); - return snd_ctl_hw_open(ctlp, name, cardno); + int card; + char socket[256], sname[256]; + err = sscanf(name, "hw:%d", &card); + if (err == 1) + return snd_ctl_hw_open(ctlp, NULL, card); + err = sscanf(name, "shm:%256s,%256s", socket, sname); + if (err == 2) + return snd_ctl_shm_open(ctlp, NULL, socket, sname); + ERR("Unknown control %s", name); + return -ENOENT; } if (snd_config_type(ctl_conf) != SND_CONFIG_TYPE_COMPOUND) return -EINVAL; @@ -182,8 +191,6 @@ int snd_ctl_open(snd_ctl_t **ctlp, char *name) if (err < 0) return err; err = snd_config_searchv(snd_config, &type_conf, "ctltype", str, 0); - if (err < 0) - return err; snd_config_foreach(i, type_conf) { snd_config_t *n = snd_config_entry(i); if (strcmp(n->id, "comment") == 0) @@ -215,3 +222,4 @@ int snd_ctl_open(snd_ctl_t **ctlp, char *name) return -ENXIO; return open_func(ctlp, name, ctl_conf); } + diff --git a/src/control/control_hw.c b/src/control/control_hw.c index eb805ae9..152e99c7 100644 --- a/src/control/control_hw.c +++ b/src/control/control_hw.c @@ -140,7 +140,7 @@ static int snd_ctl_hw_read(snd_ctl_t *handle, snd_ctl_event_t *event) return read(hw->fd, event, sizeof(*event)); } -struct snd_ctl_ops snd_ctl_hw_ops = { +snd_ctl_ops_t snd_ctl_hw_ops = { close: snd_ctl_hw_close, poll_descriptor: snd_ctl_hw_poll_descriptor, hw_info: snd_ctl_hw_hw_info, diff --git a/src/control/control_local.h b/src/control/control_local.h index 8f912ccb..3cfb7f55 100644 --- a/src/control/control_local.h +++ b/src/control/control_local.h @@ -23,7 +23,15 @@ #include "asoundlib.h" #include "list.h" -struct snd_ctl_ops { +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) +#define ERR(...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, 0, __VA_ARGS__) +#define SYSERR(...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, errno, __VA_ARGS__) +#else +#define ERR(args...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, 0, ##args) +#define SYSERR(args...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, errno, ##args) +#endif + +typedef struct { int (*close)(snd_ctl_t *handle); int (*poll_descriptor)(snd_ctl_t *handle); int (*hw_info)(snd_ctl_t *handle, snd_ctl_hw_info_t *info); @@ -37,13 +45,13 @@ struct snd_ctl_ops { int (*rawmidi_info)(snd_ctl_t *handle, snd_rawmidi_info_t * info); int (*rawmidi_prefer_subdevice)(snd_ctl_t *handle, int subdev); int (*read)(snd_ctl_t *handle, snd_ctl_event_t *event); -}; +} snd_ctl_ops_t; -struct snd_ctl { +struct _snd_ctl { char *name; snd_ctl_type_t type; - struct snd_ctl_ops *ops; + snd_ctl_ops_t *ops; void *private; int hcount; int herr; @@ -56,3 +64,6 @@ struct snd_ctl { snd_ctl_hcallback_add_t *callback_add; void *callback_add_private_data; }; + +int snd_ctl_hw_open(snd_ctl_t **handle, char *name, int card); +int snd_ctl_shm_open(snd_ctl_t **handlep, char *name, char *socket, char *sname); diff --git a/src/control/control_shm.c b/src/control/control_shm.c index 02210c74..e8086f19 100644 --- a/src/control/control_shm.c +++ b/src/control/control_shm.c @@ -266,7 +266,7 @@ static int snd_ctl_shm_read(snd_ctl_t *ctl, snd_ctl_event_t *event) return err; } -struct snd_ctl_ops snd_ctl_shm_ops = { +snd_ctl_ops_t snd_ctl_shm_ops = { close: snd_ctl_shm_close, poll_descriptor: snd_ctl_shm_poll_descriptor, hw_info: snd_ctl_shm_hw_info, diff --git a/src/error.c b/src/error.c index 47fb5ec8..b5871f9a 100644 --- a/src/error.c +++ b/src/error.c @@ -23,6 +23,7 @@ #include <stdio.h> #include <stdlib.h> +#include <stdarg.h> #include <string.h> #include "asoundlib.h" @@ -42,3 +43,16 @@ const char *snd_strerror(int errnum) return "Unknown error"; return snd_error_codes[errnum]; } + +void snd_lib_error(const char *file, int line, const char *function, int err, const char *fmt, ...) +{ + va_list arg; + va_start(arg, fmt); + fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function); + vfprintf(stderr, fmt, arg); + if (err) + fprintf(stderr, ": %s", snd_strerror(err)); + putc('\n', stderr); + va_end(arg); +} + diff --git a/src/hwdep/hwdep.c b/src/hwdep/hwdep.c index e6440536..d8e428f9 100644 --- a/src/hwdep/hwdep.c +++ b/src/hwdep/hwdep.c @@ -31,7 +31,7 @@ #define SND_FILE_HWDEP "/dev/snd/hwC%iD%i" #define SND_HWDEP_VERSION_MAX SND_PROTOCOL_VERSION(1, 0, 0) -struct snd_hwdep { +struct _snd_hwdep { int card; int device; int fd; diff --git a/src/instr/iwffff.c b/src/instr/iwffff.c index 90b3f585..383b40ac 100644 --- a/src/instr/iwffff.c +++ b/src/instr/iwffff.c @@ -171,7 +171,7 @@ struct envelope_record { #define copyright_header IW_ID_VALUE('C', 'P', 'R', 'T') #endif -struct snd_iwffff_handle { +struct _snd_iwffff_handle { int rom; unsigned char *fff_data; size_t fff_size; diff --git a/src/mixer/mixer_local.h b/src/mixer/mixer_local.h index 956be64e..a5eb3adb 100644 --- a/src/mixer/mixer_local.h +++ b/src/mixer/mixer_local.h @@ -23,8 +23,8 @@ #include "asoundlib.h" #include "list.h" -typedef struct mixer_simple mixer_simple_t; -typedef struct mixer_simple_hcontrol_private mixer_simple_hcontrol_private_t; +typedef struct _mixer_simple mixer_simple_t; +typedef struct _mixer_simple_hcontrol_private mixer_simple_hcontrol_private_t; typedef int (mixer_simple_get_t) (snd_mixer_t *handle, mixer_simple_t *simple, snd_mixer_simple_control_t *control); typedef int (mixer_simple_put_t) (snd_mixer_t *handle, mixer_simple_t *simple, snd_mixer_simple_control_t *control); @@ -43,7 +43,7 @@ typedef int (mixer_simple_event_add_t) (snd_mixer_t *handle, snd_hcontrol_t *hco #define MIXER_PRESENT_CAPTURE_ROUTE (1<<10) #define MIXER_PRESENT_CAPTURE_SOURCE (1<<11) -struct mixer_simple { +struct _mixer_simple { /* this may be moved to a private area */ unsigned int present; /* present controls */ unsigned int global_values; @@ -74,11 +74,11 @@ struct mixer_simple { unsigned long private_value; }; -struct mixer_simple_hcontrol_private { +struct _mixer_simple_hcontrol_private { void *simples; /* list of associated hcontrols */ }; -struct snd_mixer { +struct _snd_mixer { snd_ctl_t *ctl_handle; int simple_valid; int simple_changes; /* total number of changes */ diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 71fd8827..b8285fa8 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -28,6 +28,7 @@ #include <sys/poll.h> #include <sys/shm.h> #include <sys/mman.h> +#include <limits.h> #include <dlfcn.h> #include "pcm_local.h" #include "list.h" @@ -38,7 +39,7 @@ snd_pcm_type_t snd_pcm_type(snd_pcm_t *pcm) return pcm->type; } -snd_pcm_type_t snd_pcm(snd_pcm_t *pcm) +snd_pcm_type_t snd_pcm_stream(snd_pcm_t *pcm) { assert(pcm); return pcm->stream; @@ -49,23 +50,23 @@ int snd_pcm_close(snd_pcm_t *pcm) int ret = 0; int err; assert(pcm); - if (pcm->valid_setup) { + if (pcm->setup) { if (pcm->mode & SND_PCM_NONBLOCK) snd_pcm_drop(pcm); else snd_pcm_drain(pcm); } - if (pcm->mmap_info) { + if (pcm->mmap_channels) { if ((err = snd_pcm_munmap(pcm)) < 0) ret = err; } if ((err = pcm->ops->close(pcm->op_arg)) < 0) ret = err; - pcm->valid_setup = 0; + pcm->setup = 0; if (pcm->name) free(pcm->name); free(pcm); - return ret; + return 0; } int snd_pcm_nonblock(snd_pcm_t *pcm, int nonblock) @@ -93,84 +94,176 @@ int snd_pcm_info(snd_pcm_t *pcm, snd_pcm_info_t *info) return pcm->ops->info(pcm->op_arg, info); } -int snd_pcm_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info) -{ - assert(pcm && info); - return pcm->ops->params_info(pcm->op_arg, info); -} - -int snd_pcm_setup(snd_pcm_t *pcm, snd_pcm_setup_t *setup) +int snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info) { int err; - assert(pcm && setup); - if (pcm->valid_setup) { - *setup = pcm->setup; - return 0; - } - if ((err = pcm->ops->setup(pcm->op_arg, &pcm->setup)) < 0) - return err; - *setup = pcm->setup; - pcm->bits_per_sample = snd_pcm_format_physical_width(setup->format.sfmt); - pcm->bits_per_frame = pcm->bits_per_sample * setup->format.channels; - pcm->valid_setup = 1; - return 0; + assert(pcm && info); +#if 0 + fprintf(stderr, "hw_info entered:\n"); + snd_pcm_dump_hw_info(info, stderr); + fprintf(stderr, "\n"); +#endif + err = pcm->ops->hw_info(pcm->op_arg, info); +#if 0 + fprintf(stderr, "hw_info return %d:\n", err); + snd_pcm_dump_hw_info(info, stderr); + fprintf(stderr, "\n"); +#endif + return err; } -int snd_pcm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info) +void snd_pcm_hw_params_to_info(snd_pcm_hw_params_t *params, snd_pcm_hw_info_t *info) { - assert(pcm && info); - assert(pcm->valid_setup); - assert(info->channel < pcm->setup.format.channels); - return pcm->ops->channel_info(pcm->op_arg, info); + info->access_mask = 1U << params->access; + info->format_mask = 1U << params->format; + info->subformat_mask = 1U << params->subformat; + info->channels_min = info->channels_max = params->channels; + info->rate_min = info->rate_max = params->rate; + info->fragment_size_min = info->fragment_size_max = params->fragment_size; + info->fragments_min = info->fragments_max = params->fragments; } -int snd_pcm_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params) +void snd_pcm_hw_info_to_params(snd_pcm_hw_info_t *info, snd_pcm_hw_params_t *params) { - assert(pcm && params); - assert(pcm->valid_setup); - assert(params->channel < pcm->setup.format.channels); - return pcm->ops->channel_params(pcm->op_arg, params); + assert(info->access_mask && + !(info->access_mask & (info->access_mask - 1))); + params->access = ffs(info->access_mask) - 1; + assert(info->format_mask && + !(info->format_mask & (info->format_mask - 1))); + params->format = ffs(info->format_mask) - 1; + assert(info->subformat_mask && + !(info->subformat_mask & (info->subformat_mask - 1))); + params->subformat = ffs(info->subformat_mask) - 1; + assert(info->channels_min == info->channels_max); + params->channels = info->channels_min; + assert(info->rate_min == info->rate_max); + params->rate = info->rate_min; + assert(info->fragment_size_min == info->fragment_size_max); + params->fragment_size = info->fragment_size_min; + assert(info->fragments_min == info->fragments_max); + params->fragments = info->fragments_min; } -int snd_pcm_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup) +void snd_pcm_hw_info_to_params_fail(snd_pcm_hw_info_t *info, snd_pcm_hw_params_t *params) { - assert(pcm && setup); - assert(pcm->valid_setup); - assert(setup->channel < pcm->setup.format.channels); - return pcm->ops->channel_setup(pcm->op_arg, setup); + unsigned int f = 0; + if (info->access_mask == 0) + f |= SND_PCM_HW_PARBIT_ACCESS; + if (info->format_mask == 0) + f |= SND_PCM_HW_PARBIT_FORMAT; + if (info->subformat_mask == 0) + f |= SND_PCM_HW_PARBIT_SUBFORMAT; + if (info->channels_min > info->channels_max) + f |= SND_PCM_HW_PARBIT_CHANNELS; + if (info->rate_min > info->rate_max) + f |= SND_PCM_HW_PARBIT_RATE; + if (info->fragment_size_min > info->fragment_size_max) + f |= SND_PCM_HW_PARBIT_FRAGMENT_SIZE; + if (info->fragments_min > info->fragments_max) + f |= SND_PCM_HW_PARBIT_FRAGMENTS; + assert(f); + params->fail_mask = f; } -int _snd_pcm_params(snd_pcm_t *pcm, snd_pcm_params_t *params) +int _snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { int err; - snd_pcm_setup_t setup; - if ((err = pcm->ops->params(pcm->op_arg, params)) < 0) + snd_pcm_hw_info_t info; + + snd_pcm_hw_params_to_info(params, &info); + err = snd_pcm_hw_info(pcm, &info); + if (err < 0) { + snd_pcm_hw_info_to_params_fail(&info, params); return err; - pcm->valid_setup = 0; - return snd_pcm_setup(pcm, &setup); + } + snd_pcm_hw_info_to_params(&info, params); + if ((err = pcm->ops->hw_params(pcm->op_arg, params)) < 0) + return err; + pcm->setup = 1; + pcm->access = params->access; + pcm->format = params->format; + pcm->subformat = params->subformat; + pcm->rate = params->rate; + pcm->channels = params->channels; + pcm->fragment_size = params->fragment_size; + pcm->fragments = params->fragments; + pcm->bits_per_sample = snd_pcm_format_physical_width(params->format); + pcm->bits_per_frame = pcm->bits_per_sample * params->channels; + pcm->buffer_size = params->fragment_size * params->fragments; + + pcm->info = info.info; + pcm->msbits = info.msbits; + pcm->rate_master = info.rate_master; + pcm->rate_divisor = info.rate_divisor; + pcm->fifo_size = info.fifo_size; + + /* Default sw params */ + pcm->start_mode = SND_PCM_START_DATA; + pcm->ready_mode = SND_PCM_READY_FRAGMENT; + pcm->xrun_mode = SND_PCM_XRUN_FRAGMENT; + pcm->avail_min = pcm->fragment_size; + pcm->xfer_min = pcm->fragment_size; + pcm->xfer_align = pcm->fragment_size; + pcm->time = 0; + pcm->boundary = LONG_MAX - pcm->buffer_size * 2 - LONG_MAX % pcm->buffer_size; + return 0; } -int snd_pcm_params_mmap(snd_pcm_t *pcm, snd_pcm_params_t *params) +int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { int err; - if (pcm->mmap_info) { + assert(pcm && params); + if (pcm->setup && pcm->mmap_channels && + (pcm->mmap_rw || + (pcm->access == SND_PCM_ACCESS_MMAP_INTERLEAVED || + pcm->access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED || + pcm->access == SND_PCM_ACCESS_MMAP_COMPLEX))) { err = snd_pcm_munmap(pcm); if (err < 0) return err; } - err = _snd_pcm_params(pcm, params); - if (pcm->valid_setup) - snd_pcm_mmap(pcm); + err = _snd_pcm_hw_params(pcm, params); + if (pcm->setup && + (pcm->mmap_rw || + (pcm->access == SND_PCM_ACCESS_MMAP_INTERLEAVED || + pcm->access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED || + pcm->access == SND_PCM_ACCESS_MMAP_COMPLEX))) { + int err; + err = snd_pcm_mmap(pcm); + if (err < 0) + return err; + } return err; } -int snd_pcm_params(snd_pcm_t *pcm, snd_pcm_params_t *params) +int snd_pcm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params) { + int err; assert(pcm && params); - if (pcm->mmap_auto) - return snd_pcm_params_mmap(pcm, params); - assert(!pcm->mmap_info); - return _snd_pcm_params(pcm, params); + assert(pcm->setup); + if ((err = pcm->ops->sw_params(pcm->op_arg, params)) < 0) + return err; + pcm->start_mode = params->start_mode; + pcm->ready_mode = params->ready_mode; + pcm->xrun_mode = params->xrun_mode; + pcm->avail_min = params->avail_min; + pcm->xfer_min = params->xfer_min; + pcm->xfer_align = params->xfer_align; + pcm->time = params->time; + pcm->boundary = params->boundary; + return 0; +} + +int snd_pcm_dig_params(snd_pcm_t *pcm, snd_pcm_dig_params_t *params) +{ + assert(pcm && params); + return pcm->ops->dig_params(pcm->op_arg, params); +} + +int snd_pcm_dig_info(snd_pcm_t *pcm, snd_pcm_dig_info_t *info) +{ + assert(pcm && info); + return pcm->ops->dig_info(pcm->op_arg, info); } int snd_pcm_status(snd_pcm_t *pcm, snd_pcm_status_t *status) @@ -188,44 +281,42 @@ int snd_pcm_state(snd_pcm_t *pcm) int snd_pcm_delay(snd_pcm_t *pcm, ssize_t *delayp) { assert(pcm); - assert(pcm->valid_setup); + assert(pcm->setup); return pcm->fast_ops->delay(pcm->fast_op_arg, delayp); } int snd_pcm_prepare(snd_pcm_t *pcm) { assert(pcm); - assert(pcm->valid_setup); + assert(pcm->setup); return pcm->fast_ops->prepare(pcm->fast_op_arg); } int snd_pcm_start(snd_pcm_t *pcm) { assert(pcm); - assert(pcm->valid_setup); - assert(pcm->stream != SND_PCM_STREAM_PLAYBACK || - snd_pcm_mmap_playback_hw_avail(pcm) > 0); + assert(pcm->setup); return pcm->fast_ops->start(pcm->fast_op_arg); } int snd_pcm_drop(snd_pcm_t *pcm) { assert(pcm); - assert(pcm->valid_setup); + assert(pcm->setup); return pcm->fast_ops->drop(pcm->fast_op_arg); } int snd_pcm_drain(snd_pcm_t *pcm) { assert(pcm); - assert(pcm->valid_setup); + assert(pcm->setup); return pcm->fast_ops->drain(pcm->fast_op_arg); } int snd_pcm_pause(snd_pcm_t *pcm, int enable) { assert(pcm); - assert(pcm->valid_setup); + assert(pcm->setup); return pcm->fast_ops->pause(pcm->fast_op_arg, enable); } @@ -233,7 +324,7 @@ int snd_pcm_pause(snd_pcm_t *pcm, int enable) ssize_t snd_pcm_rewind(snd_pcm_t *pcm, size_t frames) { assert(pcm); - assert(pcm->valid_setup); + assert(pcm->setup); assert(frames > 0); return pcm->fast_ops->rewind(pcm->fast_op_arg, frames); } @@ -242,12 +333,12 @@ int snd_pcm_set_avail_min(snd_pcm_t *pcm, size_t frames) { int err; assert(pcm); - assert(pcm->valid_setup); + assert(pcm->setup); assert(frames > 0); err = pcm->fast_ops->set_avail_min(pcm->fast_op_arg, frames); if (err < 0) return err; - pcm->setup.avail_min = frames; + pcm->avail_min = frames; return 0; } @@ -255,9 +346,8 @@ ssize_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, size_t size) { assert(pcm); assert(size == 0 || buffer); - assert(pcm->valid_setup); - assert(pcm->setup.xfer_mode == SND_PCM_XFER_INTERLEAVED); - assert(!pcm->mmap_info || pcm->mmap_auto); + assert(pcm->setup); + assert(pcm->access == SND_PCM_ACCESS_RW_INTERLEAVED); return _snd_pcm_writei(pcm, buffer, size); } @@ -265,9 +355,8 @@ ssize_t snd_pcm_writen(snd_pcm_t *pcm, void **bufs, size_t size) { assert(pcm); assert(size == 0 || bufs); - assert(pcm->valid_setup); - assert(pcm->setup.xfer_mode == SND_PCM_XFER_NONINTERLEAVED); - assert(!pcm->mmap_info || pcm->mmap_auto); + assert(pcm->setup); + assert(pcm->access == SND_PCM_ACCESS_RW_NONINTERLEAVED); return _snd_pcm_writen(pcm, bufs, size); } @@ -275,9 +364,8 @@ ssize_t snd_pcm_readi(snd_pcm_t *pcm, void *buffer, size_t size) { assert(pcm); assert(size == 0 || buffer); - assert(pcm->valid_setup); - assert(pcm->setup.xfer_mode == SND_PCM_XFER_INTERLEAVED); - assert(!pcm->mmap_info || pcm->mmap_auto); + assert(pcm->setup); + assert(pcm->access == SND_PCM_ACCESS_RW_INTERLEAVED); return _snd_pcm_readi(pcm, buffer, size); } @@ -285,9 +373,8 @@ ssize_t snd_pcm_readn(snd_pcm_t *pcm, void **bufs, size_t size) { assert(pcm); assert(size == 0 || bufs); - assert(pcm->valid_setup); - assert(pcm->setup.xfer_mode == SND_PCM_XFER_NONINTERLEAVED); - assert(!pcm->mmap_info || pcm->mmap_auto); + assert(pcm->setup); + assert(pcm->access == SND_PCM_ACCESS_RW_NONINTERLEAVED); return _snd_pcm_readn(pcm, bufs, size); } @@ -296,8 +383,8 @@ ssize_t snd_pcm_writev(snd_pcm_t *pcm, const struct iovec *vector, int count) void **bufs; int k; assert(pcm); - assert(pcm->valid_setup); - assert((int)pcm->setup.format.channels == count); + assert(pcm->setup); + assert((int)pcm->channels == count); bufs = alloca(sizeof(*bufs) * count); for (k = 0; k < count; ++k) { bufs[k] = vector[k].iov_base; @@ -311,8 +398,8 @@ ssize_t snd_pcm_readv(snd_pcm_t *pcm, const struct iovec *vector, int count) void **bufs; int k; assert(pcm); - assert(pcm->valid_setup); - assert((int)pcm->setup.format.channels == count); + assert(pcm->setup); + assert((int)pcm->channels == count); bufs = alloca(sizeof(*bufs) * count); for (k = 0; k < count; ++k) { bufs[k] = vector[k].iov_base; @@ -400,80 +487,329 @@ static const char *assoc(int value, assoc_t *alist) #define STREAM(v) { SND_PCM_STREAM_##v, #v, #v } #define READY(v) { SND_PCM_READY_##v, #v, #v } #define XRUN(v) { SND_PCM_XRUN_##v, #v, #v } -#define XFER(v) { SND_PCM_XFER_##v, #v, #v } -#define MMAP(v) { SND_PCM_MMAP_##v, #v, #v } -#define SFMT(v, d) { SND_PCM_SFMT_##v, #v, d } +#define ACCESS(v) { SND_PCM_ACCESS_##v, #v, #v } +#define FORMAT(v, d) { SND_PCM_FORMAT_##v, #v, d } +#define SUBFORMAT(v, d) { SND_PCM_SUBFORMAT_##v, #v, d } #define XRUN_ACT(v) { SND_PCM_XRUN_ACT_##v, #v, #v } #define START(v) { SND_PCM_START_##v, #v, #v } #define FILL(v) { SND_PCM_FILL_##v, #v, #v } +#define HW_PARAM(v) { SND_PCM_HW_PARAM_##v, #v, #v } +#define SW_PARAM(v) { SND_PCM_SW_PARAM_##v, #v, #v } #define END { 0, NULL, NULL } -static assoc_t states[] = { STATE(OPEN), STATE(SETUP), STATE(PREPARED), - STATE(RUNNING), STATE(XRUN), STATE(PAUSED), END }; -static assoc_t streams[] = { STREAM(PLAYBACK), STREAM(CAPTURE), END }; -static assoc_t xruns[] = { XRUN(ASAP), XRUN(FRAGMENT), XRUN(NONE), END }; -static assoc_t fmts[] = { - SFMT(S8, "Signed 8-bit"), - SFMT(U8, "Unsigned 8-bit"), - SFMT(S16_LE, "Signed 16-bit Little Endian"), - SFMT(S16_BE, "Signed 16-bit Big Endian"), - SFMT(U16_LE, "Unsigned 16-bit Little Endian"), - SFMT(U16_BE, "Unsigned 16-bit Big Endian"), - SFMT(S24_LE, "Signed 24-bit Little Endian"), - SFMT(S24_BE, "Signed 24-bit Big Endian"), - SFMT(U24_LE, "Unsigned 24-bit Little Endian"), - SFMT(U24_BE, "Unsigned 24-bit Big Endian"), - SFMT(S32_LE, "Signed 32-bit Little Endian"), - SFMT(S32_BE, "Signed 32-bit Big Endian"), - SFMT(U32_LE, "Unsigned 32-bit Little Endian"), - SFMT(U32_BE, "Unsigned 32-bit Big Endian"), - SFMT(FLOAT_LE, "Float Little Endian"), - SFMT(FLOAT_BE, "Float Big Endian"), - SFMT(FLOAT64_LE, "Float64 Little Endian"), - SFMT(FLOAT64_BE, "Float64 Big Endian"), - SFMT(IEC958_SUBFRAME_LE, "IEC-958 Little Endian"), - SFMT(IEC958_SUBFRAME_BE, "IEC-958 Big Endian"), - SFMT(MU_LAW, "Mu-Law"), - SFMT(A_LAW, "A-Law"), - SFMT(IMA_ADPCM, "Ima-ADPCM"), - SFMT(MPEG, "MPEG"), - SFMT(GSM, "GSM"), - SFMT(SPECIAL, "Special"), +static assoc_t streams[] = { + STREAM(PLAYBACK), + STREAM(CAPTURE), + END +}; + +static assoc_t states[] = { + STATE(OPEN), + STATE(SETUP), + STATE(PREPARED), + STATE(RUNNING), + STATE(XRUN), + STATE(PAUSED), + END +}; + +#if 0 +static assoc_t hw_params[] = { + HW_PARAM(ACCESS), + HW_PARAM(FORMAT), + HW_PARAM(SUBFORMAT), + HW_PARAM(CHANNELS), + HW_PARAM(RATE), + HW_PARAM(FRAGMENT_SIZE), + HW_PARAM(FRAGMENTS), + END +}; + +static assoc_t sw_params[] = { + SW_PARAM(START_MODE), + SW_PARAM(READY_MODE), + SW_PARAM(AVAIL_MIN), + SW_PARAM(XFER_MIN), + SW_PARAM(XFER_ALIGN), + SW_PARAM(XRUN_MODE), + SW_PARAM(TIME), + END +}; +#endif + +static assoc_t accesses[] = { + ACCESS(MMAP_INTERLEAVED), + ACCESS(MMAP_NONINTERLEAVED), + ACCESS(MMAP_COMPLEX), + ACCESS(RW_INTERLEAVED), + ACCESS(RW_NONINTERLEAVED), + END +}; + +static assoc_t formats[] = { + FORMAT(S8, "Signed 8-bit"), + FORMAT(U8, "Unsigned 8-bit"), + FORMAT(S16_LE, "Signed 16-bit Little Endian"), + FORMAT(S16_BE, "Signed 16-bit Big Endian"), + FORMAT(U16_LE, "Unsigned 16-bit Little Endian"), + FORMAT(U16_BE, "Unsigned 16-bit Big Endian"), + FORMAT(S24_LE, "Signed 24-bit Little Endian"), + FORMAT(S24_BE, "Signed 24-bit Big Endian"), + FORMAT(U24_LE, "Unsigned 24-bit Little Endian"), + FORMAT(U24_BE, "Unsigned 24-bit Big Endian"), + FORMAT(S32_LE, "Signed 32-bit Little Endian"), + FORMAT(S32_BE, "Signed 32-bit Big Endian"), + FORMAT(U32_LE, "Unsigned 32-bit Little Endian"), + FORMAT(U32_BE, "Unsigned 32-bit Big Endian"), + FORMAT(FLOAT_LE, "Float Little Endian"), + FORMAT(FLOAT_BE, "Float Big Endian"), + FORMAT(FLOAT64_LE, "Float64 Little Endian"), + FORMAT(FLOAT64_BE, "Float64 Big Endian"), + FORMAT(IEC958_SUBFRAME_LE, "IEC-958 Little Endian"), + FORMAT(IEC958_SUBFRAME_BE, "IEC-958 Big Endian"), + FORMAT(MU_LAW, "Mu-Law"), + FORMAT(A_LAW, "A-Law"), + FORMAT(IMA_ADPCM, "Ima-ADPCM"), + FORMAT(MPEG, "MPEG"), + FORMAT(GSM, "GSM"), + FORMAT(SPECIAL, "Special"), END }; -static assoc_t starts[] = { START(EXPLICIT), START(DATA), END }; -static assoc_t readys[] = { READY(FRAGMENT), READY(ASAP), END }; -static assoc_t xfers[] = { XFER(INTERLEAVED), XFER(NONINTERLEAVED), END }; -static assoc_t mmaps[] = { MMAP(INTERLEAVED), MMAP(NONINTERLEAVED), END }; -static assoc_t onoff[] = { {0, "OFF", NULL}, {1, "ON", NULL}, {-1, "ON", NULL}, END }; +static assoc_t subformats[] = { + SUBFORMAT(STD, "Standard"), + END +}; -int snd_pcm_dump_setup(snd_pcm_t *pcm, FILE *fp) +static assoc_t starts[] = { + START(EXPLICIT), + START(DATA), + END +}; +static assoc_t readys[] = { + READY(FRAGMENT), + READY(ASAP), + END +}; + +static assoc_t xruns[] = { + XRUN(ASAP), + XRUN(FRAGMENT), + XRUN(NONE), + END +}; + +static assoc_t onoff[] = { + {0, "OFF", NULL}, + {1, "ON", NULL}, + {-1, "ON", NULL}, + END +}; + +int snd_pcm_dump_hw_setup(snd_pcm_t *pcm, FILE *fp) +{ + assert(pcm); + assert(fp); + assert(pcm->setup); + fprintf(fp, "stream : %s\n", assoc(pcm->stream, streams)); + fprintf(fp, "access : %s\n", assoc(pcm->access, accesses)); + fprintf(fp, "format : %s\n", assoc(pcm->format, formats)); + fprintf(fp, "subformat : %s\n", assoc(pcm->subformat, subformats)); + fprintf(fp, "channels : %d\n", pcm->channels); + fprintf(fp, "rate : %d\n", pcm->rate); + fprintf(fp, "rate : %g (%d/%d)\n", (double) pcm->rate_master / pcm->rate_divisor, pcm->rate_master, pcm->rate_divisor); + fprintf(fp, "msbits : %d\n", pcm->msbits); + fprintf(fp, "fragment_size: %ld\n", (long)pcm->fragment_size); + fprintf(fp, "fragments : %d\n", pcm->fragments); + return 0; +} + +int snd_pcm_dump_sw_setup(snd_pcm_t *pcm, FILE *fp) { - snd_pcm_setup_t *setup; assert(pcm); assert(fp); - assert(pcm->valid_setup); - setup = &pcm->setup; - fprintf(fp, "stream : %s\n", assoc(pcm->stream, streams)); - fprintf(fp, "format : %s\n", assoc(setup->format.sfmt, fmts)); - fprintf(fp, "channels : %d\n", setup->format.channels); - fprintf(fp, "rate : %d (%d/%d=%g)\n", setup->format.rate, setup->rate_master, setup->rate_divisor, (double) setup->rate_master / setup->rate_divisor); - // digital - fprintf(fp, "start_mode : %s\n", assoc(setup->start_mode, starts)); - fprintf(fp, "ready_mode : %s\n", assoc(setup->ready_mode, readys)); - fprintf(fp, "avail_min : %ld\n", (long)setup->avail_min); - fprintf(fp, "xfer_mode : %s\n", assoc(setup->xfer_mode, xfers)); - fprintf(fp, "xfer_min : %ld\n", (long)setup->xfer_min); - fprintf(fp, "xfer_align : %ld\n", (long)setup->xfer_align); - fprintf(fp, "xrun_mode : %s\n", assoc(setup->xrun_mode, xruns)); - fprintf(fp, "mmap_shape : %s\n", assoc(setup->mmap_shape, mmaps)); - fprintf(fp, "buffer_size: %ld\n", (long)setup->buffer_size); - fprintf(fp, "frag_size : %ld\n", (long)setup->frag_size); - fprintf(fp, "boundary : %ld\n", (long)setup->boundary); - fprintf(fp, "time : %s\n", assoc(setup->time, onoff)); - fprintf(fp, "frags : %ld\n", (long)setup->frags); - fprintf(fp, "msbits : %d\n", setup->msbits); + assert(pcm->setup); + fprintf(fp, "start_mode : %s\n", assoc(pcm->start_mode, starts)); + fprintf(fp, "ready_mode : %s\n", assoc(pcm->ready_mode, readys)); + fprintf(fp, "xrun_mode : %s\n", assoc(pcm->xrun_mode, xruns)); + fprintf(fp, "avail_min : %ld\n", (long)pcm->avail_min); + fprintf(fp, "xfer_min : %ld\n", (long)pcm->xfer_min); + fprintf(fp, "xfer_align : %ld\n", (long)pcm->xfer_align); + fprintf(fp, "time : %s\n", assoc(pcm->time, onoff)); + fprintf(fp, "boundary : %ld\n", (long)pcm->boundary); + return 0; +} + +int snd_pcm_dump_setup(snd_pcm_t *pcm, FILE *fp) +{ + snd_pcm_dump_hw_setup(pcm, fp); + snd_pcm_dump_sw_setup(pcm, fp); + return 0; +} + +int snd_pcm_dump_hw_info(snd_pcm_hw_info_t *info, + FILE *fp) +{ + unsigned int k; + fputs("access:", fp); + if (info->access_mask) { + for (k = 0; k <= SND_PCM_ACCESS_LAST; ++k) + if (info->access_mask & (1U << k)) { + putc(' ', fp); + fputs(assoc(k, accesses), fp); + } + } else + fputs(" NONE", fp); + putc('\n', fp); + + fputs("format:", fp); + if (info->format_mask) { + for (k = 0; k <= SND_PCM_FORMAT_LAST; ++k) + if (info->format_mask & (1U << k)) { + putc(' ', fp); + fputs(assoc(k, formats), fp); + } + } else + fputs(" NONE", fp); + putc('\n', fp); + + fputs("subformat:", fp); + if (info->subformat_mask) { + for (k = 0; k <= SND_PCM_SUBFORMAT_LAST; ++k) + if (info->subformat_mask & (1U << k)) { + putc(' ', fp); + fputs(assoc(k, subformats), fp); + } + } else + fputs(" NONE", fp); + putc('\n', fp); + + fputs("channels: ", fp); + if (info->channels_min == 0 && info->channels_max == UINT_MAX) + fputs("ALL", fp); + else if (info->channels_min > info->channels_max) + fputs("NONE", fp); + else { + fprintf(fp, "%u", info->channels_min); + if (info->channels_min < info->channels_max) + fprintf(fp, " - %u", info->channels_max); + } + putc('\n', fp); + + fputs("rate: ", fp); + if (info->rate_min == 0 && info->rate_max == UINT_MAX) + fputs("ALL", fp); + else if (info->rate_min > info->rate_max) + fputs("NONE", fp); + else { + fprintf(fp, "%u", info->rate_min); + if (info->rate_min < info->rate_max) + fprintf(fp, " - %u", info->rate_max); + } + putc('\n', fp); + + fputs("fragment_size: ", fp); + if (info->fragment_size_min <= 1 && + info->fragment_size_max == ULONG_MAX) + fputs("ALL", fp); + else if (info->fragment_size_min > info->fragment_size_max) + fputs("NONE", fp); + else { + fprintf(fp, "%lu", (unsigned long)info->fragment_size_min); + if (info->fragment_size_min < info->fragment_size_max) + fprintf(fp, " - %lu", (unsigned long)info->fragment_size_max); + } + putc('\n', fp); + + fputs("fragments: ", fp); + if (info->fragments_min <= 1 && info->fragments_max == UINT_MAX) + fputs("ALL", fp); + else if (info->fragments_min > info->fragments_max) + fputs("NONE", fp); + else { + fprintf(fp, "%u", info->fragments_min); + if (info->fragments_min < info->fragments_max) + fprintf(fp, " - %u", info->fragments_max); + } + putc('\n', fp); + return 0; +} + +int snd_pcm_dump_hw_params_fail(snd_pcm_hw_params_t *params, FILE *fp) +{ + int k; + if (params->fail_mask == 0) + return 0; + fprintf(fp, "hw_params failed on the following field value(s):\n"); + for (k = 0; k <= SND_PCM_HW_PARAM_LAST; ++k) { + if (!(params->fail_mask & (1U << k))) + continue; + switch (k) { + case SND_PCM_HW_PARAM_ACCESS: + fprintf(fp, "access: %s\n", assoc(params->access, accesses)); + break; + case SND_PCM_HW_PARAM_FORMAT: + fprintf(fp, "format: %s\n", assoc(params->format, formats)); + break; + case SND_PCM_HW_PARAM_SUBFORMAT: + fprintf(fp, "subformat: %s\n", assoc(params->subformat, subformats)); + break; + case SND_PCM_HW_PARAM_CHANNELS: + fprintf(fp, "channels: %d\n", params->channels); + break; + case SND_PCM_HW_PARAM_RATE: + fprintf(fp, "rate: %d\n", params->rate); + break; + case SND_PCM_HW_PARAM_FRAGMENT_SIZE: + fprintf(fp, "fragment_size: %ld\n", (long)params->fragment_size); + break; + case SND_PCM_HW_PARAM_FRAGMENTS: + fprintf(fp, "fragments: %d\n", params->fragments); + break; + default: + assert(0); + break; + } + } + return 0; +} + +int snd_pcm_dump_sw_params_fail(snd_pcm_sw_params_t *params, FILE *fp) +{ + int k; + if (params->fail_mask == 0) + return 0; + fprintf(fp, "sw_params failed on the following field value(s):\n"); + for (k = 0; k <= SND_PCM_SW_PARAM_LAST; ++k) { + if (!(params->fail_mask & (1U << k))) + continue; + switch (k) { + case SND_PCM_SW_PARAM_START_MODE: + fprintf(fp, "start_mode: %s\n", assoc(params->start_mode, starts)); + break; + case SND_PCM_SW_PARAM_READY_MODE: + fprintf(fp, "ready_mode: %s\n", assoc(params->ready_mode, readys)); + break; + case SND_PCM_SW_PARAM_XRUN_MODE: + fprintf(fp, "xrun_mode: %s\n", assoc(params->xrun_mode, xruns)); + break; + case SND_PCM_SW_PARAM_AVAIL_MIN: + fprintf(fp, "avail_min: %ld\n", (long)params->avail_min); + break; + case SND_PCM_SW_PARAM_XFER_MIN: + fprintf(fp, "xfer_min: %ld\n", (long)params->xfer_min); + break; + case SND_PCM_SW_PARAM_XFER_ALIGN: + fprintf(fp, "xfer_align: %ld\n", (long)params->xfer_align); + break; + case SND_PCM_SW_PARAM_TIME: + fprintf(fp, "time: %d\n", params->time); + break; + default: + assert(0); + break; + } + } return 0; } @@ -501,7 +837,7 @@ int snd_pcm_dump(snd_pcm_t *pcm, FILE *fp) const char *snd_pcm_format_name(int format) { - assoc_t *a = assoc_value(format, fmts); + assoc_t *a = assoc_value(format, formats); if (a) return a->name; return 0; @@ -509,7 +845,7 @@ const char *snd_pcm_format_name(int format) const char *snd_pcm_format_description(int format) { - assoc_t *a = assoc_value(format, fmts); + assoc_t *a = assoc_value(format, formats); if (a) return a->desc; return "Unknown"; @@ -517,7 +853,7 @@ const char *snd_pcm_format_description(int format) int snd_pcm_format_value(const char* name) { - assoc_t *a = assoc_name(name, fmts); + assoc_t *a = assoc_name(name, formats); if (a) return a->value; return -1; @@ -526,28 +862,28 @@ int snd_pcm_format_value(const char* name) ssize_t snd_pcm_bytes_to_frames(snd_pcm_t *pcm, ssize_t bytes) { assert(pcm); - assert(pcm->valid_setup); + assert(pcm->setup); return bytes * 8 / pcm->bits_per_frame; } ssize_t snd_pcm_frames_to_bytes(snd_pcm_t *pcm, ssize_t frames) { assert(pcm); - assert(pcm->valid_setup); + assert(pcm->setup); return frames * pcm->bits_per_frame / 8; } ssize_t snd_pcm_bytes_to_samples(snd_pcm_t *pcm, ssize_t bytes) { assert(pcm); - assert(pcm->valid_setup); + assert(pcm->setup); return bytes * 8 / pcm->bits_per_sample; } ssize_t snd_pcm_samples_to_bytes(snd_pcm_t *pcm, ssize_t samples) { assert(pcm); - assert(pcm->valid_setup); + assert(pcm->setup); return samples * pcm->bits_per_sample / 8; } @@ -568,8 +904,44 @@ int snd_pcm_open(snd_pcm_t **pcmp, char *name, return err; err = snd_config_searchv(snd_config, &pcm_conf, "pcm", name, 0); if (err < 0) { + int card, dev, subdev; + char socket[256], sname[256]; + char format[16], file[256]; + err = sscanf(name, "hw:%d,%d,%d", &card, &dev, &subdev); + if (err == 3) + return snd_pcm_hw_open(pcmp, name, card, dev, subdev, stream, mode); + err = sscanf(name, "hw:%d,%d", &card, &dev); + if (err == 2) + return snd_pcm_hw_open(pcmp, name, card, dev, -1, stream, mode); + err = sscanf(name, "plug:%d,%d,%d", &card, &dev, &subdev); + if (err == 3) + return snd_pcm_plug_open_hw(pcmp, name, card, dev, subdev, stream, mode); + err = sscanf(name, "plug:%d,%d", &card, &dev); + if (err == 2) + return snd_pcm_plug_open_hw(pcmp, name, card, dev, -1, stream, mode); + err = sscanf(name, "shm:%256s,%256s", socket, sname); + if (err == 2) + return snd_pcm_shm_open(pcmp, NULL, socket, sname, stream, mode); + err = sscanf(name, "file:%256s,%16s", file, format); + if (err == 2) { + snd_pcm_t *slave; + err = snd_pcm_null_open(&slave, NULL, stream, mode); + if (err < 0) + return err; + return snd_pcm_file_open(pcmp, NULL, file, -1, format, slave, 1); + } + err = sscanf(name, "file:%256s", file); + if (err == 1) { + snd_pcm_t *slave; + err = snd_pcm_null_open(&slave, NULL, stream, mode); + if (err < 0) + return err; + return snd_pcm_file_open(pcmp, NULL, file, -1, "raw", slave, 1); + } + if (strcmp(name, "null") == 0) + return snd_pcm_null_open(pcmp, NULL, stream, mode); ERR("Unknown PCM %s", name); - return err; + return -ENOENT; } if (snd_config_type(pcm_conf) != SND_CONFIG_TYPE_COMPOUND) { ERR("Invalid type for PCM definition"); @@ -655,7 +1027,7 @@ void snd_pcm_areas_from_buf(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, void *buf) { unsigned int channel; - unsigned int channels = pcm->setup.format.channels; + unsigned int channels = pcm->channels; for (channel = 0; channel < channels; ++channel, ++areas) { areas->addr = buf; areas->first = channel * pcm->bits_per_sample; @@ -667,7 +1039,7 @@ void snd_pcm_areas_from_bufs(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, void **bufs) { unsigned int channel; - unsigned int channels = pcm->setup.format.channels; + unsigned int channels = pcm->channels; for (channel = 0; channel < channels; ++channel, ++areas, ++bufs) { areas->addr = *bufs; areas->first = 0; @@ -974,7 +1346,7 @@ ssize_t snd_pcm_read_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, assert(size > 0); assert(state >= SND_PCM_STATE_PREPARED); if (state == SND_PCM_STATE_PREPARED && - pcm->setup.start_mode != SND_PCM_START_EXPLICIT) { + pcm->start_mode != SND_PCM_START_EXPLICIT) { err = snd_pcm_start(pcm); if (err < 0) return err; @@ -989,7 +1361,7 @@ ssize_t snd_pcm_read_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, err = avail; break; } - if ((size_t)avail < pcm->setup.avail_min) { + if ((size_t)avail < pcm->avail_min) { if (state != SND_PCM_STATE_RUNNING) { err = -EPIPE; break; @@ -1041,7 +1413,7 @@ ssize_t snd_pcm_write_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, err = avail; break; } - if ((size_t)avail < pcm->setup.avail_min) { + if ((size_t)avail < pcm->avail_min) { if (state != SND_PCM_STATE_RUNNING) { err = -EPIPE; break; @@ -1066,7 +1438,7 @@ ssize_t snd_pcm_write_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, xfer += err; offset += err; if (state == SND_PCM_STATE_PREPARED && - pcm->setup.start_mode != SND_PCM_START_EXPLICIT) { + pcm->start_mode != SND_PCM_START_EXPLICIT) { err = snd_pcm_start(pcm); if (err < 0) break; @@ -1078,10 +1450,11 @@ ssize_t snd_pcm_write_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, return err; } +#if 0 int snd_pcm_alloc_user_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i) { i->type = SND_PCM_MMAP_USER; - i->size = snd_pcm_frames_to_bytes(pcm, pcm->setup.buffer_size); + i->size = snd_pcm_frames_to_bytes(pcm, pcm->buffer_size); i->u.user.shmid = shmget(IPC_PRIVATE, i->size, 0666); if (i->u.user.shmid < 0) { SYSERR("shmget failed"); @@ -1098,8 +1471,9 @@ int snd_pcm_alloc_user_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i) int snd_pcm_alloc_kernel_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i, int fd) { i->type = SND_PCM_MMAP_KERNEL; - i->size = pcm->setup.mmap_bytes; - i->addr = mmap(NULL, pcm->setup.mmap_bytes, + /* FIXME */ + i->size = PAGE_ALIGN(snd_pcm_frames_to_bytes(pcm, pcm->buffer_size)); + i->addr = mmap(NULL, i->size, PROT_WRITE | PROT_READ, MAP_FILE|MAP_SHARED, fd, SND_PCM_MMAP_OFFSET_DATA); @@ -1131,15 +1505,1176 @@ int snd_pcm_free_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i) } return 0; } +#endif -void snd_pcm_error(const char *file, int line, const char *function, int err, const char *fmt, ...) +int snd_pcm_hw_info_bits_per_sample(snd_pcm_hw_info_t *info, + unsigned int *min, unsigned int *max) +{ + int k; + unsigned int bits_min = UINT_MAX, bits_max = 0; + int changed = 0; + for (k = 0; k <= SND_PCM_FORMAT_LAST; ++k) { + int bits; + if (!(info->format_mask & (1U << k))) + continue; + bits = snd_pcm_format_physical_width(k); + assert(bits > 0); + if ((unsigned) bits < *min || (unsigned)bits > *max) { + info->format_mask &= ~(1U << k); + changed++; + continue; + } + if ((unsigned)bits < bits_min) + bits_min = bits; + if ((unsigned)bits > bits_max) + bits_max = bits; + } + *min = bits_min; + *max = bits_max; + if (info->format_mask == 0) + return -EINVAL; + return changed; +} + + +int snd_pcm_hw_info_complete(snd_pcm_hw_info_t *info) +{ + if (info->msbits == 0) { + unsigned int bits_min = 0, bits_max = UINT_MAX; + snd_pcm_hw_info_bits_per_sample(info, &bits_min, &bits_max); + if (bits_min == bits_max) + info->msbits = bits_min; + } + if (info->rate_divisor == 0 && + info->rate_min == info->rate_max) { + info->rate_master = info->rate_min; + info->rate_divisor = 1; + } + return 0; +} + +struct { + unsigned int rate; + unsigned int flag; +} snd_pcm_rates[] = { + { 5512, SND_PCM_RATE_5512 }, + { 8000, SND_PCM_RATE_8000 }, + { 11025, SND_PCM_RATE_11025 }, + { 16000, SND_PCM_RATE_16000 }, + { 22050, SND_PCM_RATE_22050 }, + { 32000, SND_PCM_RATE_32000 }, + { 44100, SND_PCM_RATE_44100 }, + { 48000, SND_PCM_RATE_48000 }, + { 64000, SND_PCM_RATE_64000 }, + { 88200, SND_PCM_RATE_88200 }, + { 96000, SND_PCM_RATE_96000 }, + { 176400, SND_PCM_RATE_176400 }, + { 192000, SND_PCM_RATE_192000 } +}; + +#define SND_PCM_RATES (sizeof(snd_pcm_rates) / sizeof(snd_pcm_rates[0])) + +int snd_pcm_hw_info_rules_access(snd_pcm_t *pcm, + snd_pcm_hw_info_t *info, + snd_pcm_hw_params_t *params, + unsigned int count, int *rules) +{ + int k; + unsigned int rel, mask; + snd_pcm_hw_info_t i; + rel = *rules & SND_PCM_RULE_REL_MASK; + switch (rel) { + case SND_PCM_RULE_REL_LT: + mask = (1U << params->access) - 1; + break; + case SND_PCM_RULE_REL_LE: + mask = (1U << (params->access + 1)) - 1; + break; + case SND_PCM_RULE_REL_GT: + mask = ~((1U << (params->access + 1)) - 1); + break; + case SND_PCM_RULE_REL_GE: + mask = ~((1U << params->access) - 1); + break; + case SND_PCM_RULE_REL_EQ: + mask = 1U << params->access; + break; + case SND_PCM_RULE_REL_NEAR: + { + unsigned int diff = 0; + int n; + for (diff = 0; diff < 32; ++diff) { + n = (int)params->access - (int)diff; + if (n >= 0) { + unsigned int bit = 1U << n; + if (info->access_mask & bit) { + i = *info; + i.access_mask = bit; + if (snd_pcm_hw_info(pcm, &i) >= 0 && + snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + } + } else if (params->access + diff > SND_PCM_ACCESS_LAST) + break; + if (diff == 0) + continue; + n = params->access + diff; + if (n <= SND_PCM_ACCESS_LAST) { + unsigned int bit = 1U << n; + if (info->access_mask & bit) { + i = *info; + i.access_mask = bit; + if (snd_pcm_hw_info(pcm, &i) >= 0 && + snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + } + } + } + info->access_mask = 0; + return -EINVAL; + } + case SND_PCM_RULE_REL_BITS: + mask = params->access; + break; + default: + assert(0); + return -EINVAL; + } + info->access_mask &= mask; + if (info->access_mask == 0) + return -EINVAL; + switch (rel) { + case SND_PCM_RULE_REL_LE: + case SND_PCM_RULE_REL_LT: + for (k = SND_PCM_ACCESS_LAST; k >= 0; --k) { + if (!(info->access_mask & (1U << k))) + continue; + i = *info; + i.access_mask = 1U << k; + if (snd_pcm_hw_info(pcm, &i) >= 0 && + snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + } + info->access_mask = 0; + return -EINVAL; + default: + for (k = 0; k <= SND_PCM_ACCESS_LAST; ++k) { + if (!(info->access_mask & (1U << k))) + continue; + i = *info; + i.access_mask = 1U << k; + if (snd_pcm_hw_info(pcm, &i) >= 0 && + snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + } + info->access_mask = 0; + return -EINVAL; + } + return 0; +} + +int snd_pcm_hw_info_rules_format(snd_pcm_t *pcm, + snd_pcm_hw_info_t *info, + snd_pcm_hw_params_t *params, + unsigned int count, int *rules) +{ + int k; + unsigned int rel, mask; + snd_pcm_hw_info_t i; + rel = *rules & SND_PCM_RULE_REL_MASK; + switch (rel) { + case SND_PCM_RULE_REL_LT: + mask = (1U << params->format) - 1; + break; + case SND_PCM_RULE_REL_LE: + mask = (1U << (params->format + 1)) - 1; + break; + case SND_PCM_RULE_REL_GT: + mask = ~((1U << (params->format + 1)) - 1); + break; + case SND_PCM_RULE_REL_GE: + mask = ~((1U << params->format) - 1); + break; + case SND_PCM_RULE_REL_EQ: + mask = 1U << params->format; + break; + case SND_PCM_RULE_REL_NEAR: + { + unsigned int diff = 0; + int n; + for (diff = 0; diff < 32; ++diff) { + n = (int)params->format - (int)diff; + if (n >= 0) { + unsigned int bit = 1U << n; + if (info->format_mask & bit) { + i = *info; + i.format_mask = bit; + if (snd_pcm_hw_info(pcm, &i) >= 0 && + snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + } + } else if (params->format + diff > SND_PCM_FORMAT_LAST) + break; + if (diff == 0) + continue; + n = params->format + diff; + if (n <= SND_PCM_FORMAT_LAST) { + unsigned int bit = 1U << n; + if (info->format_mask & bit) { + i = *info; + i.format_mask = bit; + if (snd_pcm_hw_info(pcm, &i) >= 0 && + snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + } + } + } + info->format_mask = 0; + return -EINVAL; + } + case SND_PCM_RULE_REL_BITS: + mask = params->format; + break; + default: + assert(0); + return -EINVAL; + } + info->format_mask &= mask; + if (info->format_mask == 0) + return -EINVAL; + switch (rel) { + case SND_PCM_RULE_REL_LE: + case SND_PCM_RULE_REL_LT: + for (k = SND_PCM_FORMAT_LAST; k >= 0; --k) { + if (!(info->format_mask & (1U << k))) + continue; + i = *info; + i.format_mask = 1U << k; + if (snd_pcm_hw_info(pcm, &i) >= 0 && + snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + } + info->format_mask = 0; + return -EINVAL; + default: + for (k = 0; k <= SND_PCM_FORMAT_LAST; ++k) { + if (!(info->format_mask & (1U << k))) + continue; + i = *info; + i.format_mask = 1U << k; + if (snd_pcm_hw_info(pcm, &i) >= 0 && + snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + } + info->format_mask = 0; + return -EINVAL; + } + return 0; +} + +int snd_pcm_hw_info_rules_subformat(snd_pcm_t *pcm, + snd_pcm_hw_info_t *info, + snd_pcm_hw_params_t *params, + unsigned int count, int *rules) +{ + int k; + unsigned int rel, mask; + snd_pcm_hw_info_t i; + rel = *rules & SND_PCM_RULE_REL_MASK; + switch (rel) { + case SND_PCM_RULE_REL_LT: + mask = (1U << params->subformat) - 1; + break; + case SND_PCM_RULE_REL_LE: + mask = (1U << (params->subformat + 1)) - 1; + break; + case SND_PCM_RULE_REL_GT: + mask = ~((1U << (params->subformat + 1)) - 1); + break; + case SND_PCM_RULE_REL_GE: + mask = ~((1U << params->subformat) - 1); + break; + case SND_PCM_RULE_REL_EQ: + mask = 1U << params->subformat; + break; + case SND_PCM_RULE_REL_NEAR: + { + unsigned int diff = 0; + int n; + for (diff = 0; diff < 32; ++diff) { + n = (int)params->subformat - (int)diff; + if (n >= 0) { + unsigned int bit = 1U << n; + if (info->subformat_mask & bit) { + i = *info; + i.subformat_mask = bit; + if (snd_pcm_hw_info(pcm, &i) >= 0 && + snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + } + } else if (params->subformat + diff > SND_PCM_SUBFORMAT_LAST) + break; + if (diff == 0) + continue; + n = params->subformat + diff; + if (n <= SND_PCM_SUBFORMAT_LAST) { + unsigned int bit = 1U << n; + if (info->subformat_mask & bit) { + i = *info; + i.subformat_mask = bit; + if (snd_pcm_hw_info(pcm, &i) >= 0 && + snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + } + } + } + info->subformat_mask = 0; + return -EINVAL; + } + case SND_PCM_RULE_REL_BITS: + mask = params->subformat; + break; + default: + assert(0); + return -EINVAL; + } + info->subformat_mask &= mask; + if (info->subformat_mask == 0) + return -EINVAL; + switch (rel) { + case SND_PCM_RULE_REL_LE: + case SND_PCM_RULE_REL_LT: + for (k = SND_PCM_SUBFORMAT_LAST; k >= 0; --k) { + if (!(info->subformat_mask & (1U << k))) + continue; + i = *info; + i.subformat_mask = 1U << k; + if (snd_pcm_hw_info(pcm, &i) >= 0 && + snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + } + info->subformat_mask = 0; + return -EINVAL; + default: + for (k = 0; k <= SND_PCM_SUBFORMAT_LAST; ++k) { + if (!(info->subformat_mask & (1U << k))) + continue; + i = *info; + i.subformat_mask = 1U << k; + if (snd_pcm_hw_info(pcm, &i) >= 0 && + snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + } + info->subformat_mask = 0; + return -EINVAL; + } + return 0; +} + +int snd_pcm_hw_info_rules_channels(snd_pcm_t *pcm, + snd_pcm_hw_info_t *info, + snd_pcm_hw_params_t *params, + unsigned int count, int *rules) +{ + int err; + unsigned int rel; + snd_pcm_hw_info_t i; + rel = *rules & SND_PCM_RULE_REL_MASK; + switch (rel) { + case SND_PCM_RULE_REL_LT: + if (info->channels_max > params->channels - 1) + info->channels_max = params->channels - 1; + goto _le; + case SND_PCM_RULE_REL_LE: + if (info->channels_max > params->channels) + info->channels_max = params->channels; + _le: + while (1) { + if (info->channels_min > info->channels_max) + return -EINVAL; + i = *info; + err = snd_pcm_hw_info(pcm, &i); + if (err < 0) { + info->channels_min = i.channels_min; + info->channels_max = i.channels_max; + return err; + } + i.channels_min = i.channels_max; + if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + info->channels_max--; + } + break; + case SND_PCM_RULE_REL_GT: + if (info->channels_min < params->channels + 1) + info->channels_min = params->channels + 1; + goto _ge; + case SND_PCM_RULE_REL_GE: + if (info->channels_min < params->channels) + info->channels_min = params->channels; + _ge: + while (1) { + if (info->channels_min > info->channels_max) + return -EINVAL; + i = *info; + err = snd_pcm_hw_info(pcm, &i); + if (err < 0) { + info->channels_min = i.channels_min; + info->channels_max = i.channels_max; + return err; + } + i.channels_max = i.channels_min; + if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + info->channels_min++; + } + break; + case SND_PCM_RULE_REL_EQ: + info->channels_min = params->channels; + info->channels_max = params->channels; + return snd_pcm_hw_info_rules(pcm, info, params, count - 1, rules + 1); + break; + case SND_PCM_RULE_REL_NEAR: + { + unsigned int max1, min2; + int err1 = -EINVAL, err2 = -EINVAL; + max1 = params->channels; + min2 = params->channels+1; + if (info->channels_min <= max1) { + i = *info; + i.channels_max = max1; + err1 = snd_pcm_hw_info(pcm, &i); + /* shortcut for common case */ + if (err1 >= 0 && max1 == i.channels_max) { + i.channels_min = max1; + if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + i.channels_max = max1 - 1; + err1 = snd_pcm_hw_info(pcm, &i); + } + max1 = i.channels_max; + } + if (min2 <= info->channels_max) { + i = *info; + i.channels_min = min2; + err2 = snd_pcm_hw_info(pcm, &i); + min2 = i.channels_min; + } + while (1) { + unsigned int channels; + if (err1 >= 0) { + if (err2 >= 0) { + if (params->channels - max1 < + min2 - params->channels) + channels = max1; + else + channels = min2; + } else + channels = max1; + } else if (err2 >= 0) + channels = min2; + else { + info->channels_min = UINT_MAX; + info->channels_max = 0; + return -EINVAL; + } + i = *info; + i.channels_min = i.channels_max = channels; + if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + if (channels == max1) { + max1--; + i = *info; + i.channels_max = max1; + err1 = snd_pcm_hw_info(pcm, &i); + max1 = i.channels_max; + } else { + min2++; + i = *info; + i.channels_min = min2; + err2 = snd_pcm_hw_info(pcm, &i); + min2 = i.channels_min; + } + + } + break; + } + case SND_PCM_RULE_REL_BITS: + { + unsigned int k; + for (k = info->channels_min; k < 32; ++k) { + if (!(params->channels & (1U << k))) + continue; + info->channels_min = k; + if (info->channels_min > info->channels_max) + return -EINVAL; + i = *info; + err = snd_pcm_hw_info(pcm, &i); + if (err < 0) { + info->channels_min = i.channels_min; + info->channels_max = i.channels_max; + return err; + } + i.channels_max = i.channels_min; + if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + } + break; + } + default: + assert(0); + return -EINVAL; + } + return 0; +} + +int snd_pcm_hw_info_rules_rate(snd_pcm_t *pcm, + snd_pcm_hw_info_t *info, + snd_pcm_hw_params_t *params, + unsigned int count, int *rules) +{ + int err; + unsigned int rel; + snd_pcm_hw_info_t i; + rel = *rules & SND_PCM_RULE_REL_MASK; + switch (rel) { + case SND_PCM_RULE_REL_LT: + if (info->rate_max > params->rate - 1) + info->rate_max = params->rate - 1; + goto _le; + case SND_PCM_RULE_REL_LE: + if (info->rate_max > params->rate) + info->rate_max = params->rate; + _le: + while (1) { + if (info->rate_min > info->rate_max) + return -EINVAL; + i = *info; + err = snd_pcm_hw_info(pcm, &i); + if (err < 0) { + info->rate_min = i.rate_min; + info->rate_max = i.rate_max; + return err; + } + i.rate_min = i.rate_max; + if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + info->rate_max--; + } + break; + case SND_PCM_RULE_REL_GT: + if (info->rate_min < params->rate + 1) + info->rate_min = params->rate + 1; + goto _ge; + case SND_PCM_RULE_REL_GE: + if (info->rate_min < params->rate) + info->rate_min = params->rate; + _ge: + while (1) { + if (info->rate_min > info->rate_max) + return -EINVAL; + i = *info; + err = snd_pcm_hw_info(pcm, &i); + if (err < 0) { + info->rate_min = i.rate_min; + info->rate_max = i.rate_max; + return err; + } + i.rate_max = i.rate_min; + if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + info->rate_min++; + } + break; + case SND_PCM_RULE_REL_EQ: + info->rate_min = params->rate; + info->rate_max = params->rate; + return snd_pcm_hw_info_rules(pcm, info, params, count - 1, rules + 1); + break; + case SND_PCM_RULE_REL_NEAR: + { + unsigned int max1, min2; + int err1 = -EINVAL, err2 = -EINVAL; + max1 = params->rate; + min2 = params->rate+1; + if (info->rate_min <= max1) { + i = *info; + i.rate_max = max1; + err1 = snd_pcm_hw_info(pcm, &i); + /* shortcut for common case */ + if (err1 >= 0 && max1 == i.rate_max) { + i.rate_min = max1; + if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + i.rate_max = max1 - 1; + err1 = snd_pcm_hw_info(pcm, &i); + } + max1 = i.rate_max; + } + if (min2 <= info->rate_max) { + i = *info; + i.rate_min = min2; + err2 = snd_pcm_hw_info(pcm, &i); + min2 = i.rate_min; + } + while (1) { + unsigned int rate; + if (err1 >= 0) { + if (err2 >= 0) { + if (params->rate - max1 < + min2 - params->rate) + rate = max1; + else + rate = min2; + } else + rate = max1; + } else if (err2 >= 0) + rate = min2; + else { + info->rate_min = UINT_MAX; + info->rate_max = 0; + return -EINVAL; + } + i = *info; + i.rate_min = i.rate_max = rate; + if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + if (rate == max1) { + max1--; + i = *info; + i.rate_max = max1; + err1 = snd_pcm_hw_info(pcm, &i); + max1 = i.rate_max; + } else { + min2++; + i = *info; + i.rate_min = min2; + err2 = snd_pcm_hw_info(pcm, &i); + min2 = i.rate_min; + } + + } + break; + } + case SND_PCM_RULE_REL_BITS: + { + unsigned int k; + for (k = 0; k < SND_PCM_RATES; ++k) { + if (snd_pcm_rates[k].rate < info->rate_min) + continue; + if (!(params->rate & snd_pcm_rates[k].flag)) + continue; + info->rate_min = snd_pcm_rates[k].rate; + if (info->rate_min > info->rate_max) + return -EINVAL; + i = *info; + err = snd_pcm_hw_info(pcm, &i); + if (err < 0) { + info->rate_min = i.rate_min; + info->rate_max = i.rate_max; + return err; + } + i.rate_max = i.rate_min; + if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + } + break; + } + default: + assert(0); + return -EINVAL; + } + return 0; +} + +int snd_pcm_hw_info_rules_fragment_size(snd_pcm_t *pcm, + snd_pcm_hw_info_t *info, + snd_pcm_hw_params_t *params, + unsigned int count, int *rules) +{ + int err; + unsigned int rel; + snd_pcm_hw_info_t i; + rel = *rules & SND_PCM_RULE_REL_MASK; + switch (rel) { + case SND_PCM_RULE_REL_LT: + if (info->fragment_size_max > params->fragment_size - 1) + info->fragment_size_max = params->fragment_size - 1; + goto _le; + case SND_PCM_RULE_REL_LE: + if (info->fragment_size_max > params->fragment_size) + info->fragment_size_max = params->fragment_size; + _le: + while (1) { + if (info->fragment_size_min > info->fragment_size_max) + return -EINVAL; + i = *info; + err = snd_pcm_hw_info(pcm, &i); + if (err < 0) { + info->fragment_size_min = i.fragment_size_min; + info->fragment_size_max = i.fragment_size_max; + return err; + } + i.fragment_size_min = i.fragment_size_max; + if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + info->fragment_size_max--; + } + break; + case SND_PCM_RULE_REL_GT: + if (info->fragment_size_min < params->fragment_size + 1) + info->fragment_size_min = params->fragment_size + 1; + goto _ge; + case SND_PCM_RULE_REL_GE: + if (info->fragment_size_min < params->fragment_size) + info->fragment_size_min = params->fragment_size; + _ge: + while (1) { + if (info->fragment_size_min > info->fragment_size_max) + return -EINVAL; + i = *info; + err = snd_pcm_hw_info(pcm, &i); + if (err < 0) { + info->fragment_size_min = i.fragment_size_min; + info->fragment_size_max = i.fragment_size_max; + return err; + } + i.fragment_size_max = i.fragment_size_min; + if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + info->fragment_size_min++; + } + break; + case SND_PCM_RULE_REL_EQ: + info->fragment_size_min = params->fragment_size; + info->fragment_size_max = params->fragment_size; + return snd_pcm_hw_info_rules(pcm, info, params, count - 1, rules + 1); + break; + case SND_PCM_RULE_REL_NEAR: + { + size_t max1, min2; + int err1 = -EINVAL, err2 = -EINVAL; + max1 = params->fragment_size; + min2 = params->fragment_size+1; + if (info->fragment_size_min <= max1) { + i = *info; + i.fragment_size_max = max1; + err1 = snd_pcm_hw_info(pcm, &i); + /* shortcut for common case */ + if (err1 >= 0 && max1 == i.fragment_size_max) { + i.fragment_size_min = max1; + if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + i.fragment_size_max = max1 - 1; + err1 = snd_pcm_hw_info(pcm, &i); + } + max1 = i.fragment_size_max; + } + if (min2 <= info->fragment_size_max) { + i = *info; + i.fragment_size_min = min2; + err2 = snd_pcm_hw_info(pcm, &i); + min2 = i.fragment_size_min; + } + while (1) { + size_t fragment_size; + if (err1 >= 0) { + if (err2 >= 0) { + if (params->fragment_size - max1 < + min2 - params->fragment_size) + fragment_size = max1; + else + fragment_size = min2; + } else + fragment_size = max1; + } else if (err2 >= 0) + fragment_size = min2; + else { + info->fragment_size_min = ULONG_MAX; + info->fragment_size_max = 0; + return -EINVAL; + } + i = *info; + i.fragment_size_min = i.fragment_size_max = fragment_size; + if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + if (fragment_size == max1) { + max1--; + i = *info; + i.fragment_size_max = max1; + err1 = snd_pcm_hw_info(pcm, &i); + max1 = i.fragment_size_max; + } else { + min2++; + i = *info; + i.fragment_size_min = min2; + err2 = snd_pcm_hw_info(pcm, &i); + min2 = i.fragment_size_min; + } + + } + break; + } + case SND_PCM_RULE_REL_BITS: + { + unsigned int k; + for (k = info->fragment_size_min; k < 32; ++k) { + if (!(params->fragment_size & (1U << k))) + continue; + info->fragment_size_min = 1U << k; + if (info->fragment_size_min > info->fragment_size_max) + return -EINVAL; + i = *info; + err = snd_pcm_hw_info(pcm, &i); + if (err < 0) { + info->fragment_size_min = i.fragment_size_min; + info->fragment_size_max = i.fragment_size_max; + return err; + } + i.fragment_size_max = i.fragment_size_min; + if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + } + break; + } + default: + assert(0); + return -EINVAL; + } + return 0; +} + +int snd_pcm_hw_info_rules_fragments(snd_pcm_t *pcm, + snd_pcm_hw_info_t *info, + snd_pcm_hw_params_t *params, + unsigned int count, int *rules) +{ + int err; + unsigned int rel; + snd_pcm_hw_info_t i; + rel = *rules & SND_PCM_RULE_REL_MASK; + switch (rel) { + case SND_PCM_RULE_REL_LT: + if (info->fragments_max > params->fragments - 1) + info->fragments_max = params->fragments - 1; + goto _le; + case SND_PCM_RULE_REL_LE: + if (info->fragments_max > params->fragments) + info->fragments_max = params->fragments; + _le: + while (1) { + if (info->fragments_min > info->fragments_max) + return -EINVAL; + i = *info; + err = snd_pcm_hw_info(pcm, &i); + if (err < 0) { + info->fragments_min = i.fragments_min; + info->fragments_max = i.fragments_max; + return err; + } + i.fragments_min = i.fragments_max; + if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + info->fragments_max--; + } + break; + case SND_PCM_RULE_REL_GT: + if (info->fragments_min < params->fragments + 1) + info->fragments_min = params->fragments + 1; + goto _ge; + case SND_PCM_RULE_REL_GE: + if (info->fragments_min < params->fragments) + info->fragments_min = params->fragments; + _ge: + while (1) { + if (info->fragments_min > info->fragments_max) + return -EINVAL; + i = *info; + err = snd_pcm_hw_info(pcm, &i); + if (err < 0) { + info->fragments_min = i.fragments_min; + info->fragments_max = i.fragments_max; + return err; + } + i.fragments_max = i.fragments_min; + if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + info->fragments_min++; + } + break; + case SND_PCM_RULE_REL_EQ: + info->fragments_min = params->fragments; + info->fragments_max = params->fragments; + return snd_pcm_hw_info_rules(pcm, info, params, count - 1, rules + 1); + break; + case SND_PCM_RULE_REL_NEAR: + { + unsigned int max1, min2; + int err1 = -EINVAL, err2 = -EINVAL; + max1 = params->fragments; + min2 = params->fragments+1; + if (info->fragments_min <= max1) { + i = *info; + i.fragments_max = max1; + err1 = snd_pcm_hw_info(pcm, &i); + /* shortcut for common case */ + if (err1 >= 0 && max1 == i.fragments_max) { + i.fragments_min = max1; + if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + i.fragments_max = max1 - 1; + err1 = snd_pcm_hw_info(pcm, &i); + } + max1 = i.fragments_max; + } + if (min2 <= info->fragments_max) { + i = *info; + i.fragments_min = min2; + err2 = snd_pcm_hw_info(pcm, &i); + min2 = i.fragments_min; + } + while (1) { + unsigned int fragments; + if (err1 >= 0) { + if (err2 >= 0) { + if (params->fragments - max1 < + min2 - params->fragments) + fragments = max1; + else + fragments = min2; + } else + fragments = max1; + } else if (err2 >= 0) + fragments = min2; + else { + info->fragments_min = UINT_MAX; + info->fragments_max = 0; + return -EINVAL; + } + i = *info; + i.fragments_min = i.fragments_max = fragments; + if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + if (fragments == max1) { + max1--; + i = *info; + i.fragments_max = max1; + err1 = snd_pcm_hw_info(pcm, &i); + max1 = i.fragments_max; + } else { + min2++; + i = *info; + i.fragments_min = min2; + err2 = snd_pcm_hw_info(pcm, &i); + min2 = i.fragments_min; + } + + } + break; + } + case SND_PCM_RULE_REL_BITS: + { + unsigned int k; + for (k = info->fragments_min; k < 32; ++k) { + if (!(params->fragments & (1U << k))) + continue; + info->fragments_min = k; + if (info->fragments_min > info->fragments_max) + return -EINVAL; + i = *info; + err = snd_pcm_hw_info(pcm, &i); + if (err < 0) { + info->fragments_min = i.fragments_min; + info->fragments_max = i.fragments_max; + return err; + } + i.fragments_max = i.fragments_min; + if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { + *info = i; + return 0; + } + } + break; + } + default: + assert(0); + return -EINVAL; + } + return 0; +} + +int snd_pcm_hw_info_rules(snd_pcm_t *pcm, + snd_pcm_hw_info_t *info, + snd_pcm_hw_params_t *params, + unsigned int count, int *rules) +{ + unsigned int par; + if (count == 0) + return snd_pcm_hw_info(pcm, info); + par = rules[0] & SND_PCM_RULE_PAR_MASK; + switch (par) { + case SND_PCM_HW_PARAM_ACCESS: + return snd_pcm_hw_info_rules_access(pcm, info, params, count, rules); + case SND_PCM_HW_PARAM_FORMAT: + return snd_pcm_hw_info_rules_format(pcm, info, params, count, rules); + case SND_PCM_HW_PARAM_SUBFORMAT: + return snd_pcm_hw_info_rules_subformat(pcm, info, params, count, rules); + case SND_PCM_HW_PARAM_CHANNELS: + return snd_pcm_hw_info_rules_channels(pcm, info, params, count, rules); + case SND_PCM_HW_PARAM_RATE: + return snd_pcm_hw_info_rules_rate(pcm, info, params, count, rules); + case SND_PCM_HW_PARAM_FRAGMENT_SIZE: + return snd_pcm_hw_info_rules_fragment_size(pcm, info, params, count, rules); + case SND_PCM_HW_PARAM_FRAGMENTS: + return snd_pcm_hw_info_rules_fragments(pcm, info, params, count, rules); + default: + assert(0); + return -EINVAL; + } +} + +int snd_pcm_hw_info_rulesv(snd_pcm_t *pcm, + snd_pcm_hw_info_t *info, + snd_pcm_hw_params_t *params, ...) +{ + va_list arg; + unsigned int count = 0; + int rules[32]; + va_start(arg, params); + while (1) { + int i = va_arg(arg, int); + if (i == -1) + break; + rules[count++] = i; + } + va_end(arg); + return snd_pcm_hw_info_rules(pcm, info, params, count, rules); +} + +int snd_pcm_hw_params_rules(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + unsigned int count, int *rules) +{ + int err; + snd_pcm_hw_info_t info; + unsigned int k; + snd_pcm_hw_params_to_info(params, &info); + for (k = 0; k < count; ++k) { + switch (rules[k] & SND_PCM_RULE_PAR_MASK) { + case SND_PCM_HW_PARAM_ACCESS: + info.access_mask = ~0; + break; + case SND_PCM_HW_PARAM_FORMAT: + info.format_mask = ~0; + break; + case SND_PCM_HW_PARAM_SUBFORMAT: + info.subformat_mask = ~0; + break; + case SND_PCM_HW_PARAM_CHANNELS: + info.channels_min = 0; + info.channels_max = UINT_MAX; + break; + case SND_PCM_HW_PARAM_RATE: + info.rate_min = 0; + info.rate_max = UINT_MAX; + break; + case SND_PCM_HW_PARAM_FRAGMENT_SIZE: + info.fragment_size_min = 0; + info.fragment_size_max = ULONG_MAX; + break; + case SND_PCM_HW_PARAM_FRAGMENTS: + info.fragments_min = 0; + info.fragments_max = UINT_MAX; + break; + default: + assert(0); + break; + } + } + err = snd_pcm_hw_info_rules(pcm, &info, params, count, rules); + if (err < 0) { + snd_pcm_hw_info_to_params_fail(&info, params); + return err; + } + snd_pcm_hw_info_to_params(&info, params); + return snd_pcm_hw_params(pcm, params); +} + +int snd_pcm_hw_params_rulesv(snd_pcm_t *pcm, + snd_pcm_hw_params_t *params, ...) { va_list arg; - va_start(arg, fmt); - fprintf(stderr, "ALSA PCM lib %s:%i:(%s) ", file, line, function); - vfprintf(stderr, fmt, arg); - if (err) - fprintf(stderr, ": %s", snd_strerror(err)); - putc('\n', stderr); + unsigned int count = 0; + int rules[32]; + va_start(arg, params); + while (1) { + int i = va_arg(arg, int); + if (i == -1) + break; + rules[count++] = i; + } va_end(arg); + return snd_pcm_hw_params_rules(pcm, params, count, rules); } + diff --git a/src/pcm/pcm_adpcm.c b/src/pcm/pcm_adpcm.c index 2c58b1da..d6be0e57 100644 --- a/src/pcm/pcm_adpcm.c +++ b/src/pcm/pcm_adpcm.c @@ -69,8 +69,6 @@ typedef struct { int getput_idx; adpcm_f func; int sformat; - int cformat; - int cxfer_mode, cmmap_shape; adpcm_state_t *states; } snd_pcm_adpcm_t; @@ -331,102 +329,82 @@ static int snd_pcm_adpcm_close(snd_pcm_t *pcm) return 0; } -static int snd_pcm_adpcm_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info) +static int snd_pcm_adpcm_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info) { snd_pcm_adpcm_t *adpcm = pcm->private; - unsigned int req_mask = info->req_mask; - unsigned int sfmt = info->req.format.sfmt; + unsigned int format_mask, access_mask; int err; - if (req_mask & SND_PCM_PARAMS_SFMT) { - if (adpcm->sformat == SND_PCM_SFMT_IMA_ADPCM ? - !snd_pcm_format_linear(sfmt) : - sfmt != SND_PCM_SFMT_IMA_ADPCM) { - info->req.fail_mask = SND_PCM_PARAMS_SFMT; - info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL; - return -EINVAL; - } + info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | + SND_PCM_ACCBIT_RW_INTERLEAVED | + SND_PCM_ACCBIT_MMAP_NONINTERLEAVED | + SND_PCM_ACCBIT_RW_NONINTERLEAVED); + access_mask = info->access_mask; + if (access_mask == 0) + return -EINVAL; + if (adpcm->sformat == SND_PCM_FORMAT_IMA_ADPCM) + info->format_mask &= SND_PCM_FMTBIT_LINEAR; + else + info->format_mask &= SND_PCM_FMTBIT_IMA_ADPCM; + format_mask = info->format_mask; + if (format_mask == 0) + return -EINVAL; + + info->format_mask = 1U << adpcm->sformat; + info->access_mask = SND_PCM_ACCBIT_MMAP; + err = snd_pcm_hw_info(adpcm->plug.slave, info); + if (info->format_mask) + info->format_mask = format_mask; + if (info->access_mask) { + adpcm->plug.saccess_mask = info->access_mask; + info->access_mask = access_mask; } - info->req_mask |= SND_PCM_PARAMS_SFMT; - info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE | - SND_PCM_PARAMS_XFER_MODE); - info->req.format.sfmt = adpcm->sformat; - err = snd_pcm_params_info(adpcm->plug.slave, info); - info->req_mask = req_mask; - info->req.format.sfmt = sfmt; if (err < 0) return err; - if (req_mask & SND_PCM_PARAMS_SFMT) - info->formats = 1 << sfmt; - else - info->formats = adpcm->sformat == SND_PCM_SFMT_IMA_ADPCM ? - SND_PCM_LINEAR_FORMATS : 1 << SND_PCM_SFMT_IMA_ADPCM; - info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); - info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED; - return err; + info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); + snd_pcm_hw_info_complete(info); + return 0; } -static int snd_pcm_adpcm_params(snd_pcm_t *pcm, snd_pcm_params_t * params) +static int snd_pcm_adpcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) { snd_pcm_adpcm_t *adpcm = pcm->private; snd_pcm_t *slave = adpcm->plug.slave; + unsigned int format, access; int err; - if (adpcm->sformat == SND_PCM_SFMT_IMA_ADPCM ? - !snd_pcm_format_linear(params->format.sfmt) : - params->format.sfmt != SND_PCM_SFMT_IMA_ADPCM) { - params->fail_mask = SND_PCM_PARAMS_SFMT; - params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL; - return -EINVAL; - } - adpcm->cformat = params->format.sfmt; - adpcm->cxfer_mode = params->xfer_mode; - adpcm->cmmap_shape = params->mmap_shape; - params->format.sfmt = adpcm->sformat; - params->xfer_mode = SND_PCM_XFER_UNSPECIFIED; - params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED; - err = snd_pcm_params_mmap(slave, params); - params->format.sfmt = adpcm->cformat; - params->xfer_mode = adpcm->cxfer_mode; - params->mmap_shape = adpcm->cmmap_shape; - return err; -} - -static int snd_pcm_adpcm_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup) -{ - snd_pcm_adpcm_t *adpcm = pcm->private; - int err = snd_pcm_setup(adpcm->plug.slave, setup); + format = params->format; + access = params->access; + params->format = adpcm->sformat; + if (adpcm->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) + params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED; + else if (adpcm->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED) + params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; + else + assert(0); + err = snd_pcm_hw_params(slave, params); + params->format = format; + params->access = access; if (err < 0) return err; - assert(adpcm->sformat == setup->format.sfmt); - if (adpcm->cxfer_mode == SND_PCM_XFER_UNSPECIFIED) - setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED; - else - setup->xfer_mode = adpcm->cxfer_mode; - if (adpcm->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED) - setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED; - else - setup->mmap_shape = adpcm->cmmap_shape; - setup->format.sfmt = adpcm->cformat; - setup->mmap_bytes = 0; if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { - if (adpcm->sformat == SND_PCM_SFMT_IMA_ADPCM) { - adpcm->getput_idx = get_index(adpcm->cformat, SND_PCM_SFMT_S16); + if (adpcm->sformat == SND_PCM_FORMAT_IMA_ADPCM) { + adpcm->getput_idx = get_index(format, SND_PCM_FORMAT_S16); adpcm->func = adpcm_encode; } else { - adpcm->getput_idx = put_index(SND_PCM_SFMT_S16, adpcm->sformat); + adpcm->getput_idx = put_index(SND_PCM_FORMAT_S16, adpcm->sformat); adpcm->func = adpcm_decode; } } else { - if (adpcm->sformat == SND_PCM_SFMT_IMA_ADPCM) { - adpcm->getput_idx = put_index(SND_PCM_SFMT_S16, adpcm->cformat); + if (adpcm->sformat == SND_PCM_FORMAT_IMA_ADPCM) { + adpcm->getput_idx = put_index(SND_PCM_FORMAT_S16, format); adpcm->func = adpcm_decode; } else { - adpcm->getput_idx = get_index(adpcm->sformat, SND_PCM_SFMT_S16); + adpcm->getput_idx = get_index(adpcm->sformat, SND_PCM_FORMAT_S16); adpcm->func = adpcm_encode; } } if (adpcm->states) free(adpcm->states); - adpcm->states = malloc(setup->format.channels * sizeof(*adpcm->states)); + adpcm->states = malloc(params->channels * sizeof(*adpcm->states)); return 0; } @@ -434,7 +412,7 @@ static int snd_pcm_adpcm_init(snd_pcm_t *pcm) { snd_pcm_adpcm_t *adpcm = pcm->private; unsigned int k; - for (k = 0; k < pcm->setup.format.channels; ++k) { + for (k = 0; k < pcm->channels; ++k) { adpcm->states[k].pred_val = 0; adpcm->states[k].step_idx = 0; } @@ -458,7 +436,7 @@ static ssize_t snd_pcm_adpcm_write_areas(snd_pcm_t *pcm, size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer); adpcm->func(areas, offset, snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave), - frames, pcm->setup.format.channels, + frames, pcm->channels, adpcm->getput_idx, adpcm->states); err = snd_pcm_mmap_forward(slave, frames); if (err < 0) @@ -493,7 +471,7 @@ static ssize_t snd_pcm_adpcm_read_areas(snd_pcm_t *pcm, size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer); adpcm->func(snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave), areas, offset, - frames, pcm->setup.format.channels, + frames, pcm->channels, adpcm->getput_idx, adpcm->states); err = snd_pcm_mmap_forward(slave, frames); if (err < 0) @@ -516,7 +494,7 @@ static void snd_pcm_adpcm_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_adpcm_t *adpcm = pcm->private; fprintf(fp, "Ima-ADPCM conversion PCM (%s)\n", snd_pcm_format_name(adpcm->sformat)); - if (pcm->valid_setup) { + if (pcm->setup) { fprintf(fp, "Its setup is:\n"); snd_pcm_dump_setup(pcm, fp); } @@ -527,12 +505,12 @@ static void snd_pcm_adpcm_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_adpcm_ops = { close: snd_pcm_adpcm_close, info: snd_pcm_plugin_info, - params_info: snd_pcm_adpcm_params_info, - params: snd_pcm_adpcm_params, - setup: snd_pcm_adpcm_setup, + hw_info: snd_pcm_adpcm_hw_info, + hw_params: snd_pcm_adpcm_hw_params, + sw_params: snd_pcm_plugin_sw_params, + dig_info: snd_pcm_plugin_dig_info, + dig_params: snd_pcm_plugin_dig_params, channel_info: snd_pcm_plugin_channel_info, - channel_params: snd_pcm_plugin_channel_params, - channel_setup: snd_pcm_plugin_channel_setup, dump: snd_pcm_adpcm_dump, nonblock: snd_pcm_plugin_nonblock, async: snd_pcm_plugin_async, @@ -546,7 +524,7 @@ int snd_pcm_adpcm_open(snd_pcm_t **pcmp, char *name, int sformat, snd_pcm_t *sla snd_pcm_adpcm_t *adpcm; assert(pcmp && slave); if (snd_pcm_format_linear(sformat) != 1 && - sformat != SND_PCM_SFMT_IMA_ADPCM) + sformat != SND_PCM_FORMAT_IMA_ADPCM) return -EINVAL; adpcm = calloc(1, sizeof(snd_pcm_adpcm_t)); if (!adpcm) { @@ -614,7 +592,7 @@ int _snd_pcm_adpcm_open(snd_pcm_t **pcmp, char *name, if (sformat < 0) return -EINVAL; if (snd_pcm_format_linear(sformat) != 1 && - sformat != SND_PCM_SFMT_IMA_ADPCM) + sformat != SND_PCM_FORMAT_IMA_ADPCM) return -EINVAL; continue; } diff --git a/src/pcm/pcm_alaw.c b/src/pcm/pcm_alaw.c index de8f944e..fd5f619c 100644 --- a/src/pcm/pcm_alaw.c +++ b/src/pcm/pcm_alaw.c @@ -35,8 +35,6 @@ typedef struct { int getput_idx; alaw_f func; int sformat; - int cformat; - int cxfer_mode, cmmap_shape; } snd_pcm_alaw_t; static inline int val_seg(int val) @@ -213,96 +211,76 @@ static void alaw_encode(snd_pcm_channel_area_t *src_areas, } } -static int snd_pcm_alaw_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info) +static int snd_pcm_alaw_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info) { snd_pcm_alaw_t *alaw = pcm->private; - unsigned int req_mask = info->req_mask; - unsigned int sfmt = info->req.format.sfmt; + unsigned int format_mask, access_mask; int err; - if (req_mask & SND_PCM_PARAMS_SFMT) { - if (alaw->sformat == SND_PCM_SFMT_A_LAW ? - !snd_pcm_format_linear(sfmt) : - sfmt != SND_PCM_SFMT_A_LAW) { - info->req.fail_mask = SND_PCM_PARAMS_SFMT; - info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL; - return -EINVAL; - } + info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | + SND_PCM_ACCBIT_RW_INTERLEAVED | + SND_PCM_ACCBIT_MMAP_NONINTERLEAVED | + SND_PCM_ACCBIT_RW_NONINTERLEAVED); + access_mask = info->access_mask; + if (access_mask == 0) + return -EINVAL; + if (alaw->sformat == SND_PCM_FORMAT_MU_LAW) + info->format_mask &= SND_PCM_FMTBIT_LINEAR; + else + info->format_mask &= SND_PCM_FMTBIT_MU_LAW; + format_mask = info->format_mask; + if (format_mask == 0) + return -EINVAL; + + info->format_mask = 1U << alaw->sformat; + info->access_mask = SND_PCM_ACCBIT_MMAP; + err = snd_pcm_hw_info(alaw->plug.slave, info); + if (info->format_mask) + info->format_mask = format_mask; + if (info->access_mask) { + alaw->plug.saccess_mask = info->access_mask; + info->access_mask = access_mask; } - info->req_mask |= SND_PCM_PARAMS_SFMT; - info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE | - SND_PCM_PARAMS_XFER_MODE); - info->req.format.sfmt = alaw->sformat; - err = snd_pcm_params_info(alaw->plug.slave, info); - info->req_mask = req_mask; - info->req.format.sfmt = sfmt; if (err < 0) return err; - if (req_mask & SND_PCM_PARAMS_SFMT) - info->formats = 1 << sfmt; - else - info->formats = alaw->sformat == SND_PCM_SFMT_A_LAW ? - SND_PCM_LINEAR_FORMATS : 1 << SND_PCM_SFMT_A_LAW; - info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); - info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED; - return err; + info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); + snd_pcm_hw_info_complete(info); + return 0; } -static int snd_pcm_alaw_params(snd_pcm_t *pcm, snd_pcm_params_t * params) +static int snd_pcm_alaw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) { snd_pcm_alaw_t *alaw = pcm->private; snd_pcm_t *slave = alaw->plug.slave; + unsigned int format, access; int err; - if (alaw->sformat == SND_PCM_SFMT_A_LAW ? - !snd_pcm_format_linear(params->format.sfmt) : - params->format.sfmt != SND_PCM_SFMT_A_LAW) { - params->fail_mask = SND_PCM_PARAMS_SFMT; - params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL; - return -EINVAL; - } - alaw->cformat = params->format.sfmt; - alaw->cxfer_mode = params->xfer_mode; - alaw->cmmap_shape = params->mmap_shape; - params->format.sfmt = alaw->sformat; - params->xfer_mode = SND_PCM_XFER_UNSPECIFIED; - params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED; - err = snd_pcm_params_mmap(slave, params); - params->format.sfmt = alaw->cformat; - params->xfer_mode = alaw->cxfer_mode; - params->mmap_shape = alaw->cmmap_shape; - return err; -} - -static int snd_pcm_alaw_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup) -{ - snd_pcm_alaw_t *alaw = pcm->private; - int err = snd_pcm_setup(alaw->plug.slave, setup); + format = params->format; + access = params->access; + params->format = alaw->sformat; + if (alaw->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) + params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED; + else if (alaw->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED) + params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; + else + assert(0); + err = snd_pcm_hw_params(slave, params); + params->format = format; + params->access = access; if (err < 0) return err; - assert(alaw->sformat == setup->format.sfmt); - if (alaw->cxfer_mode == SND_PCM_XFER_UNSPECIFIED) - setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED; - else - setup->xfer_mode = alaw->cxfer_mode; - if (alaw->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED) - setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED; - else - setup->mmap_shape = alaw->cmmap_shape; - setup->format.sfmt = alaw->cformat; - setup->mmap_bytes = 0; if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { - if (alaw->sformat == SND_PCM_SFMT_A_LAW) { - alaw->getput_idx = get_index(alaw->cformat, SND_PCM_SFMT_S16); + if (alaw->sformat == SND_PCM_FORMAT_MU_LAW) { + alaw->getput_idx = get_index(format, SND_PCM_FORMAT_S16); alaw->func = alaw_encode; } else { - alaw->getput_idx = put_index(SND_PCM_SFMT_S16, alaw->sformat); + alaw->getput_idx = put_index(SND_PCM_FORMAT_S16, alaw->sformat); alaw->func = alaw_decode; } } else { - if (alaw->sformat == SND_PCM_SFMT_A_LAW) { - alaw->getput_idx = put_index(SND_PCM_SFMT_S16, alaw->cformat); + if (alaw->sformat == SND_PCM_FORMAT_MU_LAW) { + alaw->getput_idx = put_index(SND_PCM_FORMAT_S16, format); alaw->func = alaw_decode; } else { - alaw->getput_idx = get_index(alaw->sformat, SND_PCM_SFMT_S16); + alaw->getput_idx = get_index(alaw->sformat, SND_PCM_FORMAT_S16); alaw->func = alaw_encode; } } @@ -326,7 +304,7 @@ static ssize_t snd_pcm_alaw_write_areas(snd_pcm_t *pcm, size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer); alaw->func(areas, offset, snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave), - frames, pcm->setup.format.channels, + frames, pcm->channels, alaw->getput_idx); err = snd_pcm_mmap_forward(slave, frames); if (err < 0) @@ -361,7 +339,7 @@ static ssize_t snd_pcm_alaw_read_areas(snd_pcm_t *pcm, size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer); alaw->func(snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave), areas, offset, - frames, pcm->setup.format.channels, + frames, pcm->channels, alaw->getput_idx); err = snd_pcm_mmap_forward(slave, frames); if (err < 0) @@ -384,7 +362,7 @@ static void snd_pcm_alaw_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_alaw_t *alaw = pcm->private; fprintf(fp, "A-Law conversion PCM (%s)\n", snd_pcm_format_name(alaw->sformat)); - if (pcm->valid_setup) { + if (pcm->setup) { fprintf(fp, "Its setup is:\n"); snd_pcm_dump_setup(pcm, fp); } @@ -395,12 +373,12 @@ static void snd_pcm_alaw_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_alaw_ops = { close: snd_pcm_plugin_close, info: snd_pcm_plugin_info, - params_info: snd_pcm_alaw_params_info, - params: snd_pcm_alaw_params, - setup: snd_pcm_alaw_setup, + hw_info: snd_pcm_alaw_hw_info, + hw_params: snd_pcm_alaw_hw_params, + sw_params: snd_pcm_plugin_sw_params, + dig_info: snd_pcm_plugin_dig_info, + dig_params: snd_pcm_plugin_dig_params, channel_info: snd_pcm_plugin_channel_info, - channel_params: snd_pcm_plugin_channel_params, - channel_setup: snd_pcm_plugin_channel_setup, dump: snd_pcm_alaw_dump, nonblock: snd_pcm_plugin_nonblock, async: snd_pcm_plugin_async, @@ -414,7 +392,7 @@ int snd_pcm_alaw_open(snd_pcm_t **pcmp, char *name, int sformat, snd_pcm_t *slav snd_pcm_alaw_t *alaw; assert(pcmp && slave); if (snd_pcm_format_linear(sformat) != 1 && - sformat != SND_PCM_SFMT_A_LAW) + sformat != SND_PCM_FORMAT_A_LAW) return -EINVAL; alaw = calloc(1, sizeof(snd_pcm_alaw_t)); if (!alaw) { @@ -481,7 +459,7 @@ int _snd_pcm_alaw_open(snd_pcm_t **pcmp, char *name, if (sformat < 0) return -EINVAL; if (snd_pcm_format_linear(sformat) != 1 && - sformat != SND_PCM_SFMT_A_LAW) + sformat != SND_PCM_FORMAT_A_LAW) return -EINVAL; continue; } diff --git a/src/pcm/pcm_file.c b/src/pcm/pcm_file.c index 192bccc6..4130812e 100644 --- a/src/pcm/pcm_file.c +++ b/src/pcm/pcm_file.c @@ -23,11 +23,16 @@ #include "pcm_local.h" #include "pcm_plugin.h" +enum { + SND_PCM_FILE_FORMAT_RAW +}; + typedef struct { snd_pcm_t *slave; int close_slave; char *fname; int fd; + int format; } snd_pcm_file_t; static int snd_pcm_file_close(snd_pcm_t *pcm) @@ -68,18 +73,6 @@ static int snd_pcm_file_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * in return snd_pcm_channel_info(file->slave, info); } -static int snd_pcm_file_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params) -{ - snd_pcm_file_t *file = pcm->private; - return snd_pcm_channel_params(file->slave, params); -} - -static int snd_pcm_file_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup) -{ - snd_pcm_file_t *file = pcm->private; - return snd_pcm_channel_setup(file->slave, setup); -} - static int snd_pcm_file_status(snd_pcm_t *pcm, snd_pcm_status_t * status) { snd_pcm_file_t *file = pcm->private; @@ -148,11 +141,16 @@ static void snd_pcm_file_write_areas(snd_pcm_t *pcm, snd_pcm_file_t *file = pcm->private; size_t bytes = snd_pcm_frames_to_bytes(pcm, frames); char *buf; - size_t channels = pcm->setup.format.channels; + size_t channels = pcm->channels; snd_pcm_channel_area_t buf_areas[channels]; size_t channel; ssize_t r; - if (pcm->setup.mmap_shape != SND_PCM_MMAP_INTERLEAVED) { + switch (pcm->access) { + case SND_PCM_ACCESS_MMAP_INTERLEAVED: + case SND_PCM_ACCESS_RW_INTERLEAVED: + buf = snd_pcm_channel_area_addr(areas, offset); + break; + default: buf = alloca(bytes); for (channel = 0; channel < channels; ++channel) { snd_pcm_channel_area_t *a = &buf_areas[channel]; @@ -161,9 +159,8 @@ static void snd_pcm_file_write_areas(snd_pcm_t *pcm, a->step = pcm->bits_per_frame; } snd_pcm_areas_copy(areas, offset, buf_areas, 0, - channels, frames, pcm->setup.format.sfmt); - } else - buf = snd_pcm_channel_area_addr(areas, offset); + channels, frames, pcm->format); + } r = write(file->fd, buf, bytes); assert(r == (ssize_t)bytes); } @@ -183,7 +180,7 @@ static ssize_t snd_pcm_file_writei(snd_pcm_t *pcm, const void *buffer, size_t si static ssize_t snd_pcm_file_writen(snd_pcm_t *pcm, void **bufs, size_t size) { snd_pcm_file_t *file = pcm->private; - snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + snd_pcm_channel_area_t areas[pcm->channels]; ssize_t n = snd_pcm_writen(file->slave, bufs, size); if (n > 0) { snd_pcm_areas_from_bufs(pcm, areas, bufs); @@ -207,7 +204,7 @@ static ssize_t snd_pcm_file_readi(snd_pcm_t *pcm, void *buffer, size_t size) static ssize_t snd_pcm_file_readn(snd_pcm_t *pcm, void **bufs, size_t size) { snd_pcm_file_t *file = pcm->private; - snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + snd_pcm_channel_area_t areas[pcm->channels]; ssize_t n = snd_pcm_writen(file->slave, bufs, size); if (n > 0) { snd_pcm_areas_from_bufs(pcm, areas, bufs); @@ -226,12 +223,12 @@ static ssize_t snd_pcm_file_mmap_forward(snd_pcm_t *pcm, size_t size) return n; while (xfer < (size_t)n) { size_t frames = size - xfer; - size_t cont = pcm->setup.buffer_size - ofs; + size_t cont = pcm->buffer_size - ofs; if (cont < frames) frames = cont; snd_pcm_file_write_areas(pcm, snd_pcm_mmap_areas(file->slave), ofs, frames); ofs += frames; - if (ofs == pcm->setup.buffer_size) + if (ofs == pcm->buffer_size) ofs = 0; xfer += frames; } @@ -250,44 +247,46 @@ static int snd_pcm_file_set_avail_min(snd_pcm_t *pcm, size_t frames) return snd_pcm_set_avail_min(file->slave, frames); } -static int snd_pcm_file_mmap(snd_pcm_t *pcm) +static int snd_pcm_file_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info) { snd_pcm_file_t *file = pcm->private; - int err = snd_pcm_mmap(file->slave); - if (err < 0) - return err; - pcm->mmap_info_count = file->slave->mmap_info_count; - pcm->mmap_info = file->slave->mmap_info; - return 0; + return snd_pcm_hw_info(file->slave, info); } -static int snd_pcm_file_munmap(snd_pcm_t *pcm) +static int snd_pcm_file_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) { snd_pcm_file_t *file = pcm->private; - int err = snd_pcm_munmap(file->slave); - if (err < 0) - return err; - pcm->mmap_info_count = 0; - pcm->mmap_info = 0; - return 0; + return snd_pcm_hw_params(file->slave, params); +} + +static int snd_pcm_file_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_sw_params(file->slave, params); +} + +static int snd_pcm_file_dig_info(snd_pcm_t *pcm, snd_pcm_dig_info_t * info) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_dig_info(file->slave, info); } -static int snd_pcm_file_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info) +static int snd_pcm_file_dig_params(snd_pcm_t *pcm, snd_pcm_dig_params_t * params) { snd_pcm_file_t *file = pcm->private; - return snd_pcm_params_info(file->slave, info); + return snd_pcm_dig_params(file->slave, params); } -static int snd_pcm_file_params(snd_pcm_t *pcm, snd_pcm_params_t * params) +static int snd_pcm_file_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED) { snd_pcm_file_t *file = pcm->private; - return snd_pcm_params(file->slave, params); + return snd_pcm_mmap(file->slave); } -static int snd_pcm_file_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup) +static int snd_pcm_file_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED) { snd_pcm_file_t *file = pcm->private; - return snd_pcm_setup(file->slave, setup); + return snd_pcm_munmap(file->slave); } static void snd_pcm_file_dump(snd_pcm_t *pcm, FILE *fp) @@ -297,7 +296,7 @@ static void snd_pcm_file_dump(snd_pcm_t *pcm, FILE *fp) fprintf(fp, "File PCM (file=%s)\n", file->fname); else fprintf(fp, "File PCM (fd=%d)\n", file->fd); - if (pcm->valid_setup) { + if (pcm->setup) { fprintf(fp, "Its setup is:\n"); snd_pcm_dump_setup(pcm, fp); } @@ -308,12 +307,12 @@ static void snd_pcm_file_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_file_ops = { close: snd_pcm_file_close, info: snd_pcm_file_info, - params_info: snd_pcm_file_params_info, - params: snd_pcm_file_params, - setup: snd_pcm_file_setup, + hw_info: snd_pcm_file_hw_info, + hw_params: snd_pcm_file_hw_params, + sw_params: snd_pcm_file_sw_params, + dig_info: snd_pcm_file_dig_info, + dig_params: snd_pcm_file_dig_params, channel_info: snd_pcm_file_channel_info, - channel_params: snd_pcm_file_channel_params, - channel_setup: snd_pcm_file_channel_setup, dump: snd_pcm_file_dump, nonblock: snd_pcm_file_nonblock, async: snd_pcm_file_async, @@ -340,11 +339,11 @@ snd_pcm_fast_ops_t snd_pcm_file_fast_ops = { set_avail_min: snd_pcm_file_set_avail_min, }; -int snd_pcm_file_open(snd_pcm_t **pcmp, char *name, char *fname, int fd, snd_pcm_t *slave, int close_slave) +int snd_pcm_file_open(snd_pcm_t **pcmp, char *name, char *fname, int fd, char *fmt, snd_pcm_t *slave, int close_slave) { snd_pcm_t *pcm; snd_pcm_file_t *file; - assert(pcmp && slave); + assert(pcmp); if (fname) { fd = open(fname, O_WRONLY|O_CREAT, 0666); if (fd < 0) { @@ -354,8 +353,16 @@ int snd_pcm_file_open(snd_pcm_t **pcmp, char *name, char *fname, int fd, snd_pcm } file = calloc(1, sizeof(snd_pcm_file_t)); if (!file) { + if (fname) + close(fd); return -ENOMEM; } + if (fmt == NULL || + strcmp(fmt, "raw") == 0) + file->format = SND_PCM_FILE_FORMAT_RAW; + else + ERR("file format %s is unknown", fmt); + file->fname = fname; file->fd = fd; file->slave = slave; @@ -393,6 +400,7 @@ int _snd_pcm_file_open(snd_pcm_t **pcmp, char *name, int err; snd_pcm_t *spcm; char *fname = NULL; + char *format = NULL; long fd = -1; snd_config_foreach(i, conf) { snd_config_t *n = snd_config_entry(i); @@ -408,6 +416,12 @@ int _snd_pcm_file_open(snd_pcm_t **pcmp, char *name, return -EINVAL; continue; } + if (strcmp(n->id, "format") == 0) { + err = snd_config_string_get(n, &format); + if (err < 0) + return -EINVAL; + continue; + } if (strcmp(n->id, "file") == 0) { err = snd_config_string_get(n, &fname); if (err < 0) { @@ -434,7 +448,7 @@ int _snd_pcm_file_open(snd_pcm_t **pcmp, char *name, free(sname); if (err < 0) return err; - err = snd_pcm_file_open(pcmp, name, fname, fd, spcm, 1); + err = snd_pcm_file_open(pcmp, name, fname, fd, format, spcm, 1); if (err < 0) snd_pcm_close(spcm); return err; diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index 63eb82bb..f9bf3ba2 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -29,17 +29,24 @@ #include <sys/ioctl.h> #include <sys/mman.h> #include <sys/shm.h> +#include <asm/page.h> #include "pcm_local.h" +#include "../control/control_local.h" #ifndef F_SETSIG #define F_SETSIG 10 #endif +#ifndef PAGE_ALIGN +#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) +#endif + typedef struct { int fd; int card, device, subdevice; volatile snd_pcm_mmap_status_t *mmap_status; snd_pcm_mmap_control_t *mmap_control; + int shmid; } snd_pcm_hw_t; #define SND_FILE_PCM_STREAM_PLAYBACK "/dev/snd/pcmC%iD%ip" @@ -102,7 +109,7 @@ static int snd_pcm_hw_async(snd_pcm_t *pcm, int sig, pid_t pid) return 0; } -static int snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_info_t * info) +static int _snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_info_t * info) { snd_pcm_hw_t *hw = pcm->private; int fd = hw->fd; @@ -113,93 +120,82 @@ static int snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_info_t * info) return 0; } -static int snd_pcm_hw_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info) +static int snd_pcm_hw_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info) { snd_pcm_hw_t *hw = pcm->private; int fd = hw->fd; - if (ioctl(fd, SND_PCM_IOCTL_PARAMS_INFO, info) < 0) { - SYSERR("SND_PCM_IOCTL_PARAMS_INFO failed"); + if (ioctl(fd, SND_PCM_IOCTL_HW_INFO, info) < 0) { + // SYSERR("SND_PCM_IOCTL_HW_INFO failed"); return -errno; } return 0; } -static int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_params_t * params) +static int snd_pcm_hw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) { snd_pcm_hw_t *hw = pcm->private; int fd = hw->fd; - if (ioctl(fd, SND_PCM_IOCTL_PARAMS, params) < 0) { - SYSERR("SND_PCM_IOCTL_PARAMS failed"); + if (ioctl(fd, SND_PCM_IOCTL_HW_PARAMS, params) < 0) { + SYSERR("SND_PCM_IOCTL_HW_PARAMS failed"); return -errno; } return 0; } -static int snd_pcm_hw_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup) +static int snd_pcm_hw_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params) { snd_pcm_hw_t *hw = pcm->private; int fd = hw->fd; - if (ioctl(fd, SND_PCM_IOCTL_SETUP, setup) < 0) { - SYSERR("SND_PCM_IOCTL_SETUP failed"); + if (ioctl(fd, SND_PCM_IOCTL_SW_PARAMS, params) < 0) { + SYSERR("SND_PCM_IOCTL_SW_PARAMS failed"); return -errno; } - if (setup->mmap_shape == SND_PCM_MMAP_UNSPECIFIED) { - if (setup->xfer_mode == SND_PCM_XFER_INTERLEAVED) - setup->mmap_shape = SND_PCM_MMAP_INTERLEAVED; - else - setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED; - } return 0; } -static int snd_pcm_hw_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info) +static int snd_pcm_hw_dig_info(snd_pcm_t *pcm, snd_pcm_dig_info_t * info) { snd_pcm_hw_t *hw = pcm->private; int fd = hw->fd; - if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_INFO, info) < 0) { - SYSERR("SND_PCM_IOCTL_CHANNEL_INFO failed"); + if (ioctl(fd, SND_PCM_IOCTL_DIG_INFO, info) < 0) { + SYSERR("SND_PCM_IOCTL_DIG_INFO failed"); return -errno; } return 0; } -static int snd_pcm_hw_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params) +static int snd_pcm_hw_dig_params(snd_pcm_t *pcm, snd_pcm_dig_params_t * params) { snd_pcm_hw_t *hw = pcm->private; int fd = hw->fd; - if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_PARAMS, params) < 0) { - SYSERR("SND_PCM_IOCTL_CHANNEL_PARAMS failed"); + if (ioctl(fd, SND_PCM_IOCTL_DIG_PARAMS, params) < 0) { + SYSERR("SND_PCM_IOCTL_DIG_PARAMS failed"); return -errno; } return 0; } -static int snd_pcm_hw_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup) +static int snd_pcm_hw_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info) { snd_pcm_hw_t *hw = pcm->private; + snd_pcm_hw_channel_info_t hw_info; int fd = hw->fd; - if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_SETUP, setup) < 0) { - SYSERR("SND_PCM_IOCTL_CHANNEL_SETUP failed"); + hw_info.channel = info->channel; + if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_INFO, &hw_info) < 0) { + SYSERR("SND_PCM_IOCTL_CHANNEL_INFO failed"); return -errno; } - if (!pcm->mmap_info) + info->channel = hw_info.channel; + if (pcm->info & SND_PCM_INFO_MMAP) { + info->addr = 0; + info->first = hw_info.first; + info->step = hw_info.step; + info->type = SND_PCM_AREA_MMAP; + info->u.mmap.fd = fd; + info->u.mmap.offset = hw_info.offset; return 0; - if (pcm->setup.mmap_shape == SND_PCM_MMAP_UNSPECIFIED) { - if (pcm->setup.xfer_mode == SND_PCM_XFER_INTERLEAVED) { - setup->running_area.addr = pcm->mmap_info->addr; - setup->running_area.first = setup->channel * pcm->bits_per_sample; - setup->running_area.step = pcm->bits_per_frame; - } else { - setup->running_area.addr = pcm->mmap_info->addr + setup->channel * pcm->setup.buffer_size * pcm->bits_per_sample / 8; - setup->running_area.first = 0; - setup->running_area.step = pcm->bits_per_sample; - } - setup->stopped_area = setup->running_area; - } else { - setup->running_area.addr = pcm->mmap_info->addr + (long)setup->running_area.addr; - setup->stopped_area.addr = setup->running_area.addr; } - return 0; + return snd_pcm_channel_info_shm(pcm, info, hw->shmid); } static int snd_pcm_hw_status(snd_pcm_t *pcm, snd_pcm_status_t * status) @@ -245,6 +241,8 @@ static int snd_pcm_hw_start(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private; int fd = hw->fd; + assert(pcm->stream != SND_PCM_STREAM_PLAYBACK || + snd_pcm_mmap_playback_hw_avail(pcm) > 0); if (ioctl(fd, SND_PCM_IOCTL_START) < 0) { SYSERR("SND_PCM_IOCTL_START failed"); return -errno; @@ -288,7 +286,7 @@ static int snd_pcm_hw_pause(snd_pcm_t *pcm, int enable) static ssize_t snd_pcm_hw_rewind(snd_pcm_t *pcm, size_t frames) { ssize_t hw_avail; - if (pcm->setup.xrun_mode == SND_PCM_XRUN_ASAP) { + if (pcm->xrun_mode == SND_PCM_XRUN_ASAP) { ssize_t d; int err = snd_pcm_hw_delay(pcm, &d); if (err < 0) @@ -363,7 +361,7 @@ static int snd_pcm_hw_mmap_status(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private; void *ptr; - ptr = mmap(NULL, sizeof(snd_pcm_mmap_status_t), PROT_READ, MAP_FILE|MAP_SHARED, + ptr = mmap(NULL, PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t)), PROT_READ, MAP_FILE|MAP_SHARED, hw->fd, SND_PCM_MMAP_OFFSET_STATUS); if (ptr == MAP_FAILED || ptr == NULL) { SYSERR("status mmap failed"); @@ -378,7 +376,7 @@ static int snd_pcm_hw_mmap_control(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private; void *ptr; - ptr = mmap(NULL, sizeof(snd_pcm_mmap_control_t), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, + ptr = mmap(NULL, PAGE_ALIGN(sizeof(snd_pcm_mmap_control_t)), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, hw->fd, SND_PCM_MMAP_OFFSET_CONTROL); if (ptr == MAP_FAILED || ptr == NULL) { SYSERR("control mmap failed"); @@ -389,31 +387,6 @@ static int snd_pcm_hw_mmap_control(snd_pcm_t *pcm) return 0; } -static int snd_pcm_hw_mmap(snd_pcm_t *pcm) -{ - snd_pcm_hw_t *hw = pcm->private; - snd_pcm_mmap_info_t *i = calloc(1, sizeof(*i)); - int err; - if (!i) - return -ENOMEM; - if (pcm->setup.mmap_shape == SND_PCM_MMAP_UNSPECIFIED) { - err = snd_pcm_alloc_user_mmap(pcm, i); - if (err < 0) { - free(i); - return err; - } - } else { - err = snd_pcm_alloc_kernel_mmap(pcm, i, hw->fd); - if (err < 0) { - free(i); - return err; - } - } - pcm->mmap_info = i; - pcm->mmap_info_count = 1; - return 0; -} - static int snd_pcm_hw_munmap_status(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private; @@ -434,34 +407,50 @@ static int snd_pcm_hw_munmap_control(snd_pcm_t *pcm) return 0; } +static int snd_pcm_hw_mmap(snd_pcm_t *pcm) +{ + snd_pcm_hw_t *hw = pcm->private; + if (!(pcm->info & SND_PCM_INFO_MMAP)) { + size_t size = snd_pcm_frames_to_bytes(pcm, pcm->buffer_size); + int id = shmget(IPC_PRIVATE, size, 0666); + if (id < 0) { + SYSERR("shmget failed"); + return -errno; + } + hw->shmid = id; + } + return 0; +} + static int snd_pcm_hw_munmap(snd_pcm_t *pcm) { - int err = snd_pcm_free_mmap(pcm, pcm->mmap_info); - if (err < 0) - return err; - pcm->mmap_info_count = 0; - free(pcm->mmap_info); - pcm->mmap_info = 0; + snd_pcm_hw_t *hw = pcm->private; + if (!(pcm->info & SND_PCM_INFO_MMAP)) { + if (shmctl(hw->shmid, IPC_RMID, 0) < 0) { + SYSERR("shmctl IPC_RMID failed"); + return -errno; + } + } return 0; } static int snd_pcm_hw_close(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private; - int fd = hw->fd; - free(hw); - if (close(fd)) { + if (close(hw->fd)) { SYSERR("close failed\n"); return -errno; } snd_pcm_hw_munmap_status(pcm); snd_pcm_hw_munmap_control(pcm); + free(hw); return 0; } static ssize_t snd_pcm_hw_mmap_forward(snd_pcm_t *pcm, size_t size) { - if (pcm->setup.mmap_shape == SND_PCM_MMAP_UNSPECIFIED && pcm->stream == SND_PCM_STREAM_PLAYBACK) + if (!(pcm->info & SND_PCM_INFO_MMAP) && + pcm->stream == SND_PCM_STREAM_PLAYBACK) return snd_pcm_write_mmap(pcm, size); snd_pcm_mmap_appl_forward(pcm, size); return size; @@ -471,8 +460,8 @@ static ssize_t snd_pcm_hw_avail_update(snd_pcm_t *pcm) { size_t avail; ssize_t err; - if (pcm->setup.ready_mode == SND_PCM_READY_ASAP || - pcm->setup.xrun_mode == SND_PCM_XRUN_ASAP) { + if (pcm->ready_mode == SND_PCM_READY_ASAP || + pcm->xrun_mode == SND_PCM_XRUN_ASAP) { ssize_t d; int err = snd_pcm_hw_delay(pcm, &d); if (err < 0) @@ -482,7 +471,8 @@ static ssize_t snd_pcm_hw_avail_update(snd_pcm_t *pcm) avail = snd_pcm_mmap_playback_avail(pcm); } else { avail = snd_pcm_mmap_capture_avail(pcm); - if (avail > 0 && pcm->setup.mmap_shape == SND_PCM_MMAP_UNSPECIFIED) { + if (avail > 0 && + !(pcm->info & SND_PCM_INFO_MMAP)) { err = snd_pcm_read_mmap(pcm, avail); if (err < 0) return err; @@ -490,7 +480,7 @@ static ssize_t snd_pcm_hw_avail_update(snd_pcm_t *pcm) return err; } } - if (avail > pcm->setup.buffer_size) + if (avail > pcm->buffer_size) return -EPIPE; return avail; } @@ -510,7 +500,7 @@ static void snd_pcm_hw_dump(snd_pcm_t *pcm, FILE *fp) fprintf(fp, "Hardware PCM card %d '%s' device %d subdevice %d\n", hw->card, name, hw->device, hw->subdevice); free(name); - if (pcm->valid_setup) { + if (pcm->setup) { fprintf(fp, "\nIts setup is:\n"); snd_pcm_dump_setup(pcm, fp); } @@ -518,13 +508,13 @@ static void snd_pcm_hw_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_hw_ops = { close: snd_pcm_hw_close, - info: snd_pcm_hw_info, - params_info: snd_pcm_hw_params_info, - params: snd_pcm_hw_params, - setup: snd_pcm_hw_setup, + info: _snd_pcm_hw_info, + hw_info: snd_pcm_hw_hw_info, + hw_params: snd_pcm_hw_hw_params, + sw_params: snd_pcm_hw_sw_params, + dig_info: snd_pcm_hw_dig_info, + dig_params: snd_pcm_hw_dig_params, channel_info: snd_pcm_hw_channel_info, - channel_params: snd_pcm_hw_channel_params, - channel_setup: snd_pcm_hw_channel_setup, dump: snd_pcm_hw_dump, nonblock: snd_pcm_hw_nonblock, async: snd_pcm_hw_async, diff --git a/src/pcm/pcm_linear.c b/src/pcm/pcm_linear.c index b0cc2393..74ada49c 100644 --- a/src/pcm/pcm_linear.c +++ b/src/pcm/pcm_linear.c @@ -28,8 +28,6 @@ typedef struct { snd_pcm_plugin_t plug; int conv_idx; int sformat; - int cformat; - int cxfer_mode, cmmap_shape; } snd_pcm_linear_t; static void linear_transfer(snd_pcm_channel_area_t *src_areas, size_t src_offset, @@ -74,83 +72,65 @@ static void linear_transfer(snd_pcm_channel_area_t *src_areas, size_t src_offset } } -static int snd_pcm_linear_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info) +static int snd_pcm_linear_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info) { snd_pcm_linear_t *linear = pcm->private; - unsigned int req_mask = info->req_mask; - unsigned int sfmt = info->req.format.sfmt; + unsigned int format_mask, access_mask; int err; - if (req_mask & SND_PCM_PARAMS_SFMT && - !snd_pcm_format_linear(sfmt)) { - info->req.fail_mask = SND_PCM_PARAMS_SFMT; - info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL; + info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | + SND_PCM_ACCBIT_RW_INTERLEAVED | + SND_PCM_ACCBIT_MMAP_NONINTERLEAVED | + SND_PCM_ACCBIT_RW_NONINTERLEAVED); + access_mask = info->access_mask; + if (access_mask == 0) return -EINVAL; + info->format_mask &= SND_PCM_FMTBIT_LINEAR; + format_mask = info->format_mask; + if (format_mask == 0) + return -EINVAL; + + info->format_mask = 1U << linear->sformat; + info->access_mask = SND_PCM_ACCBIT_MMAP; + err = snd_pcm_hw_info(linear->plug.slave, info); + if (info->format_mask) + info->format_mask = format_mask; + if (info->access_mask) { + linear->plug.saccess_mask = info->access_mask; + info->access_mask = access_mask; } - info->req_mask |= SND_PCM_PARAMS_SFMT; - info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE | - SND_PCM_PARAMS_XFER_MODE); - info->req.format.sfmt = linear->sformat; - err = snd_pcm_params_info(linear->plug.slave, info); - info->req_mask = req_mask; - info->req.format.sfmt = sfmt; if (err < 0) return err; - if (req_mask & SND_PCM_PARAMS_SFMT) - info->formats = 1 << sfmt; - else - info->formats = SND_PCM_LINEAR_FORMATS; - info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); - info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED; - return err; + info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); + snd_pcm_hw_info_complete(info); + return 0; } -static int snd_pcm_linear_params(snd_pcm_t *pcm, snd_pcm_params_t * params) +static int snd_pcm_linear_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { snd_pcm_linear_t *linear = pcm->private; snd_pcm_t *slave = linear->plug.slave; + unsigned int format, access; int err; - if (!snd_pcm_format_linear(params->format.sfmt)) { - params->fail_mask = SND_PCM_PARAMS_SFMT; - params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL; - return -EINVAL; - } - linear->cformat = params->format.sfmt; - linear->cxfer_mode = params->xfer_mode; - linear->cmmap_shape = params->mmap_shape; - params->format.sfmt = linear->sformat; - params->xfer_mode = SND_PCM_XFER_UNSPECIFIED; - params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED; - err = snd_pcm_params_mmap(slave, params); - params->format.sfmt = linear->cformat; - params->xfer_mode = linear->cxfer_mode; - params->mmap_shape = linear->cmmap_shape; - return err; -} - -static int snd_pcm_linear_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup) -{ - snd_pcm_linear_t *linear = pcm->private; - int err = snd_pcm_setup(linear->plug.slave, setup); + format = params->format; + access = params->access; + params->format = linear->sformat; + if (linear->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) + params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED; + else if (linear->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED) + params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; + else + assert(0); + err = snd_pcm_hw_params(slave, params); + params->format = format; + params->access = access; if (err < 0) return err; - assert(linear->sformat == setup->format.sfmt); - - if (linear->cxfer_mode == SND_PCM_XFER_UNSPECIFIED) - setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED; - else - setup->xfer_mode = linear->cxfer_mode; - if (linear->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED) - setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED; - else - setup->mmap_shape = linear->cmmap_shape; - setup->format.sfmt = linear->cformat; - setup->mmap_bytes = 0; if (pcm->stream == SND_PCM_STREAM_PLAYBACK) - linear->conv_idx = conv_index(linear->cformat, + linear->conv_idx = conv_index(format, linear->sformat); else linear->conv_idx = conv_index(linear->sformat, - linear->cformat); + format); return 0; } @@ -171,7 +151,7 @@ static ssize_t snd_pcm_linear_write_areas(snd_pcm_t *pcm, size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer); linear_transfer(areas, offset, snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave), - frames, pcm->setup.format.channels, linear->conv_idx); + frames, pcm->channels, linear->conv_idx); err = snd_pcm_mmap_forward(slave, frames); if (err < 0) break; @@ -205,7 +185,7 @@ static ssize_t snd_pcm_linear_read_areas(snd_pcm_t *pcm, size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer); linear_transfer(snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave), areas, offset, - frames, pcm->setup.format.channels, linear->conv_idx); + frames, pcm->channels, linear->conv_idx); err = snd_pcm_mmap_forward(slave, frames); if (err < 0) break; @@ -227,7 +207,7 @@ static void snd_pcm_linear_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_linear_t *linear = pcm->private; fprintf(fp, "Linear conversion PCM (%s)\n", snd_pcm_format_name(linear->sformat)); - if (pcm->valid_setup) { + if (pcm->setup) { fprintf(fp, "Its setup is:\n"); snd_pcm_dump_setup(pcm, fp); } @@ -238,12 +218,12 @@ static void snd_pcm_linear_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_linear_ops = { close: snd_pcm_plugin_close, info: snd_pcm_plugin_info, - params_info: snd_pcm_linear_params_info, - params: snd_pcm_linear_params, - setup: snd_pcm_linear_setup, + hw_info: snd_pcm_linear_hw_info, + hw_params: snd_pcm_linear_hw_params, + sw_params: snd_pcm_plugin_sw_params, + dig_info: snd_pcm_plugin_dig_info, + dig_params: snd_pcm_plugin_dig_params, channel_info: snd_pcm_plugin_channel_info, - channel_params: snd_pcm_plugin_channel_params, - channel_setup: snd_pcm_plugin_channel_setup, dump: snd_pcm_linear_dump, nonblock: snd_pcm_plugin_nonblock, async: snd_pcm_plugin_async, diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index 8d70ace1..d3b4bb10 100644 --- a/src/pcm/pcm_local.h +++ b/src/pcm/pcm_local.h @@ -22,29 +22,48 @@ #include <assert.h> #include <stdio.h> #include <stdlib.h> +#include <limits.h> #include <sys/uio.h> #include <errno.h> #include "asoundlib.h" #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) -#define ERR(...) snd_pcm_error(__FILE__, __LINE__, __FUNCTION__, 0, __VA_ARGS__) -#define SYSERR(...) snd_pcm_error(__FILE__, __LINE__, __FUNCTION__, errno, __VA_ARGS__) +#define ERR(...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, 0, __VA_ARGS__) +#define SYSERR(...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, errno, __VA_ARGS__) #else -#define ERR(args...) snd_pcm_error(__FILE__, __LINE__, __FUNCTION__, 0, ##args) -#define SYSERR(args...) snd_pcm_error(__FILE__, __LINE__, __FUNCTION__, errno, ##args) +#define ERR(args...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, 0, ##args) +#define SYSERR(args...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, errno, ##args) #endif +typedef struct _snd_pcm_channel_info { + unsigned int channel; + void *addr; /* base address of channel samples */ + unsigned int first; /* offset to first sample in bits */ + unsigned int step; /* samples distance in bits */ + enum { SND_PCM_AREA_SHM, SND_PCM_AREA_MMAP } type; + union { + struct { + int shmid; + } shm; + struct { + int fd; + off_t offset; + } mmap; + } u; + char reserved[64]; +} snd_pcm_channel_info_t; + typedef struct { int (*close)(snd_pcm_t *pcm); int (*nonblock)(snd_pcm_t *pcm, int nonblock); int (*async)(snd_pcm_t *pcm, int sig, pid_t pid); int (*info)(snd_pcm_t *pcm, snd_pcm_info_t *info); - int (*params_info)(snd_pcm_t *pcm, snd_pcm_params_info_t *info); - int (*params)(snd_pcm_t *pcm, snd_pcm_params_t *params); - int (*setup)(snd_pcm_t *pcm, snd_pcm_setup_t *setup); + int (*hw_info)(snd_pcm_t *pcm, snd_pcm_hw_info_t *info); + int (*hw_params)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); + int (*sw_params)(snd_pcm_t *pcm, snd_pcm_sw_params_t *params); + int (*dig_info)(snd_pcm_t *pcm, snd_pcm_dig_info_t *info); + int (*dig_params)(snd_pcm_t *pcm, snd_pcm_dig_params_t *params); int (*channel_info)(snd_pcm_t *pcm, snd_pcm_channel_info_t *info); - int (*channel_params)(snd_pcm_t *pcm, snd_pcm_channel_params_t *params); - int (*channel_setup)(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup); void (*dump)(snd_pcm_t *pcm, FILE *fp); int (*mmap)(snd_pcm_t *pcm); int (*munmap)(snd_pcm_t *pcm); @@ -69,38 +88,43 @@ typedef struct { int (*set_avail_min)(snd_pcm_t *pcm, size_t frames); } snd_pcm_fast_ops_t; -typedef struct { - unsigned int index; - enum { SND_PCM_MMAP_KERNEL, SND_PCM_MMAP_USER } type; - void *addr; - size_t size; - union { - struct { - int shmid; - } user; - struct { - int fd; - } kernel; - } u; -} snd_pcm_mmap_info_t; - -struct snd_pcm { +struct _snd_pcm { char *name; snd_pcm_type_t type; int stream; int mode; int poll_fd; - int valid_setup; - snd_pcm_setup_t setup; + int setup; + unsigned int access; /* access mode */ + unsigned int format; /* SND_PCM_FORMAT_* */ + unsigned int subformat; /* subformat */ + unsigned int rate; /* rate in Hz */ + unsigned int channels; /* channels */ + size_t fragment_size; /* fragment size */ + unsigned int fragments; /* fragments */ + unsigned int start_mode; /* start mode */ + unsigned int ready_mode; /* ready detection mode */ + unsigned int xrun_mode; /* xrun detection mode */ + size_t avail_min; /* min avail frames for wakeup */ + size_t xfer_min; /* xfer min size */ + size_t xfer_align; /* xfer size need to be a multiple */ + unsigned int time: 1; /* timestamp switch */ + size_t boundary; /* pointers wrap point */ + unsigned int info; /* Info for returned setup */ + unsigned int msbits; /* used most significant bits */ + unsigned int rate_master; /* Exact rate is rate_master / */ + unsigned int rate_divisor; /* rate_divisor */ + size_t fifo_size; /* chip FIFO size in frames */ + size_t buffer_size; size_t bits_per_sample; size_t bits_per_frame; size_t *appl_ptr; volatile size_t *hw_ptr; - int mmap_auto; - size_t mmap_info_count; - snd_pcm_mmap_info_t *mmap_info; + int mmap_rw; + snd_pcm_channel_info_t *mmap_channels; snd_pcm_channel_area_t *running_areas; snd_pcm_channel_area_t *stopped_areas; + void *stopped; snd_pcm_ops_t *ops; snd_pcm_fast_ops_t *fast_ops; snd_pcm_t *op_arg; @@ -108,10 +132,18 @@ struct snd_pcm { void *private; }; +int snd_pcm_hw_open(snd_pcm_t **pcm, char *name, int card, int device, int subdevice, int stream, int mode); +int snd_pcm_plug_open_hw(snd_pcm_t **pcm, char *name, int card, int device, int subdevice, int stream, int mode); +int snd_pcm_shm_open(snd_pcm_t **pcmp, char *name, char *socket, char *sname, int stream, int mode); +int snd_pcm_file_open(snd_pcm_t **pcmp, char *name, char *fname, int fd, char *fmt, snd_pcm_t *slave, int close_slave); +int snd_pcm_null_open(snd_pcm_t **pcmp, char *name, int stream, int mode); + + void snd_pcm_areas_from_buf(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, void *buf); void snd_pcm_areas_from_bufs(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, void **bufs); -int snd_pcm_params_mmap(snd_pcm_t *pcm, snd_pcm_params_t *params); +int snd_pcm_mmap(snd_pcm_t *pcm); +int snd_pcm_munmap(snd_pcm_t *pcm); int snd_pcm_mmap_ready(snd_pcm_t *pcm); ssize_t snd_pcm_mmap_appl_ptr(snd_pcm_t *pcm, off_t offset); void snd_pcm_mmap_appl_backward(snd_pcm_t *pcm, size_t frames); @@ -135,16 +167,19 @@ ssize_t snd_pcm_write_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, snd_pcm_xfer_areas_func_t func); ssize_t snd_pcm_read_mmap(snd_pcm_t *pcm, size_t size); ssize_t snd_pcm_write_mmap(snd_pcm_t *pcm, size_t size); -int snd_pcm_alloc_user_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i); -int snd_pcm_alloc_kernel_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i, int fd); -int snd_pcm_free_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i); +int snd_pcm_hw_info_complete(snd_pcm_hw_info_t *info); +void snd_pcm_hw_params_to_info(snd_pcm_hw_params_t *params, snd_pcm_hw_info_t *info); +void snd_pcm_hw_info_to_params(snd_pcm_hw_info_t *info, snd_pcm_hw_params_t *params); +void snd_pcm_hw_info_to_params_fail(snd_pcm_hw_info_t *info, snd_pcm_hw_params_t *params); +int snd_pcm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info); +int snd_pcm_channel_info_shm(snd_pcm_t *pcm, snd_pcm_channel_info_t *info, int shmid); static inline size_t snd_pcm_mmap_playback_avail(snd_pcm_t *pcm) { ssize_t avail; - avail = *pcm->hw_ptr + pcm->setup.buffer_size - *pcm->appl_ptr; + avail = *pcm->hw_ptr + pcm->buffer_size - *pcm->appl_ptr; if (avail < 0) - avail += pcm->setup.boundary; + avail += pcm->boundary; return avail; } @@ -153,7 +188,7 @@ static inline size_t snd_pcm_mmap_capture_avail(snd_pcm_t *pcm) ssize_t avail; avail = *pcm->hw_ptr - *pcm->appl_ptr; if (avail < 0) - avail += pcm->setup.boundary; + avail += pcm->boundary; return avail; } @@ -162,19 +197,19 @@ static inline size_t snd_pcm_mmap_avail(snd_pcm_t *pcm) ssize_t avail; avail = *pcm->hw_ptr - *pcm->appl_ptr; if (pcm->stream == SND_PCM_STREAM_PLAYBACK) - avail += pcm->setup.buffer_size; + avail += pcm->buffer_size; if (avail < 0) - avail += pcm->setup.boundary; + avail += pcm->boundary; return avail; } static inline ssize_t snd_pcm_mmap_playback_hw_avail(snd_pcm_t *pcm) { ssize_t avail; - avail = *pcm->hw_ptr + pcm->setup.buffer_size - *pcm->appl_ptr; + avail = *pcm->hw_ptr + pcm->buffer_size - *pcm->appl_ptr; if (avail < 0) - avail += pcm->setup.boundary; - return pcm->setup.buffer_size - avail; + avail += pcm->boundary; + return pcm->buffer_size - avail; } static inline ssize_t snd_pcm_mmap_capture_hw_avail(snd_pcm_t *pcm) @@ -182,8 +217,8 @@ static inline ssize_t snd_pcm_mmap_capture_hw_avail(snd_pcm_t *pcm) ssize_t avail; avail = *pcm->hw_ptr - *pcm->appl_ptr; if (avail < 0) - avail += pcm->setup.boundary; - return pcm->setup.buffer_size - avail; + avail += pcm->boundary; + return pcm->buffer_size - avail; } static inline ssize_t snd_pcm_mmap_hw_avail(snd_pcm_t *pcm) @@ -191,10 +226,10 @@ static inline ssize_t snd_pcm_mmap_hw_avail(snd_pcm_t *pcm) ssize_t avail; avail = *pcm->hw_ptr - *pcm->appl_ptr; if (pcm->stream == SND_PCM_STREAM_PLAYBACK) - avail += pcm->setup.buffer_size; + avail += pcm->buffer_size; if (avail < 0) - avail += pcm->setup.boundary; - return pcm->setup.buffer_size - avail; + avail += pcm->boundary; + return pcm->buffer_size - avail; } #define snd_pcm_mmap_playback_delay snd_pcm_mmap_playback_hw_avail diff --git a/src/pcm/pcm_misc.c b/src/pcm/pcm_misc.c index 57bfcf21..a6790215 100644 --- a/src/pcm/pcm_misc.c +++ b/src/pcm/pcm_misc.c @@ -39,21 +39,21 @@ int snd_pcm_format_signed(int format) { switch (format) { - case SND_PCM_SFMT_S8: - case SND_PCM_SFMT_S16_LE: - case SND_PCM_SFMT_S16_BE: - case SND_PCM_SFMT_S24_LE: - case SND_PCM_SFMT_S24_BE: - case SND_PCM_SFMT_S32_LE: - case SND_PCM_SFMT_S32_BE: + case SND_PCM_FORMAT_S8: + case SND_PCM_FORMAT_S16_LE: + case SND_PCM_FORMAT_S16_BE: + case SND_PCM_FORMAT_S24_LE: + case SND_PCM_FORMAT_S24_BE: + case SND_PCM_FORMAT_S32_LE: + case SND_PCM_FORMAT_S32_BE: return 1; - case SND_PCM_SFMT_U8: - case SND_PCM_SFMT_U16_LE: - case SND_PCM_SFMT_U16_BE: - case SND_PCM_SFMT_U24_LE: - case SND_PCM_SFMT_U24_BE: - case SND_PCM_SFMT_U32_LE: - case SND_PCM_SFMT_U32_BE: + case SND_PCM_FORMAT_U8: + case SND_PCM_FORMAT_U16_LE: + case SND_PCM_FORMAT_U16_BE: + case SND_PCM_FORMAT_U24_LE: + case SND_PCM_FORMAT_U24_BE: + case SND_PCM_FORMAT_U32_LE: + case SND_PCM_FORMAT_U32_BE: return 0; default: return -EINVAL; @@ -78,25 +78,25 @@ int snd_pcm_format_linear(int format) int snd_pcm_format_little_endian(int format) { switch (format) { - case SND_PCM_SFMT_S16_LE: - case SND_PCM_SFMT_U16_LE: - case SND_PCM_SFMT_S24_LE: - case SND_PCM_SFMT_U24_LE: - case SND_PCM_SFMT_S32_LE: - case SND_PCM_SFMT_U32_LE: - case SND_PCM_SFMT_FLOAT_LE: - case SND_PCM_SFMT_FLOAT64_LE: - case SND_PCM_SFMT_IEC958_SUBFRAME_LE: + case SND_PCM_FORMAT_S16_LE: + case SND_PCM_FORMAT_U16_LE: + case SND_PCM_FORMAT_S24_LE: + case SND_PCM_FORMAT_U24_LE: + case SND_PCM_FORMAT_S32_LE: + case SND_PCM_FORMAT_U32_LE: + case SND_PCM_FORMAT_FLOAT_LE: + case SND_PCM_FORMAT_FLOAT64_LE: + case SND_PCM_FORMAT_IEC958_SUBFRAME_LE: return 1; - case SND_PCM_SFMT_S16_BE: - case SND_PCM_SFMT_U16_BE: - case SND_PCM_SFMT_S24_BE: - case SND_PCM_SFMT_U24_BE: - case SND_PCM_SFMT_S32_BE: - case SND_PCM_SFMT_U32_BE: - case SND_PCM_SFMT_FLOAT_BE: - case SND_PCM_SFMT_FLOAT64_BE: - case SND_PCM_SFMT_IEC958_SUBFRAME_BE: + case SND_PCM_FORMAT_S16_BE: + case SND_PCM_FORMAT_U16_BE: + case SND_PCM_FORMAT_S24_BE: + case SND_PCM_FORMAT_U24_BE: + case SND_PCM_FORMAT_S32_BE: + case SND_PCM_FORMAT_U32_BE: + case SND_PCM_FORMAT_FLOAT_BE: + case SND_PCM_FORMAT_FLOAT64_BE: + case SND_PCM_FORMAT_IEC958_SUBFRAME_BE: return 0; default: return -EINVAL; @@ -116,36 +116,36 @@ int snd_pcm_format_big_endian(int format) int snd_pcm_format_width(int format) { switch (format) { - case SND_PCM_SFMT_S8: - case SND_PCM_SFMT_U8: + case SND_PCM_FORMAT_S8: + case SND_PCM_FORMAT_U8: return 8; - case SND_PCM_SFMT_S16_LE: - case SND_PCM_SFMT_S16_BE: - case SND_PCM_SFMT_U16_LE: - case SND_PCM_SFMT_U16_BE: + case SND_PCM_FORMAT_S16_LE: + case SND_PCM_FORMAT_S16_BE: + case SND_PCM_FORMAT_U16_LE: + case SND_PCM_FORMAT_U16_BE: return 16; - case SND_PCM_SFMT_S24_LE: - case SND_PCM_SFMT_S24_BE: - case SND_PCM_SFMT_U24_LE: - case SND_PCM_SFMT_U24_BE: + case SND_PCM_FORMAT_S24_LE: + case SND_PCM_FORMAT_S24_BE: + case SND_PCM_FORMAT_U24_LE: + case SND_PCM_FORMAT_U24_BE: return 24; - case SND_PCM_SFMT_S32_LE: - case SND_PCM_SFMT_S32_BE: - case SND_PCM_SFMT_U32_LE: - case SND_PCM_SFMT_U32_BE: - case SND_PCM_SFMT_FLOAT_LE: - case SND_PCM_SFMT_FLOAT_BE: + case SND_PCM_FORMAT_S32_LE: + case SND_PCM_FORMAT_S32_BE: + case SND_PCM_FORMAT_U32_LE: + case SND_PCM_FORMAT_U32_BE: + case SND_PCM_FORMAT_FLOAT_LE: + case SND_PCM_FORMAT_FLOAT_BE: return 32; - case SND_PCM_SFMT_FLOAT64_LE: - case SND_PCM_SFMT_FLOAT64_BE: + case SND_PCM_FORMAT_FLOAT64_LE: + case SND_PCM_FORMAT_FLOAT64_BE: return 64; - case SND_PCM_SFMT_IEC958_SUBFRAME_LE: - case SND_PCM_SFMT_IEC958_SUBFRAME_BE: + case SND_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SND_PCM_FORMAT_IEC958_SUBFRAME_BE: return 24; - case SND_PCM_SFMT_MU_LAW: - case SND_PCM_SFMT_A_LAW: + case SND_PCM_FORMAT_MU_LAW: + case SND_PCM_FORMAT_A_LAW: return 8; - case SND_PCM_SFMT_IMA_ADPCM: + case SND_PCM_FORMAT_IMA_ADPCM: return 4; default: return -EINVAL; @@ -155,34 +155,34 @@ int snd_pcm_format_width(int format) int snd_pcm_format_physical_width(int format) { switch (format) { - case SND_PCM_SFMT_S8: - case SND_PCM_SFMT_U8: + case SND_PCM_FORMAT_S8: + case SND_PCM_FORMAT_U8: return 8; - case SND_PCM_SFMT_S16_LE: - case SND_PCM_SFMT_S16_BE: - case SND_PCM_SFMT_U16_LE: - case SND_PCM_SFMT_U16_BE: + case SND_PCM_FORMAT_S16_LE: + case SND_PCM_FORMAT_S16_BE: + case SND_PCM_FORMAT_U16_LE: + case SND_PCM_FORMAT_U16_BE: return 16; - case SND_PCM_SFMT_S24_LE: - case SND_PCM_SFMT_S24_BE: - case SND_PCM_SFMT_U24_LE: - case SND_PCM_SFMT_U24_BE: - case SND_PCM_SFMT_S32_LE: - case SND_PCM_SFMT_S32_BE: - case SND_PCM_SFMT_U32_LE: - case SND_PCM_SFMT_U32_BE: - case SND_PCM_SFMT_FLOAT_LE: - case SND_PCM_SFMT_FLOAT_BE: - case SND_PCM_SFMT_IEC958_SUBFRAME_LE: - case SND_PCM_SFMT_IEC958_SUBFRAME_BE: + case SND_PCM_FORMAT_S24_LE: + case SND_PCM_FORMAT_S24_BE: + case SND_PCM_FORMAT_U24_LE: + case SND_PCM_FORMAT_U24_BE: + case SND_PCM_FORMAT_S32_LE: + case SND_PCM_FORMAT_S32_BE: + case SND_PCM_FORMAT_U32_LE: + case SND_PCM_FORMAT_U32_BE: + case SND_PCM_FORMAT_FLOAT_LE: + case SND_PCM_FORMAT_FLOAT_BE: + case SND_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SND_PCM_FORMAT_IEC958_SUBFRAME_BE: return 32; - case SND_PCM_SFMT_FLOAT64_LE: - case SND_PCM_SFMT_FLOAT64_BE: + case SND_PCM_FORMAT_FLOAT64_LE: + case SND_PCM_FORMAT_FLOAT64_BE: return 64; - case SND_PCM_SFMT_MU_LAW: - case SND_PCM_SFMT_A_LAW: + case SND_PCM_FORMAT_MU_LAW: + case SND_PCM_FORMAT_A_LAW: return 8; - case SND_PCM_SFMT_IMA_ADPCM: + case SND_PCM_FORMAT_IMA_ADPCM: return 4; default: return -EINVAL; @@ -192,35 +192,35 @@ int snd_pcm_format_physical_width(int format) ssize_t snd_pcm_format_size(int format, size_t samples) { switch (format) { - case SND_PCM_SFMT_S8: - case SND_PCM_SFMT_U8: + case SND_PCM_FORMAT_S8: + case SND_PCM_FORMAT_U8: return samples; - case SND_PCM_SFMT_S16_LE: - case SND_PCM_SFMT_S16_BE: - case SND_PCM_SFMT_U16_LE: - case SND_PCM_SFMT_U16_BE: + case SND_PCM_FORMAT_S16_LE: + case SND_PCM_FORMAT_S16_BE: + case SND_PCM_FORMAT_U16_LE: + case SND_PCM_FORMAT_U16_BE: return samples * 2; - case SND_PCM_SFMT_S24_LE: - case SND_PCM_SFMT_S24_BE: - case SND_PCM_SFMT_U24_LE: - case SND_PCM_SFMT_U24_BE: - case SND_PCM_SFMT_S32_LE: - case SND_PCM_SFMT_S32_BE: - case SND_PCM_SFMT_U32_LE: - case SND_PCM_SFMT_U32_BE: - case SND_PCM_SFMT_FLOAT_LE: - case SND_PCM_SFMT_FLOAT_BE: + case SND_PCM_FORMAT_S24_LE: + case SND_PCM_FORMAT_S24_BE: + case SND_PCM_FORMAT_U24_LE: + case SND_PCM_FORMAT_U24_BE: + case SND_PCM_FORMAT_S32_LE: + case SND_PCM_FORMAT_S32_BE: + case SND_PCM_FORMAT_U32_LE: + case SND_PCM_FORMAT_U32_BE: + case SND_PCM_FORMAT_FLOAT_LE: + case SND_PCM_FORMAT_FLOAT_BE: return samples * 4; - case SND_PCM_SFMT_FLOAT64_LE: - case SND_PCM_SFMT_FLOAT64_BE: + case SND_PCM_FORMAT_FLOAT64_LE: + case SND_PCM_FORMAT_FLOAT64_BE: return samples * 8; - case SND_PCM_SFMT_IEC958_SUBFRAME_LE: - case SND_PCM_SFMT_IEC958_SUBFRAME_BE: + case SND_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SND_PCM_FORMAT_IEC958_SUBFRAME_BE: return samples * 4; - case SND_PCM_SFMT_MU_LAW: - case SND_PCM_SFMT_A_LAW: + case SND_PCM_FORMAT_MU_LAW: + case SND_PCM_FORMAT_A_LAW: return samples; - case SND_PCM_SFMT_IMA_ADPCM: + case SND_PCM_FORMAT_IMA_ADPCM: if (samples & 1) return -EINVAL; return samples / 2; @@ -232,44 +232,44 @@ ssize_t snd_pcm_format_size(int format, size_t samples) u_int64_t snd_pcm_format_silence_64(int format) { switch (format) { - case SND_PCM_SFMT_S8: - case SND_PCM_SFMT_S16_LE: - case SND_PCM_SFMT_S16_BE: - case SND_PCM_SFMT_S24_LE: - case SND_PCM_SFMT_S24_BE: - case SND_PCM_SFMT_S32_LE: - case SND_PCM_SFMT_S32_BE: + case SND_PCM_FORMAT_S8: + case SND_PCM_FORMAT_S16_LE: + case SND_PCM_FORMAT_S16_BE: + case SND_PCM_FORMAT_S24_LE: + case SND_PCM_FORMAT_S24_BE: + case SND_PCM_FORMAT_S32_LE: + case SND_PCM_FORMAT_S32_BE: return 0; - case SND_PCM_SFMT_U8: + case SND_PCM_FORMAT_U8: return 0x8080808080808080ULL; #ifdef SND_LITTLE_ENDIAN - case SND_PCM_SFMT_U16_LE: + case SND_PCM_FORMAT_U16_LE: return 0x8000800080008000ULL; - case SND_PCM_SFMT_U24_LE: + case SND_PCM_FORMAT_U24_LE: return 0x0080000000800000ULL; - case SND_PCM_SFMT_U32_LE: + case SND_PCM_FORMAT_U32_LE: return 0x8000000080000000ULL; - case SND_PCM_SFMT_U16_BE: + case SND_PCM_FORMAT_U16_BE: return 0x0080008000800080ULL; - case SND_PCM_SFMT_U24_BE: + case SND_PCM_FORMAT_U24_BE: return 0x0000800000008000ULL; - case SND_PCM_SFMT_U32_BE: + case SND_PCM_FORMAT_U32_BE: return 0x0000008000000080ULL; #else - case SND_PCM_SFMT_U16_LE: + case SND_PCM_FORMAT_U16_LE: return 0x0080008000800080ULL; - case SND_PCM_SFMT_U24_LE: + case SND_PCM_FORMAT_U24_LE: return 0x0000800000008000ULL; - case SND_PCM_SFMT_U32_LE: + case SND_PCM_FORMAT_U32_LE: return 0x0000008000000080ULL; - case SND_PCM_SFMT_U16_BE: + case SND_PCM_FORMAT_U16_BE: return 0x8000800080008000ULL; - case SND_PCM_SFMT_U24_BE: + case SND_PCM_FORMAT_U24_BE: return 0x0080000000800000ULL; - case SND_PCM_SFMT_U32_BE: + case SND_PCM_FORMAT_U32_BE: return 0x8000000080000000ULL; #endif - case SND_PCM_SFMT_FLOAT_LE: + case SND_PCM_FORMAT_FLOAT_LE: { union { float f; @@ -282,7 +282,7 @@ u_int64_t snd_pcm_format_silence_64(int format) return bswap_32(u.i); #endif } - case SND_PCM_SFMT_FLOAT64_LE: + case SND_PCM_FORMAT_FLOAT64_LE: { union { double f; @@ -295,7 +295,7 @@ u_int64_t snd_pcm_format_silence_64(int format) return bswap_64(u.i); #endif } - case SND_PCM_SFMT_FLOAT_BE: + case SND_PCM_FORMAT_FLOAT_BE: { union { float f; @@ -308,7 +308,7 @@ u_int64_t snd_pcm_format_silence_64(int format) return u.i; #endif } - case SND_PCM_SFMT_FLOAT64_BE: + case SND_PCM_FORMAT_FLOAT64_BE: { union { double f; @@ -321,16 +321,16 @@ u_int64_t snd_pcm_format_silence_64(int format) return u.i; #endif } - case SND_PCM_SFMT_IEC958_SUBFRAME_LE: - case SND_PCM_SFMT_IEC958_SUBFRAME_BE: + case SND_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SND_PCM_FORMAT_IEC958_SUBFRAME_BE: return 0; - case SND_PCM_SFMT_MU_LAW: + case SND_PCM_FORMAT_MU_LAW: return 0x7f7f7f7f7f7f7f7fULL; - case SND_PCM_SFMT_A_LAW: + case SND_PCM_FORMAT_A_LAW: return 0x5555555555555555ULL; - case SND_PCM_SFMT_IMA_ADPCM: /* special case */ - case SND_PCM_SFMT_MPEG: - case SND_PCM_SFMT_GSM: + case SND_PCM_FORMAT_IMA_ADPCM: /* special case */ + case SND_PCM_FORMAT_MPEG: + case SND_PCM_FORMAT_GSM: return 0; } return 0; @@ -395,22 +395,22 @@ ssize_t snd_pcm_format_set_silence(int format, void *data, size_t samples) } static int linear_formats[4*2*2] = { - SND_PCM_SFMT_S8, - SND_PCM_SFMT_U8, - SND_PCM_SFMT_S8, - SND_PCM_SFMT_U8, - SND_PCM_SFMT_S16_LE, - SND_PCM_SFMT_S16_BE, - SND_PCM_SFMT_U16_LE, - SND_PCM_SFMT_U16_BE, - SND_PCM_SFMT_S24_LE, - SND_PCM_SFMT_S24_BE, - SND_PCM_SFMT_U24_LE, - SND_PCM_SFMT_U24_BE, - SND_PCM_SFMT_S32_LE, - SND_PCM_SFMT_S32_BE, - SND_PCM_SFMT_U32_LE, - SND_PCM_SFMT_U32_BE + SND_PCM_FORMAT_S8, + SND_PCM_FORMAT_U8, + SND_PCM_FORMAT_S8, + SND_PCM_FORMAT_U8, + SND_PCM_FORMAT_S16_LE, + SND_PCM_FORMAT_S16_BE, + SND_PCM_FORMAT_U16_LE, + SND_PCM_FORMAT_U16_BE, + SND_PCM_FORMAT_S24_LE, + SND_PCM_FORMAT_S24_BE, + SND_PCM_FORMAT_U24_LE, + SND_PCM_FORMAT_U24_BE, + SND_PCM_FORMAT_S32_LE, + SND_PCM_FORMAT_S32_BE, + SND_PCM_FORMAT_U32_LE, + SND_PCM_FORMAT_U32_BE }; int snd_pcm_build_linear_format(int width, int unsignd, int big_endian) diff --git a/src/pcm/pcm_mmap.c b/src/pcm/pcm_mmap.c index 4eb51d4f..1a754c72 100644 --- a/src/pcm/pcm_mmap.c +++ b/src/pcm/pcm_mmap.c @@ -23,15 +23,23 @@ #include <string.h> #include <errno.h> #include <sys/poll.h> +#include <sys/mman.h> +#include <sys/shm.h> +#include <asm/page.h> #include "pcm_local.h" + +#ifndef PAGE_ALIGN +#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) +#endif + + snd_pcm_channel_area_t *snd_pcm_mmap_areas(snd_pcm_t *pcm) { - int state = snd_pcm_state(pcm); - if (state == SND_PCM_STATE_RUNNING) - return pcm->running_areas; - else - return pcm->stopped_areas; + if (pcm->stopped_areas && + snd_pcm_state(pcm) != SND_PCM_STATE_RUNNING) + return pcm->stopped_areas; + return pcm->running_areas; } size_t snd_pcm_mmap_playback_xfer(snd_pcm_t *pcm, size_t frames) @@ -40,7 +48,7 @@ size_t snd_pcm_mmap_playback_xfer(snd_pcm_t *pcm, size_t frames) size_t avail = snd_pcm_mmap_playback_avail(pcm); if (avail < frames) frames = avail; - cont = pcm->setup.buffer_size - *pcm->appl_ptr % pcm->setup.buffer_size; + cont = pcm->buffer_size - *pcm->appl_ptr % pcm->buffer_size; if (cont < frames) frames = cont; return frames; @@ -52,7 +60,7 @@ size_t snd_pcm_mmap_capture_xfer(snd_pcm_t *pcm, size_t frames) size_t avail = snd_pcm_mmap_capture_avail(pcm); if (avail < frames) frames = avail; - cont = pcm->setup.buffer_size - *pcm->appl_ptr % pcm->setup.buffer_size; + cont = pcm->buffer_size - *pcm->appl_ptr % pcm->buffer_size; if (cont < frames) frames = cont; return frames; @@ -70,13 +78,13 @@ size_t snd_pcm_mmap_xfer(snd_pcm_t *pcm, size_t frames) size_t snd_pcm_mmap_offset(snd_pcm_t *pcm) { assert(pcm); - return *pcm->appl_ptr % pcm->setup.buffer_size; + return *pcm->appl_ptr % pcm->buffer_size; } size_t snd_pcm_mmap_hw_offset(snd_pcm_t *pcm) { assert(pcm); - return *pcm->hw_ptr % pcm->setup.buffer_size; + return *pcm->hw_ptr % pcm->buffer_size; } void snd_pcm_mmap_appl_backward(snd_pcm_t *pcm, size_t frames) @@ -84,7 +92,7 @@ void snd_pcm_mmap_appl_backward(snd_pcm_t *pcm, size_t frames) ssize_t appl_ptr = *pcm->appl_ptr; appl_ptr -= frames; if (appl_ptr < 0) - appl_ptr += pcm->setup.boundary; + appl_ptr += pcm->boundary; *pcm->appl_ptr = appl_ptr; } @@ -92,8 +100,8 @@ void snd_pcm_mmap_appl_forward(snd_pcm_t *pcm, size_t frames) { size_t appl_ptr = *pcm->appl_ptr; appl_ptr += frames; - if (appl_ptr >= pcm->setup.boundary) - appl_ptr -= pcm->setup.boundary; + if (appl_ptr >= pcm->boundary) + appl_ptr -= pcm->boundary; *pcm->appl_ptr = appl_ptr; } @@ -102,7 +110,7 @@ void snd_pcm_mmap_hw_backward(snd_pcm_t *pcm, size_t frames) ssize_t hw_ptr = *pcm->hw_ptr; hw_ptr -= frames; if (hw_ptr < 0) - hw_ptr += pcm->setup.boundary; + hw_ptr += pcm->boundary; *pcm->hw_ptr = hw_ptr; } @@ -110,8 +118,8 @@ void snd_pcm_mmap_hw_forward(snd_pcm_t *pcm, size_t frames) { size_t hw_ptr = *pcm->hw_ptr; hw_ptr += frames; - if (hw_ptr >= pcm->setup.boundary) - hw_ptr -= pcm->setup.boundary; + if (hw_ptr >= pcm->boundary) + hw_ptr -= pcm->boundary; *pcm->hw_ptr = hw_ptr; } @@ -127,11 +135,13 @@ ssize_t snd_pcm_mmap_write_areas(snd_pcm_t *pcm, xfer = 0; while (xfer < size) { size_t frames = snd_pcm_mmap_playback_xfer(pcm, size - xfer); + ssize_t err; snd_pcm_areas_copy(areas, offset, snd_pcm_mmap_areas(pcm), snd_pcm_mmap_offset(pcm), - pcm->setup.format.channels, - frames, pcm->setup.format.sfmt); - snd_pcm_mmap_forward(pcm, frames); + pcm->channels, + frames, pcm->format); + err = snd_pcm_mmap_forward(pcm, frames); + assert(err == (ssize_t)frames); offset += frames; xfer += frames; } @@ -152,11 +162,13 @@ ssize_t snd_pcm_mmap_read_areas(snd_pcm_t *pcm, xfer = 0; while (xfer < size) { size_t frames = snd_pcm_mmap_capture_xfer(pcm, size - xfer); + ssize_t err; snd_pcm_areas_copy(snd_pcm_mmap_areas(pcm), snd_pcm_mmap_offset(pcm), areas, offset, - pcm->setup.format.channels, - frames, pcm->setup.format.sfmt); - snd_pcm_mmap_forward(pcm, frames); + pcm->channels, + frames, pcm->format); + err = snd_pcm_mmap_forward(pcm, frames); + assert(err == (ssize_t)frames); offset += frames; xfer += frames; } @@ -167,7 +179,7 @@ ssize_t snd_pcm_mmap_read_areas(snd_pcm_t *pcm, ssize_t snd_pcm_mmap_writei(snd_pcm_t *pcm, const void *buffer, size_t size) { - snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + snd_pcm_channel_area_t areas[pcm->channels]; snd_pcm_areas_from_buf(pcm, areas, (void*)buffer); return snd_pcm_write_areas(pcm, areas, 0, size, snd_pcm_mmap_write_areas); @@ -175,7 +187,7 @@ ssize_t snd_pcm_mmap_writei(snd_pcm_t *pcm, const void *buffer, size_t size) ssize_t snd_pcm_mmap_writen(snd_pcm_t *pcm, void **bufs, size_t size) { - snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + snd_pcm_channel_area_t areas[pcm->channels]; snd_pcm_areas_from_bufs(pcm, areas, bufs); return snd_pcm_write_areas(pcm, areas, 0, size, snd_pcm_mmap_write_areas); @@ -183,7 +195,7 @@ ssize_t snd_pcm_mmap_writen(snd_pcm_t *pcm, void **bufs, size_t size) ssize_t snd_pcm_mmap_readi(snd_pcm_t *pcm, void *buffer, size_t size) { - snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + snd_pcm_channel_area_t areas[pcm->channels]; snd_pcm_areas_from_buf(pcm, areas, buffer); return snd_pcm_read_areas(pcm, areas, 0, size, snd_pcm_mmap_read_areas); @@ -191,71 +203,204 @@ ssize_t snd_pcm_mmap_readi(snd_pcm_t *pcm, void *buffer, size_t size) ssize_t snd_pcm_mmap_readn(snd_pcm_t *pcm, void **bufs, size_t size) { - snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + snd_pcm_channel_area_t areas[pcm->channels]; snd_pcm_areas_from_bufs(pcm, areas, bufs); return snd_pcm_read_areas(pcm, areas, 0, size, snd_pcm_mmap_read_areas); } -int snd_pcm_mmap_get_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *stopped_areas, snd_pcm_channel_area_t *running_areas) +int snd_pcm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info) { - snd_pcm_channel_setup_t setup; - snd_pcm_channel_area_t *r, *rp, *s, *sp; - unsigned int channel; - int err; - assert(pcm); - assert(pcm->mmap_info); - if (!pcm->running_areas) { - r = calloc(pcm->setup.format.channels, sizeof(*r)); - s = calloc(pcm->setup.format.channels, sizeof(*s)); - for (channel = 0, rp = r, sp = s; channel < pcm->setup.format.channels; ++channel, ++rp, ++sp) { - setup.channel = channel; - err = snd_pcm_channel_setup(pcm, &setup); - if (err < 0) { - free(r); - free(s); - return err; - } - *rp = setup.running_area; - *sp = setup.stopped_area; - } - pcm->running_areas = r; - pcm->stopped_areas = s; + return pcm->ops->channel_info(pcm, info); +} + +int snd_pcm_channel_info_shm(snd_pcm_t *pcm, snd_pcm_channel_info_t *info, + int shmid) +{ + switch (pcm->access) { + case SND_PCM_ACCESS_MMAP_INTERLEAVED: + case SND_PCM_ACCESS_RW_INTERLEAVED: + info->first = info->channel * pcm->bits_per_sample; + info->step = pcm->bits_per_frame; + break; + case SND_PCM_ACCESS_MMAP_NONINTERLEAVED: + case SND_PCM_ACCESS_RW_NONINTERLEAVED: + info->first = 0; + info->step = pcm->bits_per_sample; + break; + default: + assert(0); + break; } - if (running_areas) - memcpy(running_areas, pcm->running_areas, pcm->setup.format.channels * sizeof(*running_areas)); - if (stopped_areas) - memcpy(stopped_areas, pcm->stopped_areas, pcm->setup.format.channels * sizeof(*stopped_areas)); + info->addr = 0; + info->type = SND_PCM_AREA_SHM; + info->u.shm.shmid = shmid; return 0; -} +} int snd_pcm_mmap(snd_pcm_t *pcm) { int err; + unsigned int c; assert(pcm); - assert(pcm->valid_setup); - if (pcm->mmap_info) - return 0; - - if ((err = pcm->ops->mmap(pcm->op_arg)) < 0) - return err; - err = snd_pcm_mmap_get_areas(pcm, NULL, NULL); + assert(pcm->setup); + assert(!pcm->mmap_channels); + err = pcm->ops->mmap(pcm); if (err < 0) return err; + pcm->mmap_channels = calloc(pcm->channels, sizeof(pcm->mmap_channels[0])); + if (!pcm->mmap_channels) + return -ENOMEM; + assert(!pcm->running_areas); + pcm->running_areas = calloc(pcm->channels, sizeof(pcm->running_areas[0])); + if (!pcm->running_areas) { + free(pcm->mmap_channels); + pcm->mmap_channels = NULL; + return -ENOMEM; + } + for (c = 0; c < pcm->channels; ++c) { + snd_pcm_channel_info_t *i = &pcm->mmap_channels[c]; + i->channel = c; + err = snd_pcm_channel_info(pcm, i); + if (err < 0) + return err; + } + for (c = 0; c < pcm->channels; ++c) { + snd_pcm_channel_info_t *i = &pcm->mmap_channels[c]; + snd_pcm_channel_area_t *a = &pcm->running_areas[c]; + unsigned int c1; + if (!i->addr) { + char *ptr; + size_t size = i->first + i->step * pcm->buffer_size; + for (c1 = c + 1; c1 < pcm->channels; ++c1) { + snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1]; + size_t s; + if (i1->type != i->type) + continue; + switch (i1->type) { + case SND_PCM_AREA_MMAP: + if (i1->u.mmap.fd != i->u.mmap.fd || + i1->u.mmap.offset != i->u.mmap.offset) + continue; + break; + case SND_PCM_AREA_SHM: + if (i1->u.shm.shmid != i->u.shm.shmid) + continue; + break; + default: + assert(0); + } + s = i1->first + i1->step * pcm->buffer_size; + if (s > size) + size = s; + } + size = (size + 7) / 8; + size = PAGE_ALIGN(size); + switch (i->type) { + case SND_PCM_AREA_MMAP: + ptr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, i->u.mmap.fd, i->u.mmap.offset); + if (ptr == MAP_FAILED) { + SYSERR("mmap failed"); + return -errno; + } + i->addr = ptr; + break; + case SND_PCM_AREA_SHM: + if (i->u.shm.shmid < 0) { + int id; + id = shmget(IPC_PRIVATE, size, 0666); + if (id < 0) { + SYSERR("shmget failed"); + return -errno; + } + i->u.shm.shmid = id; + } + ptr = shmat(i->u.shm.shmid, 0, 0); + if (ptr == (void*) -1) { + SYSERR("shmat failed"); + return -errno; + } + i->addr = ptr; + break; + default: + assert(0); + } + } + for (c1 = c + 1; c1 < pcm->channels; ++c1) { + snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1]; + if (i1->type != i->type) + continue; + switch (i1->type) { + case SND_PCM_AREA_MMAP: + if (i1->u.mmap.fd != i->u.mmap.fd || + i1->u.mmap.offset != i->u.mmap.offset) + continue; + break; + case SND_PCM_AREA_SHM: + if (i1->u.shm.shmid != i->u.shm.shmid) + continue; + break; + default: + assert(0); + } + i1->addr = i->addr; + } + a->addr = i->addr; + a->first = i->first; + a->step = i->step; + } return 0; } int snd_pcm_munmap(snd_pcm_t *pcm) { int err; + unsigned int c; assert(pcm); - assert(pcm->mmap_info); - if ((err = pcm->ops->munmap(pcm->op_arg)) < 0) + assert(pcm->mmap_channels); + for (c = 0; c < pcm->channels; ++c) { + snd_pcm_channel_info_t *i = &pcm->mmap_channels[c]; + unsigned int c1; + size_t size = i->first + i->step * pcm->buffer_size; + if (!i->addr) + continue; + for (c1 = c + 1; c1 < pcm->channels; ++c1) { + snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1]; + size_t s; + if (i1->addr != i->addr) + continue; + i1->addr = NULL; + s = i1->first + i1->step * pcm->buffer_size; + if (s > size) + size = s; + } + size = (size + 7) / 8; + size = PAGE_ALIGN(size); + switch (i->type) { + case SND_PCM_AREA_MMAP: + err = munmap(i->addr, size); + if (err < 0) { + SYSERR("mmap failed"); + return -errno; + } + break; + case SND_PCM_AREA_SHM: + err = shmdt(i->addr); + if (err < 0) { + SYSERR("shmdt failed"); + return -errno; + } + break; + default: + assert(0); + } + i->addr = NULL; + } + err = pcm->ops->munmap(pcm); + if (err < 0) return err; - free(pcm->stopped_areas); - free(pcm->running_areas); - pcm->stopped_areas = 0; - pcm->running_areas = 0; + free(pcm->mmap_channels); + pcm->mmap_channels = 0; return 0; } @@ -267,25 +412,34 @@ ssize_t snd_pcm_write_mmap(snd_pcm_t *pcm, size_t size) while (xfer < size) { size_t frames = size - xfer; size_t offset = snd_pcm_mmap_hw_offset(pcm); - size_t cont = pcm->setup.buffer_size - offset; + size_t cont = pcm->buffer_size - offset; if (cont < frames) frames = cont; - if (pcm->setup.xfer_mode == SND_PCM_XFER_INTERLEAVED) { + switch (pcm->access) { + case SND_PCM_ACCESS_MMAP_INTERLEAVED: + { snd_pcm_channel_area_t *a = snd_pcm_mmap_areas(pcm); char *buf = snd_pcm_channel_area_addr(a, offset); - assert(pcm->setup.mmap_shape == SND_PCM_MMAP_INTERLEAVED); err = _snd_pcm_writei(pcm, buf, size); - } else { - size_t channels = pcm->setup.format.channels; + break; + } + case SND_PCM_ACCESS_MMAP_NONINTERLEAVED: + { + size_t channels = pcm->channels; unsigned int c; void *bufs[channels]; snd_pcm_channel_area_t *areas = snd_pcm_mmap_areas(pcm); - assert(pcm->setup.mmap_shape == SND_PCM_MMAP_NONINTERLEAVED); for (c = 0; c < channels; ++c) { snd_pcm_channel_area_t *a = &areas[c]; bufs[c] = snd_pcm_channel_area_addr(a, offset); } err = _snd_pcm_writen(pcm, bufs, size); + break; + } + default: + assert(0); + err = -EINVAL; + break; } if (err < 0) break; @@ -304,26 +458,34 @@ ssize_t snd_pcm_read_mmap(snd_pcm_t *pcm, size_t size) while (xfer < size) { size_t frames = size - xfer; size_t offset = snd_pcm_mmap_hw_offset(pcm); - size_t cont = pcm->setup.buffer_size - offset; + size_t cont = pcm->buffer_size - offset; if (cont < frames) frames = cont; - if (pcm->setup.xfer_mode == SND_PCM_XFER_INTERLEAVED) { + switch (pcm->access) { + case SND_PCM_ACCESS_MMAP_INTERLEAVED: + { snd_pcm_channel_area_t *a = snd_pcm_mmap_areas(pcm); char *buf = snd_pcm_channel_area_addr(a, offset); - assert(pcm->setup.mmap_shape == SND_PCM_MMAP_INTERLEAVED); err = _snd_pcm_readi(pcm, buf, size); - } else { - size_t channels = pcm->setup.format.channels; + break; + } + case SND_PCM_ACCESS_MMAP_NONINTERLEAVED: + { + size_t channels = pcm->channels; unsigned int c; void *bufs[channels]; snd_pcm_channel_area_t *areas = snd_pcm_mmap_areas(pcm); - assert(pcm->setup.mmap_shape == SND_PCM_MMAP_NONINTERLEAVED); for (c = 0; c < channels; ++c) { snd_pcm_channel_area_t *a = &areas[c]; bufs[c] = snd_pcm_channel_area_addr(a, offset); } err = _snd_pcm_readn(pcm->fast_op_arg, bufs, size); } + default: + assert(0); + err = -EINVAL; + break; + } if (err < 0) break; xfer += frames; diff --git a/src/pcm/pcm_mulaw.c b/src/pcm/pcm_mulaw.c index e7ed4802..01cd202c 100644 --- a/src/pcm/pcm_mulaw.c +++ b/src/pcm/pcm_mulaw.c @@ -35,8 +35,6 @@ typedef struct { int getput_idx; mulaw_f func; int sformat; - int cformat; - int cxfer_mode, cmmap_shape; } snd_pcm_mulaw_t; static inline int val_seg(int val) @@ -230,96 +228,76 @@ static void mulaw_encode(snd_pcm_channel_area_t *src_areas, } } -static int snd_pcm_mulaw_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info) +static int snd_pcm_mulaw_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info) { snd_pcm_mulaw_t *mulaw = pcm->private; - unsigned int req_mask = info->req_mask; - unsigned int sfmt = info->req.format.sfmt; + unsigned int format_mask, access_mask; int err; - if (req_mask & SND_PCM_PARAMS_SFMT) { - if (mulaw->sformat == SND_PCM_SFMT_MU_LAW ? - !snd_pcm_format_linear(sfmt) : - sfmt != SND_PCM_SFMT_MU_LAW) { - info->req.fail_mask = SND_PCM_PARAMS_SFMT; - info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL; - return -EINVAL; - } + info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | + SND_PCM_ACCBIT_RW_INTERLEAVED | + SND_PCM_ACCBIT_MMAP_NONINTERLEAVED | + SND_PCM_ACCBIT_RW_NONINTERLEAVED); + access_mask = info->access_mask; + if (access_mask == 0) + return -EINVAL; + if (mulaw->sformat == SND_PCM_FORMAT_MU_LAW) + info->format_mask &= SND_PCM_FMTBIT_LINEAR; + else + info->format_mask &= SND_PCM_FMTBIT_MU_LAW; + format_mask = info->format_mask; + if (format_mask == 0) + return -EINVAL; + + info->format_mask = 1U << mulaw->sformat; + info->access_mask = SND_PCM_ACCBIT_MMAP; + err = snd_pcm_hw_info(mulaw->plug.slave, info); + if (info->format_mask) + info->format_mask = format_mask; + if (info->access_mask) { + mulaw->plug.saccess_mask = info->access_mask; + info->access_mask = access_mask; } - info->req_mask |= SND_PCM_PARAMS_SFMT; - info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE | - SND_PCM_PARAMS_XFER_MODE); - info->req.format.sfmt = mulaw->sformat; - err = snd_pcm_params_info(mulaw->plug.slave, info); - info->req_mask = req_mask; - info->req.format.sfmt = sfmt; if (err < 0) return err; - if (req_mask & SND_PCM_PARAMS_SFMT) - info->formats = 1 << sfmt; - else - info->formats = mulaw->sformat == SND_PCM_SFMT_MU_LAW ? - SND_PCM_LINEAR_FORMATS : 1 << SND_PCM_SFMT_MU_LAW; - info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); - info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED; - return err; + info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); + snd_pcm_hw_info_complete(info); + return 0; } -static int snd_pcm_mulaw_params(snd_pcm_t *pcm, snd_pcm_params_t * params) +static int snd_pcm_mulaw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) { snd_pcm_mulaw_t *mulaw = pcm->private; snd_pcm_t *slave = mulaw->plug.slave; + unsigned int format, access; int err; - if (mulaw->sformat == SND_PCM_SFMT_MU_LAW ? - !snd_pcm_format_linear(params->format.sfmt) : - params->format.sfmt != SND_PCM_SFMT_MU_LAW) { - params->fail_mask = SND_PCM_PARAMS_SFMT; - params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL; - return -EINVAL; - } - mulaw->cformat = params->format.sfmt; - mulaw->cxfer_mode = params->xfer_mode; - mulaw->cmmap_shape = params->mmap_shape; - params->format.sfmt = mulaw->sformat; - params->xfer_mode = SND_PCM_XFER_UNSPECIFIED; - params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED; - err = snd_pcm_params_mmap(slave, params); - params->format.sfmt = mulaw->cformat; - params->xfer_mode = mulaw->cxfer_mode; - params->mmap_shape = mulaw->cmmap_shape; - return err; -} - -static int snd_pcm_mulaw_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup) -{ - snd_pcm_mulaw_t *mulaw = pcm->private; - int err = snd_pcm_setup(mulaw->plug.slave, setup); + format = params->format; + access = params->access; + params->format = mulaw->sformat; + if (mulaw->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) + params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED; + else if (mulaw->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED) + params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; + else + assert(0); + err = snd_pcm_hw_params(slave, params); + params->format = format; + params->access = access; if (err < 0) return err; - assert(mulaw->sformat == setup->format.sfmt); - if (mulaw->cxfer_mode == SND_PCM_XFER_UNSPECIFIED) - setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED; - else - setup->xfer_mode = mulaw->cxfer_mode; - if (mulaw->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED) - setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED; - else - setup->mmap_shape = mulaw->cmmap_shape; - setup->format.sfmt = mulaw->cformat; - setup->mmap_bytes = 0; if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { - if (mulaw->sformat == SND_PCM_SFMT_MU_LAW) { - mulaw->getput_idx = get_index(mulaw->cformat, SND_PCM_SFMT_S16); + if (mulaw->sformat == SND_PCM_FORMAT_MU_LAW) { + mulaw->getput_idx = get_index(format, SND_PCM_FORMAT_S16); mulaw->func = mulaw_encode; } else { - mulaw->getput_idx = put_index(SND_PCM_SFMT_S16, mulaw->sformat); + mulaw->getput_idx = put_index(SND_PCM_FORMAT_S16, mulaw->sformat); mulaw->func = mulaw_decode; } } else { - if (mulaw->sformat == SND_PCM_SFMT_MU_LAW) { - mulaw->getput_idx = put_index(SND_PCM_SFMT_S16, mulaw->cformat); + if (mulaw->sformat == SND_PCM_FORMAT_MU_LAW) { + mulaw->getput_idx = put_index(SND_PCM_FORMAT_S16, format); mulaw->func = mulaw_decode; } else { - mulaw->getput_idx = get_index(mulaw->sformat, SND_PCM_SFMT_S16); + mulaw->getput_idx = get_index(mulaw->sformat, SND_PCM_FORMAT_S16); mulaw->func = mulaw_encode; } } @@ -343,7 +321,7 @@ static ssize_t snd_pcm_mulaw_write_areas(snd_pcm_t *pcm, size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer); mulaw->func(areas, offset, snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave), - frames, pcm->setup.format.channels, + frames, pcm->channels, mulaw->getput_idx); err = snd_pcm_mmap_forward(slave, frames); if (err < 0) @@ -378,7 +356,7 @@ static ssize_t snd_pcm_mulaw_read_areas(snd_pcm_t *pcm, size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer); mulaw->func(snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave), areas, offset, - frames, pcm->setup.format.channels, + frames, pcm->channels, mulaw->getput_idx); err = snd_pcm_mmap_forward(slave, frames); if (err < 0) @@ -401,7 +379,7 @@ static void snd_pcm_mulaw_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_mulaw_t *mulaw = pcm->private; fprintf(fp, "Mu-Law conversion PCM (%s)\n", snd_pcm_format_name(mulaw->sformat)); - if (pcm->valid_setup) { + if (pcm->setup) { fprintf(fp, "Its setup is:\n"); snd_pcm_dump_setup(pcm, fp); } @@ -412,12 +390,12 @@ static void snd_pcm_mulaw_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_mulaw_ops = { close: snd_pcm_plugin_close, info: snd_pcm_plugin_info, - params_info: snd_pcm_mulaw_params_info, - params: snd_pcm_mulaw_params, - setup: snd_pcm_mulaw_setup, + hw_info: snd_pcm_mulaw_hw_info, + hw_params: snd_pcm_mulaw_hw_params, + sw_params: snd_pcm_plugin_sw_params, + dig_info: snd_pcm_plugin_dig_info, + dig_params: snd_pcm_plugin_dig_params, channel_info: snd_pcm_plugin_channel_info, - channel_params: snd_pcm_plugin_channel_params, - channel_setup: snd_pcm_plugin_channel_setup, dump: snd_pcm_mulaw_dump, nonblock: snd_pcm_plugin_nonblock, async: snd_pcm_plugin_async, @@ -431,7 +409,7 @@ int snd_pcm_mulaw_open(snd_pcm_t **pcmp, char *name, int sformat, snd_pcm_t *sla snd_pcm_mulaw_t *mulaw; assert(pcmp && slave); if (snd_pcm_format_linear(sformat) != 1 && - sformat != SND_PCM_SFMT_MU_LAW) + sformat != SND_PCM_FORMAT_MU_LAW) return -EINVAL; mulaw = calloc(1, sizeof(snd_pcm_mulaw_t)); if (!mulaw) { @@ -498,7 +476,7 @@ int _snd_pcm_mulaw_open(snd_pcm_t **pcmp, char *name, if (sformat < 0) return -EINVAL; if (snd_pcm_format_linear(sformat) != 1 && - sformat != SND_PCM_SFMT_MU_LAW) + sformat != SND_PCM_FORMAT_MU_LAW) return -EINVAL; continue; } diff --git a/src/pcm/pcm_multi.c b/src/pcm/pcm_multi.c index 13c9f523..95f63478 100644 --- a/src/pcm/pcm_multi.c +++ b/src/pcm/pcm_multi.c @@ -31,6 +31,7 @@ typedef struct { snd_pcm_t *pcm; unsigned int channels_count; int close_slave; + unsigned int access_mask; } snd_pcm_multi_slave_t; typedef struct { @@ -43,7 +44,6 @@ typedef struct { snd_pcm_multi_slave_t *slaves; size_t channels_count; snd_pcm_multi_channel_t *channels; - int xfer_mode; } snd_pcm_multi_t; static int snd_pcm_multi_close(snd_pcm_t *pcm) @@ -91,176 +91,104 @@ static int snd_pcm_multi_info(snd_pcm_t *pcm, snd_pcm_info_t *info) return 0; } -static int snd_pcm_multi_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info) +static int snd_pcm_multi_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info) { snd_pcm_multi_t *multi = pcm->private; - unsigned int i; - int err; - snd_pcm_t *slave_0 = multi->slaves[0].pcm; - unsigned int req_mask = info->req_mask; - unsigned int channels = info->req.format.channels; - if ((req_mask & SND_PCM_PARAMS_CHANNELS) && - channels != multi->channels_count) { - info->req.fail_mask |= SND_PCM_PARAMS_CHANNELS; - info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL; + unsigned int k; + snd_pcm_hw_info_t i; + unsigned int access_mask = ~0; + int err = 0; + if (info->channels_min < multi->channels_count) + info->channels_min = multi->channels_count; + if (info->channels_max > multi->channels_count) + info->channels_max = multi->channels_count; + if (info->channels_max > info->channels_max) return -EINVAL; - } - info->req_mask |= SND_PCM_PARAMS_CHANNELS; - info->req.format.channels = multi->slaves[0].channels_count; - err = snd_pcm_params_info(slave_0, info); - info->req_mask = req_mask; - info->req.format.channels = channels; - if (err < 0) - return err; - info->min_channels = multi->channels_count; - info->max_channels = multi->channels_count; - for (i = 1; i < multi->slaves_count; ++i) { - snd_pcm_t *slave_i = multi->slaves[i].pcm; - snd_pcm_params_info_t info_i; - info_i = *info; - info_i.req_mask |= SND_PCM_PARAMS_CHANNELS; - info_i.req.format.channels = multi->slaves[i].channels_count; - err = snd_pcm_params_info(slave_i, &info_i); + i = *info; + for (k = 0; k < multi->slaves_count; ++k) { + snd_pcm_t *slave = multi->slaves[k].pcm; + i.access_mask = SND_PCM_ACCBIT_MMAP; + i.channels_min = i.channels_max = multi->slaves[k].channels_count; + err = snd_pcm_hw_info(slave, &i); + access_mask &= i.access_mask; if (err < 0) - return err; - info->formats &= info_i.formats; - info->rates &= info_i.rates; - if (info_i.min_rate > info->min_rate) - info->min_rate = info_i.min_rate; - if (info_i.max_rate < info->max_rate) - info->max_rate = info_i.max_rate; - if (info_i.buffer_size < info->buffer_size) - info->buffer_size = info_i.buffer_size; - if (info_i.min_fragment_size > info->min_fragment_size) - info->min_fragment_size = info_i.min_fragment_size; - if (info_i.max_fragment_size < info->max_fragment_size) - info->max_fragment_size = info_i.max_fragment_size; - if (info_i.min_fragments > info->min_fragments) - info->min_fragments = info_i.min_fragments; - if (info_i.max_fragments < info->max_fragments) - info->max_fragments = info_i.max_fragments; - info->flags &= info_i.flags; + break; + multi->slaves[k].access_mask = i.access_mask; } - if (info->flags & SND_PCM_INFO_INTERLEAVED) { - if (multi->slaves_count > 0) { - info->flags &= ~SND_PCM_INFO_INTERLEAVED; - info->flags |= SND_PCM_INFO_COMPLEX; - } - } else if (!(info->flags & SND_PCM_INFO_NONINTERLEAVED)) - info->flags |= SND_PCM_INFO_COMPLEX; - info->req_mask = req_mask; - return 0; + *info = i; + if (i.channels_min <= i.channels_max) + info->channels_min = info->channels_max = multi->channels_count; + if (i.access_mask) { + if (!(access_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) || + multi->slaves_count > 1) + info->access_mask &= ~SND_PCM_ACCBIT_MMAP_INTERLEAVED; + if (!(access_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)) + info->access_mask &= ~SND_PCM_ACCBIT_MMAP_NONINTERLEAVED; + } + return err; } -static int snd_pcm_multi_mmap(snd_pcm_t *pcm) +static int snd_pcm_multi_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { snd_pcm_multi_t *multi = pcm->private; unsigned int i; - size_t count = 0; + snd_pcm_hw_params_t p; + int err; + if (params->channels != multi->channels_count) { + params->fail_mask = SND_PCM_HW_PARBIT_CHANNELS; + return -EINVAL; + } + p = *params; for (i = 0; i < multi->slaves_count; ++i) { snd_pcm_t *slave = multi->slaves[i].pcm; - snd_pcm_setup_t *setup; - int err = snd_pcm_mmap(slave); + if (multi->slaves[i].access_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) + p.access = SND_PCM_ACCESS_MMAP_INTERLEAVED; + else if (multi->slaves[i].access_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED) + p.access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; + else + assert(0); + p.channels = multi->slaves[i].channels_count; + err = snd_pcm_hw_params(slave, &p); + if (err < 0) { + params->fail_mask = p.fail_mask; + return err; + } + err = snd_pcm_areas_silence(slave->running_areas, 0, slave->channels, slave->buffer_size, slave->format); if (err < 0) return err; - count += slave->mmap_info_count; - setup = &slave->setup; - if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { - snd_pcm_channel_area_t r[setup->format.channels]; - snd_pcm_channel_area_t s[setup->format.channels]; - err = snd_pcm_mmap_get_areas(slave, s, r); - if (err < 0) - return err; - err = snd_pcm_areas_silence(s, 0, setup->format.channels, setup->buffer_size, setup->format.sfmt); - if (err < 0) - return err; - err = snd_pcm_areas_silence(r, 0, setup->format.channels, setup->buffer_size, setup->format.sfmt); + if (slave->stopped_areas) { + err = snd_pcm_areas_silence(slave->stopped_areas, 0, slave->channels, slave->buffer_size, slave->format); if (err < 0) return err; } } - pcm->mmap_info_count = count; - pcm->mmap_info = malloc(count * sizeof(*pcm->mmap_info)); - count = 0; - for (i = 0; i < multi->slaves_count; ++i) { - snd_pcm_t *slave = multi->slaves[i].pcm; - memcpy(&pcm->mmap_info[count], slave->mmap_info, slave->mmap_info_count * sizeof(*pcm->mmap_info)); - count += slave->mmap_info_count; - } return 0; } -static int snd_pcm_multi_munmap(snd_pcm_t *pcm) +static int snd_pcm_multi_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params) { snd_pcm_multi_t *multi = pcm->private; unsigned int i; + int err; for (i = 0; i < multi->slaves_count; ++i) { snd_pcm_t *slave = multi->slaves[i].pcm; - int err = snd_pcm_munmap(slave); + err = snd_pcm_sw_params(slave, params); if (err < 0) return err; } - pcm->mmap_info_count = 0; - free(pcm->mmap_info); - pcm->mmap_info = 0; return 0; } - -static int snd_pcm_multi_params(snd_pcm_t *pcm, snd_pcm_params_t *params) + +static int snd_pcm_multi_dig_info(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_dig_info_t *info ATTRIBUTE_UNUSED) { - snd_pcm_multi_t *multi = pcm->private; - unsigned int i; - snd_pcm_params_t p; - int err = 0; - if (params->format.channels != multi->channels_count) { - params->fail_mask = SND_PCM_PARAMS_CHANNELS; - params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL; - return -EINVAL; - } - p = *params; - for (i = 0; i < multi->slaves_count; ++i) { - snd_pcm_t *slave = multi->slaves[i].pcm; - p.format.channels = multi->slaves[i].channels_count; - err = snd_pcm_params_mmap(slave, &p); - if (err < 0) { - params->fail_mask = p.fail_mask; - params->fail_reason = p.fail_reason; - break; - } - } - if (err == 0) - multi->xfer_mode = params->xfer_mode; - return err; + /* FIXME */ + return -ENOSYS; } -static int snd_pcm_multi_setup(snd_pcm_t *pcm, snd_pcm_setup_t *setup) +static int snd_pcm_multi_dig_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_dig_params_t *params ATTRIBUTE_UNUSED) { - snd_pcm_multi_t *multi = pcm->private; - unsigned int i; - int err; - err = snd_pcm_setup(multi->slaves[0].pcm, setup); - if (err < 0) - return err; - for (i = 1; i < multi->slaves_count; ++i) { - snd_pcm_setup_t s; - snd_pcm_t *sh = multi->slaves[i].pcm; - err = snd_pcm_setup(sh, &s); - if (err < 0) - return err; - if (setup->format.rate != s.format.rate) - return -EINVAL; - if (setup->buffer_size != s.buffer_size) - return -EINVAL; - if (setup->mmap_shape != SND_PCM_MMAP_NONINTERLEAVED || - s.mmap_shape != SND_PCM_MMAP_NONINTERLEAVED) - setup->mmap_shape = SND_PCM_MMAP_COMPLEX; - } - setup->format.channels = multi->channels_count; - if (multi->xfer_mode == SND_PCM_XFER_UNSPECIFIED) - setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED; - else - setup->xfer_mode = multi->xfer_mode; - return 0; + /* FIXME */ + return -ENOSYS; } static int snd_pcm_multi_status(snd_pcm_t *pcm, snd_pcm_status_t *status) @@ -335,34 +263,6 @@ static int snd_pcm_multi_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *in return err; } -static int snd_pcm_multi_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params) -{ - snd_pcm_multi_t *multi = pcm->private; - unsigned int channel = params->channel; - snd_pcm_multi_channel_t *c = &multi->channels[channel]; - int err; - if (c->slave_idx < 0) - return -ENXIO; - params->channel = c->slave_channel; - err = snd_pcm_channel_params(multi->slaves[c->slave_idx].pcm, params); - params->channel = channel; - return err; -} - -static int snd_pcm_multi_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup) -{ - snd_pcm_multi_t *multi = pcm->private; - unsigned int channel = setup->channel; - snd_pcm_multi_channel_t *c = &multi->channels[channel]; - int err; - if (c->slave_idx < 0) - return -ENXIO; - setup->channel = c->slave_channel; - err = snd_pcm_channel_setup(multi->slaves[c->slave_idx].pcm, setup); - setup->channel = channel; - return err; -} - static ssize_t snd_pcm_multi_rewind(snd_pcm_t *pcm, size_t frames) { snd_pcm_multi_t *multi = pcm->private; @@ -413,6 +313,16 @@ static int snd_pcm_multi_set_avail_min(snd_pcm_t *pcm, size_t frames) return snd_pcm_set_avail_min(multi->slaves[0].pcm, frames); } +static int snd_pcm_multi_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int snd_pcm_multi_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED) +{ + return 0; +} + int snd_pcm_multi_poll_descriptor(snd_pcm_t *pcm) { snd_pcm_multi_t *multi = pcm->private; @@ -433,7 +343,7 @@ static void snd_pcm_multi_dump(snd_pcm_t *pcm, FILE *fp) fprintf(fp, "%d: slave %d, channel %d\n", k, c->slave_idx, c->slave_channel); } - if (pcm->valid_setup) { + if (pcm->setup) { fprintf(fp, "\nIts setup is:\n"); snd_pcm_dump_setup(pcm, fp); } @@ -446,12 +356,12 @@ static void snd_pcm_multi_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_multi_ops = { close: snd_pcm_multi_close, info: snd_pcm_multi_info, - params_info: snd_pcm_multi_params_info, - params: snd_pcm_multi_params, - setup: snd_pcm_multi_setup, + hw_info: snd_pcm_multi_hw_info, + hw_params: snd_pcm_multi_hw_params, + sw_params: snd_pcm_multi_sw_params, + dig_info: snd_pcm_multi_dig_info, + dig_params: snd_pcm_multi_dig_params, channel_info: snd_pcm_multi_channel_info, - channel_params: snd_pcm_multi_channel_params, - channel_setup: snd_pcm_multi_channel_setup, dump: snd_pcm_multi_dump, nonblock: snd_pcm_multi_nonblock, async: snd_pcm_multi_async, @@ -538,7 +448,7 @@ int snd_pcm_multi_open(snd_pcm_t **pcmp, char *name, pcm->type = SND_PCM_TYPE_MULTI; pcm->stream = stream; pcm->mode = multi->slaves[0].pcm->mode; - pcm->mmap_auto = 1; + pcm->mmap_rw = 1; pcm->ops = &snd_pcm_multi_ops; pcm->op_arg = pcm; pcm->fast_ops = &snd_pcm_multi_fast_ops; diff --git a/src/pcm/pcm_null.c b/src/pcm/pcm_null.c index 625c08f9..2878a17b 100644 --- a/src/pcm/pcm_null.c +++ b/src/pcm/pcm_null.c @@ -21,13 +21,14 @@ #include <byteswap.h> #include <limits.h> +#include <sys/shm.h> #include "pcm_local.h" #include "pcm_plugin.h" typedef struct { - snd_pcm_setup_t setup; snd_timestamp_t trigger_time; int state; + int shmid; size_t appl_ptr; size_t hw_ptr; int poll_fd; @@ -58,25 +59,10 @@ static int snd_pcm_null_info(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_info_t * i return 0; } -static int snd_pcm_null_channel_info(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_channel_info_t * info) +static int snd_pcm_null_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info) { - int channel = info->channel; - memset(info, 0, sizeof(*info)); - info->channel = channel; - return 0; -} - -static int snd_pcm_null_channel_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_channel_params_t * params ATTRIBUTE_UNUSED) -{ - return 0; -} - -static int snd_pcm_null_channel_setup(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_channel_setup_t * setup) -{ - int channel = setup->channel; - memset(setup, 0, sizeof(*setup)); - setup->channel = channel; - return 0; + snd_pcm_null_t *null = pcm->private; + return snd_pcm_channel_info_shm(pcm, info, null->shmid); } static int snd_pcm_null_status(snd_pcm_t *pcm, snd_pcm_status_t * status) @@ -86,7 +72,7 @@ static int snd_pcm_null_status(snd_pcm_t *pcm, snd_pcm_status_t * status) status->state = null->state; status->trigger_time = null->trigger_time; gettimeofday(&status->tstamp, 0); - status->avail = pcm->setup.buffer_size; + status->avail = pcm->buffer_size; status->avail_max = status->avail; return 0; } @@ -118,7 +104,7 @@ static int snd_pcm_null_start(snd_pcm_t *pcm) assert(null->state == SND_PCM_STATE_PREPARED); null->state = SND_PCM_STATE_RUNNING; if (pcm->stream == SND_PCM_STREAM_CAPTURE) - snd_pcm_mmap_appl_forward(pcm, pcm->setup.buffer_size); + snd_pcm_mmap_appl_forward(pcm, pcm->buffer_size); return 0; } @@ -182,7 +168,7 @@ static ssize_t snd_pcm_null_writei(snd_pcm_t *pcm, const void *buffer ATTRIBUTE_ { snd_pcm_null_t *null = pcm->private; if (null->state == SND_PCM_STATE_PREPARED && - pcm->setup.start_mode != SND_PCM_START_EXPLICIT) { + pcm->start_mode != SND_PCM_START_EXPLICIT) { null->state = SND_PCM_STATE_RUNNING; } return snd_pcm_null_fwd(pcm, size); @@ -192,7 +178,7 @@ static ssize_t snd_pcm_null_writen(snd_pcm_t *pcm, void **bufs ATTRIBUTE_UNUSED, { snd_pcm_null_t *null = pcm->private; if (null->state == SND_PCM_STATE_PREPARED && - pcm->setup.start_mode != SND_PCM_START_EXPLICIT) { + pcm->start_mode != SND_PCM_START_EXPLICIT) { null->state = SND_PCM_STATE_RUNNING; } return snd_pcm_null_fwd(pcm, size); @@ -202,9 +188,9 @@ static ssize_t snd_pcm_null_readi(snd_pcm_t *pcm, void *buffer ATTRIBUTE_UNUSED, { snd_pcm_null_t *null = pcm->private; if (null->state == SND_PCM_STATE_PREPARED && - pcm->setup.start_mode != SND_PCM_START_EXPLICIT) { + pcm->start_mode != SND_PCM_START_EXPLICIT) { null->state = SND_PCM_STATE_RUNNING; - snd_pcm_mmap_hw_forward(pcm, pcm->setup.buffer_size); + snd_pcm_mmap_hw_forward(pcm, pcm->buffer_size); } return snd_pcm_null_fwd(pcm, size); } @@ -213,9 +199,9 @@ static ssize_t snd_pcm_null_readn(snd_pcm_t *pcm, void **bufs ATTRIBUTE_UNUSED, { snd_pcm_null_t *null = pcm->private; if (null->state == SND_PCM_STATE_PREPARED && - pcm->setup.start_mode != SND_PCM_START_EXPLICIT) { + pcm->start_mode != SND_PCM_START_EXPLICIT) { null->state = SND_PCM_STATE_RUNNING; - snd_pcm_mmap_hw_forward(pcm, pcm->setup.buffer_size); + snd_pcm_mmap_hw_forward(pcm, pcm->buffer_size); } return snd_pcm_null_fwd(pcm, size); } @@ -227,111 +213,83 @@ static ssize_t snd_pcm_null_mmap_forward(snd_pcm_t *pcm, size_t size) static ssize_t snd_pcm_null_avail_update(snd_pcm_t *pcm) { - return pcm->setup.buffer_size; + return pcm->buffer_size; } static int snd_pcm_null_set_avail_min(snd_pcm_t *pcm, size_t frames) { - pcm->setup.buffer_size = frames; + pcm->avail_min = frames; return 0; } -static int snd_pcm_null_mmap(snd_pcm_t *pcm) +static int snd_pcm_null_hw_info(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_info_t * info) { - snd_pcm_mmap_info_t *i; - int err; - i = calloc(1, sizeof(*i)); - if (!i) - return -ENOMEM; - err = snd_pcm_alloc_user_mmap(pcm, i); - if (err < 0) { - free(i); - return err; - } - pcm->mmap_info = i; - pcm->mmap_info_count = 1; + snd_pcm_hw_info_complete(info); + info->fifo_size = 0; return 0; } -static int snd_pcm_null_munmap(snd_pcm_t *pcm) +static int snd_pcm_null_hw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t * params ATTRIBUTE_UNUSED) { - int err = snd_pcm_free_mmap(pcm, pcm->mmap_info); - if (err < 0) - return err; - free(pcm->mmap_info); - pcm->mmap_info_count = 0; - pcm->mmap_info = 0; return 0; } -static int snd_pcm_null_params_info(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_params_info_t * info) -{ - int sizes = ((info->req_mask & SND_PCM_PARAMS_SFMT) && - (info->req_mask & SND_PCM_PARAMS_CHANNELS)); - info->flags = SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID | - SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED | - SND_PCM_INFO_PAUSE; - info->formats = ~0; - info->rates = SND_PCM_RATE_CONTINUOUS | SND_PCM_RATE_8000_192000; - info->min_rate = 4000; - info->max_rate = 192000; - info->min_channels = 1; - info->max_channels = 32; - info->min_fragments = 1; - info->max_fragments = 1024 * 1024; - if (sizes) { - info->buffer_size = 1024 * 1024; - info->min_fragment_size = 1; - info->max_fragment_size = 1024 * 1024; - info->fragment_align = 1; +static int snd_pcm_null_sw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_sw_params_t * params) +{ + if (params->start_mode > SND_PCM_START_LAST) { + params->fail_mask = SND_PCM_SW_PARBIT_START_MODE; + return -EINVAL; + } + if (params->ready_mode > SND_PCM_READY_LAST) { + params->fail_mask = SND_PCM_SW_PARBIT_READY_MODE; + return -EINVAL; + } + if (params->xrun_mode > SND_PCM_XRUN_LAST) { + params->fail_mask = SND_PCM_SW_PARBIT_XRUN_MODE; + return -EINVAL; } return 0; } -static int snd_pcm_null_params(snd_pcm_t *pcm, snd_pcm_params_t * params) +static int snd_pcm_null_dig_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_dig_params_t *params ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int snd_pcm_null_dig_info(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_dig_info_t *info ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int snd_pcm_null_mmap(snd_pcm_t *pcm) { snd_pcm_null_t *null = pcm->private; - snd_pcm_setup_t *s = &null->setup; - int w = snd_pcm_format_width(s->format.sfmt); - if (w < 0) { - params->fail_mask = SND_PCM_PARAMS_SFMT; - return -EINVAL; + if (!(pcm->info & SND_PCM_INFO_MMAP)) { + size_t size = snd_pcm_frames_to_bytes(pcm, pcm->buffer_size); + int id = shmget(IPC_PRIVATE, size, 0666); + if (id < 0) { + SYSERR("shmget failed"); + return -errno; + } + null->shmid = id; } - s->msbits = w; - s->format = params->format; - s->start_mode = params->start_mode; - s->ready_mode = params->ready_mode; - s->avail_min = params->avail_min; - s->xfer_mode = params->xfer_mode; - s->xfer_min = params->xfer_min; - s->xfer_align = params->xfer_align; - s->xrun_mode = params->xrun_mode; - s->mmap_shape = params->mmap_shape; - s->frag_size = params->frag_size; - s->frags = s->buffer_size / s->frag_size; - if (s->frags < 1) - s->frags = 1; - s->buffer_size = s->frag_size * s->frags; - s->boundary = LONG_MAX - LONG_MAX % s->buffer_size; - s->time = params->time; - s->rate_master = s->format.rate; - s->rate_divisor = 1; - s->mmap_bytes = 0; - s->fifo_size = 1; return 0; } -static int snd_pcm_null_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup) +static int snd_pcm_null_munmap(snd_pcm_t *pcm) { snd_pcm_null_t *null = pcm->private; - *setup = null->setup; + if (shmctl(null->shmid, IPC_RMID, 0) < 0) { + SYSERR("shmctl IPC_RMID failed"); + return -errno; + } return 0; } static void snd_pcm_null_dump(snd_pcm_t *pcm, FILE *fp) { fprintf(fp, "Null PCM\n"); - if (pcm->valid_setup) { + if (pcm->setup) { fprintf(fp, "Its setup is:\n"); snd_pcm_dump_setup(pcm, fp); } @@ -340,12 +298,12 @@ static void snd_pcm_null_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_null_ops = { close: snd_pcm_null_close, info: snd_pcm_null_info, - params_info: snd_pcm_null_params_info, - params: snd_pcm_null_params, - setup: snd_pcm_null_setup, + hw_info: snd_pcm_null_hw_info, + hw_params: snd_pcm_null_hw_params, + sw_params: snd_pcm_null_sw_params, + dig_params: snd_pcm_null_dig_params, + dig_info: snd_pcm_null_dig_info, channel_info: snd_pcm_null_channel_info, - channel_params: snd_pcm_null_channel_params, - channel_setup: snd_pcm_null_channel_setup, dump: snd_pcm_null_dump, nonblock: snd_pcm_null_nonblock, async: snd_pcm_null_async, diff --git a/src/pcm/pcm_plug.c b/src/pcm/pcm_plug.c index d659ccb5..cafe1970 100644 --- a/src/pcm/pcm_plug.c +++ b/src/pcm/pcm_plug.c @@ -32,37 +32,25 @@ typedef struct { } snd_pcm_plug_t; -unsigned int snd_pcm_plug_formats(unsigned int formats) -{ - int fmts = (SND_PCM_LINEAR_FORMATS | SND_PCM_FMT_MU_LAW | - SND_PCM_FMT_A_LAW | SND_PCM_FMT_IMA_ADPCM); - if (formats & fmts) - formats |= fmts; - return formats; -} - static int preferred_formats[] = { - SND_PCM_SFMT_S16_LE, - SND_PCM_SFMT_S16_BE, - SND_PCM_SFMT_U16_LE, - SND_PCM_SFMT_U16_BE, - SND_PCM_SFMT_S24_LE, - SND_PCM_SFMT_S24_BE, - SND_PCM_SFMT_U24_LE, - SND_PCM_SFMT_U24_BE, - SND_PCM_SFMT_S32_LE, - SND_PCM_SFMT_S32_BE, - SND_PCM_SFMT_U32_LE, - SND_PCM_SFMT_U32_BE, - SND_PCM_SFMT_S8, - SND_PCM_SFMT_U8 + SND_PCM_FORMAT_S16_LE, + SND_PCM_FORMAT_S16_BE, + SND_PCM_FORMAT_U16_LE, + SND_PCM_FORMAT_U16_BE, + SND_PCM_FORMAT_S24_LE, + SND_PCM_FORMAT_S24_BE, + SND_PCM_FORMAT_U24_LE, + SND_PCM_FORMAT_U24_BE, + SND_PCM_FORMAT_S32_LE, + SND_PCM_FORMAT_S32_BE, + SND_PCM_FORMAT_U32_LE, + SND_PCM_FORMAT_U32_BE, + SND_PCM_FORMAT_S8, + SND_PCM_FORMAT_U8 }; -static int snd_pcm_plug_slave_fmt(int format, - snd_pcm_params_info_t *slave_info) +static int snd_pcm_plug_slave_fmt(int format, unsigned int format_mask) { - if ((snd_pcm_plug_formats(slave_info->formats) & (1 << format)) == 0) - return -EINVAL; if (snd_pcm_format_linear(format)) { int width = snd_pcm_format_width(format); int unsignd = snd_pcm_format_unsigned(format); @@ -77,8 +65,8 @@ static int snd_pcm_plug_slave_fmt(int format, for (sgn = 0; sgn < 2; ++sgn) { format1 = snd_pcm_build_linear_format(width1, unsignd1, big1); if (format1 >= 0 && - slave_info->formats & (1 << format1)) - goto _found; + format_mask & (1U << format1)) + return format1; unsignd1 = !unsignd1; } big1 = !big1; @@ -89,80 +77,24 @@ static int snd_pcm_plug_slave_fmt(int format, } width1 += dwidth1; } - return -EINVAL; - _found: - return format1; + return ffs(format_mask) - 1; } else { unsigned int i; switch (format) { - case SND_PCM_SFMT_MU_LAW: - case SND_PCM_SFMT_A_LAW: - case SND_PCM_SFMT_IMA_ADPCM: + case SND_PCM_FORMAT_MU_LAW: + case SND_PCM_FORMAT_A_LAW: + case SND_PCM_FORMAT_IMA_ADPCM: for (i = 0; i < sizeof(preferred_formats) / sizeof(preferred_formats[0]); ++i) { int format1 = preferred_formats[i]; - if (slave_info->formats & (1 << format1)) + if (format_mask & (1U << format1)) return format1; } default: - return -EINVAL; + return ffs(format_mask) - 1; } } } -struct { - unsigned int rate; - unsigned int flag; -} snd_pcm_rates[] = { - { 8000, SND_PCM_RATE_8000 }, - { 11025, SND_PCM_RATE_11025 }, - { 16000, SND_PCM_RATE_16000 }, - { 22050, SND_PCM_RATE_22050 }, - { 32000, SND_PCM_RATE_32000 }, - { 44100, SND_PCM_RATE_44100 }, - { 48000, SND_PCM_RATE_48000 }, - { 88200, SND_PCM_RATE_88200 }, - { 96000, SND_PCM_RATE_96000 }, - { 176400, SND_PCM_RATE_176400 }, - { 192000, SND_PCM_RATE_192000 } -}; - -static int snd_pcm_plug_slave_rate(unsigned int rate, - snd_pcm_params_info_t *slave_info) -{ - if (rate <= slave_info->min_rate) - return slave_info->min_rate; - else if (rate >= slave_info->max_rate) - return slave_info->max_rate; - else if (!(slave_info->rates & (SND_PCM_RATE_CONTINUOUS | - SND_PCM_RATE_KNOT))) { - unsigned int k; - unsigned int rate1 = 0, rate2 = 0; - int delta1, delta2; - for (k = 0; k < sizeof(snd_pcm_rates) / - sizeof(snd_pcm_rates[0]); ++k) { - if (!(snd_pcm_rates[k].flag & slave_info->rates)) - continue; - if (snd_pcm_rates[k].rate < rate) { - rate1 = snd_pcm_rates[k].rate; - } else if (snd_pcm_rates[k].rate >= rate) { - rate2 = snd_pcm_rates[k].rate; - break; - } - } - if (rate1 == 0) - return rate2; - if (rate2 == 0) - return rate1; - delta1 = rate - rate1; - delta2 = rate2 - rate; - if (delta1 < delta2) - return rate1; - else - return rate2; - } - return rate; -} - static int snd_pcm_plug_close(snd_pcm_t *pcm) { snd_pcm_plug_t *plug = pcm->private; @@ -206,97 +138,156 @@ static int snd_pcm_plug_info(snd_pcm_t *pcm, snd_pcm_info_t *info) return 0; } -static int snd_pcm_plug_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info) +static int snd_pcm_plug_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info) { int err; snd_pcm_plug_t *plug = pcm->private; snd_pcm_t *slave = plug->req_slave; - snd_pcm_params_info_t slave_info; - int sformat, srate; - unsigned int schannels; - int crate; - - info->req.fail_reason = 0; - info->req.fail_mask = 0; + snd_pcm_hw_info_t sinfo, i; + snd_pcm_hw_params_t sparams; + unsigned int rate_min, rate_max; + unsigned int channels_min, channels_max; + unsigned int format, format_mask; + size_t fragment_size_min, fragment_size_max; + + info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | + SND_PCM_ACCBIT_RW_INTERLEAVED | + SND_PCM_ACCBIT_MMAP_NONINTERLEAVED | + SND_PCM_ACCBIT_RW_NONINTERLEAVED); + if (info->access_mask == 0) + return -EINVAL; - if (info->req_mask & SND_PCM_PARAMS_RATE) { - info->min_rate = info->req.format.rate; - info->max_rate = info->req.format.rate; - } else { - info->min_rate = 4000; - info->max_rate = 192000; - } - info->rates = SND_PCM_RATE_CONTINUOUS | SND_PCM_RATE_8000_192000; + info->format_mask &= (SND_PCM_FMTBIT_LINEAR | SND_PCM_FMTBIT_MU_LAW | + SND_PCM_FMTBIT_A_LAW | SND_PCM_FMTBIT_IMA_ADPCM); + if (info->format_mask == 0) + return -EINVAL; - if (info->req_mask & SND_PCM_PARAMS_CHANNELS) { - info->min_channels = info->req.format.channels; - info->max_channels = info->req.format.channels; - } else { - info->min_channels = 1; - info->max_channels = 32; - } + if (info->rate_min < 4000) + info->rate_min = 4000; + if (info->rate_max > 192000) + info->rate_max = 192000; + if (info->rate_max < info->rate_min) + return -EINVAL; - memset(&slave_info, 0, sizeof(slave_info)); - if ((err = snd_pcm_params_info(slave, &slave_info)) < 0) - return err; + if (info->channels_min < 1) + info->channels_min = 1; + if (info->channels_max > 1024) + info->channels_max = 1024; + if (info->channels_max < info->channels_min) + return -EINVAL; - info->flags = slave_info.flags; - info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED; + if (info->fragment_size_max > 1024 * 1024) + info->fragment_size_max = 1024 * 1024; + if (info->fragment_size_max < info->fragment_size_min) + return -EINVAL; - info->min_fragments = slave_info.min_fragments; - info->max_fragments = slave_info.max_fragments; - - if (info->req_mask & SND_PCM_PARAMS_SFMT) - info->formats = 1 << info->req.format.sfmt; - else { - info->formats = snd_pcm_plug_formats(slave_info.formats); - return 0; + sinfo.access_mask = SND_PCM_ACCBIT_MMAP; + sinfo.format_mask = (SND_PCM_FMTBIT_LINEAR | SND_PCM_FMTBIT_MU_LAW | + SND_PCM_FMTBIT_A_LAW | SND_PCM_FMTBIT_IMA_ADPCM); + sinfo.subformat_mask = SND_PCM_SUBFMTBIT_STD; + sinfo.channels_min = 1; + sinfo.channels_max = 1024; + sinfo.rate_min = 4000; + sinfo.rate_max = 192000; + sinfo.fragments_min = 1; + sinfo.fragments_max = UINT_MAX; + sinfo.fragment_size_min = 1; + sinfo.fragment_size_max = ULONG_MAX; + + /* Formats */ + err = snd_pcm_hw_info(slave, &sinfo); + if (err < 0) { + *info = i; + return err; } - - sformat = snd_pcm_plug_slave_fmt(info->req.format.sfmt, &slave_info); - if (sformat < 0) { - info->req.fail_mask = SND_PCM_PARAMS_SFMT; - info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL; - return -EINVAL; + format_mask = 0; + for (format = 0; format < SND_PCM_FORMAT_LAST; ++format) { + if (!(info->format_mask & (1 << format))) + continue; + err = snd_pcm_plug_slave_fmt(format, sinfo.format_mask); + if (err < 0) + info->format_mask &= ~(1 << format); + else + format_mask |= (1 << err); } - - if (!(info->req_mask & SND_PCM_PARAMS_RATE)) - return 0; - crate = info->req.format.rate; - srate = snd_pcm_plug_slave_rate(crate, &slave_info); - if (srate < 0) { - info->req.fail_mask = SND_PCM_PARAMS_RATE; - info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL; - return -EINVAL; + sinfo.format_mask = format_mask; + + /* Rate (and fragment_size) */ + i = sinfo; + sparams.rate = info->rate_min; + err = snd_pcm_hw_info_rulesv(slave, &i, &sparams, + SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_RATE, + -1); + if (err < 0) { + *info = i; + return err; } - - if (!(info->req_mask & SND_PCM_PARAMS_CHANNELS)) - return 0; - schannels = info->req.format.channels; - if (schannels < info->min_channels) - schannels = info->min_channels; - else if (schannels > info->max_channels) - schannels = info->max_channels; - - slave_info.req_mask = (SND_PCM_PARAMS_SFMT | - SND_PCM_PARAMS_CHANNELS | - SND_PCM_PARAMS_RATE); - slave_info.req.format.sfmt = sformat; - slave_info.req.format.channels = schannels; - slave_info.req.format.rate = srate; - if ((err = snd_pcm_params_info(slave, &slave_info)) < 0) { - info->req.fail_mask = slave_info.req.fail_mask; - info->req.fail_reason = slave_info.req.fail_reason; + rate_min = i.rate_min; + if (info->rate_max != info->rate_min) { + i = sinfo; + sparams.rate = info->rate_max; + err = snd_pcm_hw_info_rulesv(slave, &i, &sparams, + SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_RATE, + -1); + if (err < 0) { + *info = i; + return err; + } + rate_max = i.rate_min; + } else + rate_max = rate_min; + sinfo.rate_min = rate_min; + sinfo.rate_max = rate_max; + + /* Channels */ + i = sinfo; + sparams.channels = info->channels_min; + err = snd_pcm_hw_info_rulesv(slave, &i, &sparams, + SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_CHANNELS, + -1); + if (err < 0) { + *info = i; + return err; + } + channels_min = i.channels_min; + if (info->channels_max != info->channels_min) { + i = sinfo; + sparams.channels = info->channels_max; + err = snd_pcm_hw_info_rulesv(slave, &i, &sparams, + SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_CHANNELS, + -1); + if (err < 0) { + *info = i; + return err; + } + channels_max = i.channels_min; + } else + channels_max = channels_min; + sinfo.channels_min = channels_min; + sinfo.channels_max = channels_max; + + sinfo.fragments_min = info->fragments_min; + sinfo.fragments_max = info->fragments_max; + sinfo.fragment_size_min = muldiv_down(info->fragment_size_min, sinfo.rate_min, info->rate_max); + sinfo.fragment_size_max = muldiv_up(info->fragment_size_max, sinfo.rate_max, info->rate_min); + err = snd_pcm_hw_info(slave, &sinfo); + if (err < 0) { + *info = sinfo; return err; } - info->buffer_size = muldiv64(slave_info.buffer_size, crate, srate); - info->min_fragment_size = muldiv64(slave_info.min_fragment_size, crate, srate); - info->max_fragment_size = muldiv64(slave_info.max_fragment_size, crate, srate); - info->fragment_align = muldiv64(slave_info.fragment_align, crate, srate); - if (sformat != info->req.format.sfmt || - (unsigned int) srate != info->req.format.rate || - schannels != info->req.format.channels) - info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); + + info->subformat_mask = sinfo.subformat_mask; + info->fragments_min = sinfo.fragments_min; + info->fragments_max = sinfo.fragments_max; + + fragment_size_min = muldiv_down(sinfo.fragment_size_min, info->rate_min, sinfo.rate_max); + fragment_size_max = muldiv_up(sinfo.fragment_size_max, info->rate_max, sinfo.rate_min); + if (fragment_size_min > info->fragment_size_min) + info->fragment_size_min = fragment_size_min; + if (fragment_size_max < info->fragment_size_max) + info->fragment_size_max = fragment_size_max; + info->info = sinfo.info & ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); + snd_pcm_hw_info_complete(info); return 0; } @@ -313,29 +304,29 @@ static void snd_pcm_plug_clear(snd_pcm_t *pcm) } } -static int snd_pcm_plug_change_rate(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_format_t *clt, snd_pcm_format_t *slv) +static int snd_pcm_plug_change_rate(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_hw_params_t *clt, snd_pcm_hw_params_t *slv) { snd_pcm_plug_t *plug = pcm->private; int err; - assert(snd_pcm_format_linear(slv->sfmt)); + assert(snd_pcm_format_linear(slv->format)); if (clt->rate == slv->rate) return 0; - err = snd_pcm_rate_open(new, NULL, slv->sfmt, slv->rate, plug->slave, plug->slave != plug->req_slave); + err = snd_pcm_rate_open(new, NULL, slv->format, slv->rate, plug->slave, plug->slave != plug->req_slave); if (err < 0) return err; slv->rate = clt->rate; - if (snd_pcm_format_linear(clt->sfmt)) - slv->sfmt = clt->sfmt; + if (snd_pcm_format_linear(clt->format)) + slv->format = clt->format; return 1; } -static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_format_t *clt, snd_pcm_format_t *slv) +static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_hw_params_t *clt, snd_pcm_hw_params_t *slv) { snd_pcm_plug_t *plug = pcm->private; unsigned int tt_ssize, tt_cused, tt_sused; ttable_entry_t *ttable; int err; - assert(snd_pcm_format_linear(slv->sfmt)); + assert(snd_pcm_format_linear(slv->format)); if (clt->channels == slv->channels) return 0; if (clt->rate != slv->rate && @@ -384,100 +375,100 @@ static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm s = 0; } } - err = snd_pcm_route_open(new, NULL, slv->sfmt, slv->channels, ttable, tt_ssize, tt_cused, tt_sused, plug->slave, plug->slave != plug->req_slave); + err = snd_pcm_route_open(new, NULL, slv->format, slv->channels, ttable, tt_ssize, tt_cused, tt_sused, plug->slave, plug->slave != plug->req_slave); if (err < 0) return err; slv->channels = clt->channels; - if (snd_pcm_format_linear(clt->sfmt)) - slv->sfmt = clt->sfmt; + if (snd_pcm_format_linear(clt->format)) + slv->format = clt->format; return 1; } -static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_format_t *clt, snd_pcm_format_t *slv) +static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_hw_params_t *clt, snd_pcm_hw_params_t *slv) { snd_pcm_plug_t *plug = pcm->private; int err, cfmt; int (*f)(snd_pcm_t **pcm, char *name, int sformat, snd_pcm_t *slave, int close_slave); - if (snd_pcm_format_linear(slv->sfmt)) { + if (snd_pcm_format_linear(slv->format)) { /* Conversion is done in another plugin */ - if (clt->sfmt == slv->sfmt || + if (clt->format == slv->format || clt->rate != slv->rate || clt->channels != slv->channels) return 0; } else { /* No conversion is needed */ - if (clt->sfmt == slv->sfmt && + if (clt->format == slv->format && clt->rate == slv->rate && clt->channels == clt->channels) return 0; } - if (snd_pcm_format_linear(slv->sfmt)) { - cfmt = clt->sfmt; - switch (clt->sfmt) { - case SND_PCM_SFMT_MU_LAW: + if (snd_pcm_format_linear(slv->format)) { + cfmt = clt->format; + switch (clt->format) { + case SND_PCM_FORMAT_MU_LAW: f = snd_pcm_mulaw_open; break; - case SND_PCM_SFMT_A_LAW: + case SND_PCM_FORMAT_A_LAW: f = snd_pcm_alaw_open; break; - case SND_PCM_SFMT_IMA_ADPCM: + case SND_PCM_FORMAT_IMA_ADPCM: f = snd_pcm_adpcm_open; break; default: - assert(snd_pcm_format_linear(clt->sfmt)); + assert(snd_pcm_format_linear(clt->format)); f = snd_pcm_linear_open; break; } } else { - switch (slv->sfmt) { - case SND_PCM_SFMT_MU_LAW: + switch (slv->format) { + case SND_PCM_FORMAT_MU_LAW: f = snd_pcm_mulaw_open; break; - case SND_PCM_SFMT_A_LAW: + case SND_PCM_FORMAT_A_LAW: f = snd_pcm_alaw_open; break; - case SND_PCM_SFMT_IMA_ADPCM: + case SND_PCM_FORMAT_IMA_ADPCM: f = snd_pcm_adpcm_open; break; default: assert(0); return -EINVAL; } - if (snd_pcm_format_linear(clt->sfmt)) - cfmt = clt->sfmt; + if (snd_pcm_format_linear(clt->format)) + cfmt = clt->format; else - cfmt = SND_PCM_SFMT_S16; + cfmt = SND_PCM_FORMAT_S16; } - err = f(new, NULL, slv->sfmt, plug->slave, plug->slave != plug->req_slave); + err = f(new, NULL, slv->format, plug->slave, plug->slave != plug->req_slave); if (err < 0) return err; - slv->sfmt = cfmt; + slv->format = cfmt; return 1; } static int snd_pcm_plug_insert_plugins(snd_pcm_t *pcm, - snd_pcm_format_t *client_fmt, - snd_pcm_format_t *slave_fmt) + snd_pcm_hw_params_t *client, + snd_pcm_hw_params_t *slave) { snd_pcm_plug_t *plug = pcm->private; - int (*funcs[])(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_format_t *s, snd_pcm_format_t *d) = { + int (*funcs[])(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_hw_params_t *s, snd_pcm_hw_params_t *d) = { snd_pcm_plug_change_format, snd_pcm_plug_change_channels, snd_pcm_plug_change_rate, snd_pcm_plug_change_channels, snd_pcm_plug_change_format }; - snd_pcm_format_t sfmt = *slave_fmt; + snd_pcm_hw_params_t p = *slave; unsigned int k = 0; while (1) { snd_pcm_t *new; int err; - if (client_fmt->sfmt == sfmt.sfmt && - client_fmt->channels == sfmt.channels && - client_fmt->rate == sfmt.rate) + if (client->format == p.format && + client->channels == p.channels && + client->rate == p.rate) return 0; assert(k < sizeof(funcs)/sizeof(*funcs)); - err = funcs[k](pcm, &new, client_fmt, &sfmt); + err = funcs[k](pcm, &new, client, &p); if (err < 0) { snd_pcm_plug_clear(pcm); return err; @@ -493,134 +484,118 @@ static int snd_pcm_plug_insert_plugins(snd_pcm_t *pcm, return 0; } -static int snd_pcm_plug_params(snd_pcm_t *pcm, snd_pcm_params_t *params) +static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { snd_pcm_plug_t *plug = pcm->private; snd_pcm_t *slave = plug->req_slave; - snd_pcm_format_t *slave_format, *format; - snd_pcm_params_info_t slave_info; - int srate; + snd_pcm_hw_info_t sinfo; + snd_pcm_hw_params_t sparams; int err; - - memset(&slave_info, 0, sizeof(slave_info)); - err = snd_pcm_params_info(slave, &slave_info); - assert(err >= 0); - if (err < 0) + sparams = *params; + snd_pcm_hw_params_to_info(&sparams, &sinfo); + sinfo.access_mask = SND_PCM_ACCBIT_MMAP; + sinfo.format_mask = (SND_PCM_FMTBIT_LINEAR | SND_PCM_FMTBIT_MU_LAW | + SND_PCM_FMTBIT_A_LAW | SND_PCM_FMTBIT_IMA_ADPCM); + sinfo.subformat_mask = SND_PCM_SUBFMTBIT_STD; + sinfo.channels_min = 1; + sinfo.channels_max = 1024; + sinfo.rate_min = 4000; + sinfo.rate_max = 192000; + sinfo.fragment_size_min = 1; + sinfo.fragment_size_max = ULONG_MAX; + err = snd_pcm_hw_info_rulesv(slave, &sinfo, params, + SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_RATE, + SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_CHANNELS, + -1); + if (err < 0) { + snd_pcm_hw_info_to_params_fail(&sinfo, params); return err; - - slave_info.req = *params; - format = ¶ms->format; - slave_format = &slave_info.req.format; - - srate = snd_pcm_plug_slave_rate(format->rate, &slave_info); - if (srate < 0) { - params->fail_mask = SND_PCM_PARAMS_RATE; - params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL; - return srate; } - slave_format->rate = srate; - slave_info.req_mask |= SND_PCM_PARAMS_RATE; - err = snd_pcm_params_info(slave, &slave_info); - assert(err >= 0); + err = snd_pcm_plug_slave_fmt(sparams.format, sinfo.format_mask); if (err < 0) return err; - - if (slave_format->rate - slave_info.min_rate < slave_info.max_rate - slave_format->rate) - slave_format->rate = slave_info.min_rate; - else - slave_format->rate = slave_info.max_rate; - - if (format->channels < slave_info.min_channels) - slave_format->channels = slave_info.min_channels; - else if (format->channels > slave_info.max_channels) - slave_format->channels = slave_info.max_channels; - slave_info.req_mask |= SND_PCM_PARAMS_CHANNELS; - err = snd_pcm_params_info(slave, &slave_info); - assert(err >= 0); - if (err < 0) + sparams.format = err; + sinfo.format_mask = 1U << err; + sparams.fragment_size = muldiv_near(params->fragment_size, sparams.rate, params->rate); + err = snd_pcm_hw_info_rulesv(slave, &sinfo, &sparams, + SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_FRAGMENT_SIZE, + -1); + if (err < 0) { + snd_pcm_hw_info_to_params_fail(&sinfo, params); return err; - - if ((slave_info.formats & (1 << format->sfmt)) == 0) { - int slave_fmt = snd_pcm_plug_slave_fmt(format->sfmt, &slave_info); - if (slave_fmt < 0) { - params->fail_mask = SND_PCM_PARAMS_SFMT; - params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL; - return slave_fmt; - } - slave_format->sfmt = slave_fmt; - } - slave_info.req_mask |= SND_PCM_PARAMS_SFMT; - - if (slave_info.formats != 1U << slave_format->sfmt) { - err = snd_pcm_params_info(slave, &slave_info); - assert(err >= 0); - if (err < 0) - return err; } + if (sinfo.access_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) + sparams.access = SND_PCM_ACCESS_MMAP_INTERLEAVED; + else if (sinfo.access_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED) + sparams.access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; + else + assert(0); - err = snd_pcm_plug_insert_plugins(pcm, format, slave_format); + err = snd_pcm_plug_insert_plugins(pcm, params, &sparams); if (err < 0) return err; - err = snd_pcm_params(plug->slave, params); + err = snd_pcm_hw_params(plug->slave, params); if (err < 0) { snd_pcm_plug_clear(pcm); return err; } - assert(slave->setup.format.sfmt == slave_format->sfmt); - assert(slave->setup.format.channels == slave_format->channels); - assert(slave->setup.format.rate == slave_format->rate); pcm->hw_ptr = slave->hw_ptr; pcm->appl_ptr = slave->appl_ptr; return 0; } -static int snd_pcm_plug_setup(snd_pcm_t *pcm, snd_pcm_setup_t *setup) +static int snd_pcm_plug_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params) { snd_pcm_plug_t *plug = pcm->private; - return snd_pcm_setup(plug->slave, setup); + snd_pcm_t *slave = plug->req_slave; + size_t avail_min, xfer_min, xfer_align; + int err; + avail_min = params->avail_min; + xfer_min = params->xfer_min; + xfer_align = params->xfer_align; + params->avail_min = muldiv_near(params->avail_min, slave->rate, pcm->rate); + params->xfer_min = muldiv_near(params->xfer_min, slave->rate, pcm->rate); + params->xfer_align = muldiv_near(params->xfer_align, slave->rate, pcm->rate); + err = snd_pcm_sw_params(slave, params); + params->avail_min = avail_min; + params->xfer_min = xfer_min; + params->xfer_align = xfer_align; + params->boundary = LONG_MAX - pcm->buffer_size * 2 - LONG_MAX % pcm->buffer_size; + return err; } -static int snd_pcm_plug_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info) +static int snd_pcm_plug_dig_info(snd_pcm_t *pcm, snd_pcm_dig_info_t * info) { snd_pcm_plug_t *plug = pcm->private; - return snd_pcm_channel_info(plug->slave, info); + return snd_pcm_dig_info(plug->slave, info); } -static int snd_pcm_plug_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params) +static int snd_pcm_plug_dig_params(snd_pcm_t *pcm, snd_pcm_dig_params_t * params) { snd_pcm_plug_t *plug = pcm->private; - return snd_pcm_channel_params(plug->slave, params); + return snd_pcm_dig_params(plug->slave, params); } -static int snd_pcm_plug_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup) + +static int snd_pcm_plug_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info) { snd_pcm_plug_t *plug = pcm->private; - return snd_pcm_channel_setup(plug->slave, setup); + return snd_pcm_channel_info(plug->slave, info); } static int snd_pcm_plug_mmap(snd_pcm_t *pcm) { - snd_pcm_plug_t *plug = pcm->private; - int err = snd_pcm_mmap(plug->slave); - if (err < 0) - return err; - pcm->mmap_info_count = plug->slave->mmap_info_count; - pcm->mmap_info = plug->slave->mmap_info; - return 0; + snd_pcm_plugin_t *plug = pcm->private; + return snd_pcm_mmap(plug->slave); } static int snd_pcm_plug_munmap(snd_pcm_t *pcm) { - snd_pcm_plug_t *plug = pcm->private; - int err = snd_pcm_munmap(plug->slave); - if (err < 0) - return err; - pcm->mmap_info_count = 0; - pcm->mmap_info = 0; - return 0; + snd_pcm_plugin_t *plug = pcm->private; + return snd_pcm_munmap(plug->slave); } - + static void snd_pcm_plug_dump(snd_pcm_t *pcm, FILE *fp) { snd_pcm_plug_t *plug = pcm->private; @@ -631,12 +606,12 @@ static void snd_pcm_plug_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_plug_ops = { close: snd_pcm_plug_close, info: snd_pcm_plug_info, - params_info: snd_pcm_plug_params_info, - params: snd_pcm_plug_params, - setup: snd_pcm_plug_setup, + hw_info: snd_pcm_plug_hw_info, + hw_params: snd_pcm_plug_hw_params, + sw_params: snd_pcm_plug_sw_params, + dig_info: snd_pcm_plug_dig_info, + dig_params: snd_pcm_plug_dig_params, channel_info: snd_pcm_plug_channel_info, - channel_params: snd_pcm_plug_channel_params, - channel_setup: snd_pcm_plug_channel_setup, dump: snd_pcm_plug_dump, nonblock: snd_pcm_plug_nonblock, async: snd_pcm_plug_async, @@ -687,19 +662,14 @@ int snd_pcm_plug_open(snd_pcm_t **pcmp, return 0; } -int snd_pcm_plug_open_subdevice(snd_pcm_t **pcmp, int card, int device, int subdevice, int stream, int mode) +int snd_pcm_plug_open_hw(snd_pcm_t **pcmp, char *name, int card, int device, int subdevice, int stream, int mode) { snd_pcm_t *slave; int err; - err = snd_pcm_hw_open_subdevice(&slave, card, device, subdevice, stream, mode); + err = snd_pcm_hw_open(&slave, NULL, card, device, subdevice, stream, mode); if (err < 0) return err; - return snd_pcm_plug_open(pcmp, NULL, 0, 0, 0, 0, slave, 1); -} - -int snd_pcm_plug_open_device(snd_pcm_t **pcmp, int card, int device, int stream, int mode) -{ - return snd_pcm_plug_open_subdevice(pcmp, card, device, -1, stream, mode); + return snd_pcm_plug_open(pcmp, name, 0, 0, 0, 0, slave, 1); } #define MAX_CHANNELS 32 diff --git a/src/pcm/pcm_plugin.c b/src/pcm/pcm_plugin.c index 9929558f..9dd4e612 100644 --- a/src/pcm/pcm_plugin.c +++ b/src/pcm/pcm_plugin.c @@ -52,38 +52,28 @@ int snd_pcm_plugin_info(snd_pcm_t *pcm, snd_pcm_info_t * info) return snd_pcm_info(plugin->slave, info); } -int snd_pcm_plugin_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info) +int snd_pcm_plugin_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params) { snd_pcm_plugin_t *plugin = pcm->private; - return snd_pcm_channel_info(plugin->slave, info); + return snd_pcm_sw_params(plugin->slave, params); } -int snd_pcm_plugin_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params) +int snd_pcm_plugin_dig_info(snd_pcm_t *pcm, snd_pcm_dig_info_t *info) { snd_pcm_plugin_t *plugin = pcm->private; - return snd_pcm_channel_params(plugin->slave, params); + return snd_pcm_dig_info(plugin->slave, info); } -int snd_pcm_plugin_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup) +int snd_pcm_plugin_dig_params(snd_pcm_t *pcm, snd_pcm_dig_params_t *params) { snd_pcm_plugin_t *plugin = pcm->private; - int err; - err = snd_pcm_channel_setup(plugin->slave, setup); - if (err < 0) - return err; - if (!pcm->mmap_info) - return 0; - if (pcm->setup.mmap_shape == SND_PCM_MMAP_INTERLEAVED) { - setup->running_area.addr = pcm->mmap_info->addr; - setup->running_area.first = setup->channel * pcm->bits_per_sample; - setup->running_area.step = pcm->bits_per_frame; - } else { - setup->running_area.addr = pcm->mmap_info->addr + setup->channel * pcm->setup.buffer_size * pcm->bits_per_sample / 8; - setup->running_area.first = 0; - setup->running_area.step = pcm->bits_per_sample; - } - setup->stopped_area = setup->running_area; - return 0; + return snd_pcm_dig_params(plugin->slave, params); +} + +int snd_pcm_plugin_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info) +{ + snd_pcm_plugin_t *plugin = pcm->private; + return snd_pcm_channel_info_shm(pcm, info, plugin->shmid); } int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_status_t * status) @@ -185,7 +175,7 @@ ssize_t snd_pcm_plugin_rewind(snd_pcm_t *pcm, size_t frames) ssize_t snd_pcm_plugin_writei(snd_pcm_t *pcm, const void *buffer, size_t size) { snd_pcm_plugin_t *plugin = pcm->private; - snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + snd_pcm_channel_area_t areas[pcm->channels]; ssize_t frames; snd_pcm_areas_from_buf(pcm, areas, (void*)buffer); frames = snd_pcm_write_areas(pcm, areas, 0, size, plugin->write); @@ -197,7 +187,7 @@ ssize_t snd_pcm_plugin_writei(snd_pcm_t *pcm, const void *buffer, size_t size) ssize_t snd_pcm_plugin_writen(snd_pcm_t *pcm, void **bufs, size_t size) { snd_pcm_plugin_t *plugin = pcm->private; - snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + snd_pcm_channel_area_t areas[pcm->channels]; ssize_t frames; snd_pcm_areas_from_bufs(pcm, areas, bufs); frames = snd_pcm_write_areas(pcm, areas, 0, size, plugin->write); @@ -209,7 +199,7 @@ ssize_t snd_pcm_plugin_writen(snd_pcm_t *pcm, void **bufs, size_t size) ssize_t snd_pcm_plugin_readi(snd_pcm_t *pcm, void *buffer, size_t size) { snd_pcm_plugin_t *plugin = pcm->private; - snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + snd_pcm_channel_area_t areas[pcm->channels]; ssize_t frames; snd_pcm_areas_from_buf(pcm, areas, buffer); frames = snd_pcm_read_areas(pcm, areas, 0, size, plugin->read); @@ -221,7 +211,7 @@ ssize_t snd_pcm_plugin_readi(snd_pcm_t *pcm, void *buffer, size_t size) ssize_t snd_pcm_plugin_readn(snd_pcm_t *pcm, void **bufs, size_t size) { snd_pcm_plugin_t *plugin = pcm->private; - snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + snd_pcm_channel_area_t areas[pcm->channels]; ssize_t frames; snd_pcm_areas_from_bufs(pcm, areas, bufs); frames = snd_pcm_read_areas(pcm, areas, 0, size, plugin->read); @@ -251,7 +241,7 @@ ssize_t snd_pcm_plugin_mmap_forward(snd_pcm_t *pcm, size_t client_size) size_t slave_frames = slave_size - slave_xfer; size_t client_frames = client_size - client_xfer; size_t offset = snd_pcm_mmap_hw_offset(pcm); - size_t cont = pcm->setup.buffer_size - offset; + size_t cont = pcm->buffer_size - offset; if (cont < client_frames) client_frames = cont; err = plugin->write(pcm, pcm->running_areas, offset, @@ -279,17 +269,18 @@ ssize_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm) if (slave_size <= 0) return slave_size; if (pcm->stream == SND_PCM_STREAM_PLAYBACK || - !pcm->mmap_info) + pcm->access == SND_PCM_ACCESS_RW_INTERLEAVED || + pcm->access == SND_PCM_ACCESS_RW_NONINTERLEAVED) return plugin->client_frames ? plugin->client_frames(pcm, slave_size) : slave_size; client_xfer = snd_pcm_mmap_capture_avail(pcm); - client_size = pcm->setup.buffer_size; + client_size = pcm->buffer_size; while (slave_xfer < (size_t)slave_size && client_xfer < client_size) { size_t slave_frames = slave_size - slave_xfer; size_t client_frames = client_size - client_xfer; size_t offset = snd_pcm_mmap_hw_offset(pcm); - size_t cont = pcm->setup.buffer_size - offset; + size_t cont = pcm->buffer_size - offset; if (cont < client_frames) client_frames = cont; err = plugin->read(pcm, pcm->running_areas, offset, @@ -313,38 +304,26 @@ int snd_pcm_plugin_set_avail_min(snd_pcm_t *pcm, size_t frames) int snd_pcm_plugin_mmap(snd_pcm_t *pcm) { - snd_pcm_plugin_t *plugin = pcm->private; - snd_pcm_t *slave = plugin->slave; - snd_pcm_mmap_info_t *i; - int err = snd_pcm_mmap(slave); - if (err < 0) - return err; - i = calloc(1, sizeof(*i)); - if (!i) - return -ENOMEM; - err = snd_pcm_alloc_user_mmap(pcm, i); - if (err < 0) { - free(i); - return err; + snd_pcm_plugin_t *plug = pcm->private; + if (!(pcm->info & SND_PCM_INFO_MMAP)) { + size_t size = snd_pcm_frames_to_bytes(pcm, pcm->buffer_size); + int id = shmget(IPC_PRIVATE, size, 0666); + if (id < 0) { + SYSERR("shmget failed"); + return -errno; + } + plug->shmid = id; } - pcm->mmap_info = i; - pcm->mmap_info_count = 1; return 0; } int snd_pcm_plugin_munmap(snd_pcm_t *pcm) { - snd_pcm_plugin_t *plugin = pcm->private; - snd_pcm_t *slave = plugin->slave; - int err = snd_pcm_munmap(slave); - if (err < 0) - return err; - err = snd_pcm_free_mmap(pcm, pcm->mmap_info); - if (err < 0) - return err; - free(pcm->mmap_info); - pcm->mmap_info_count = 0; - pcm->mmap_info = 0; + snd_pcm_plugin_t *plug = pcm->private; + if (shmctl(plug->shmid, IPC_RMID, 0) < 0) { + SYSERR("shmctl IPC_RMID failed"); + return -errno; + } return 0; } diff --git a/src/pcm/pcm_plugin.h b/src/pcm/pcm_plugin.h index b0275fd8..4047ffb7 100644 --- a/src/pcm/pcm_plugin.h +++ b/src/pcm/pcm_plugin.h @@ -24,18 +24,21 @@ typedef struct { int close_slave; snd_pcm_xfer_areas_func_t read; snd_pcm_xfer_areas_func_t write; - size_t (*client_frames)(snd_pcm_t *pcm, size_t frames); + ssize_t (*client_frames)(snd_pcm_t *pcm, ssize_t frames); int (*init)(snd_pcm_t *pcm); + int shmid; size_t appl_ptr, hw_ptr; + unsigned int saccess_mask; } snd_pcm_plugin_t; int snd_pcm_plugin_close(snd_pcm_t *pcm); int snd_pcm_plugin_nonblock(snd_pcm_t *pcm, int nonblock); int snd_pcm_plugin_async(snd_pcm_t *pcm, int sig, pid_t pid); int snd_pcm_plugin_info(snd_pcm_t *pcm, snd_pcm_info_t * info); +int snd_pcm_plugin_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params); +int snd_pcm_plugin_dig_info(snd_pcm_t *pcm, snd_pcm_dig_info_t *info); +int snd_pcm_plugin_dig_params(snd_pcm_t *pcm, snd_pcm_dig_params_t *params); int snd_pcm_plugin_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info); -int snd_pcm_plugin_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params); -int snd_pcm_plugin_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup); int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_status_t * status); int snd_pcm_plugin_state(snd_pcm_t *pcm); int snd_pcm_plugin_delay(snd_pcm_t *pcm, ssize_t *delayp); @@ -58,22 +61,34 @@ int snd_pcm_plugin_munmap_status(snd_pcm_t *pcm); int snd_pcm_plugin_munmap_control(snd_pcm_t *pcm); int snd_pcm_plugin_munmap(snd_pcm_t *pcm); int snd_pcm_plugin_poll_descriptor(snd_pcm_t *pcm); -int snd_pcm_plugin_channels_mask(snd_pcm_t *pcm, bitset_t *cmask); int get_index(int src_format, int dst_format); int put_index(int src_format, int dst_format); int conv_index(int src_format, int dst_format); -#define SND_PCM_LINEAR_FORMATS (SND_PCM_FMT_S8 | SND_PCM_FMT_U8 | \ - SND_PCM_FMT_S16_LE | SND_PCM_FMT_S16_BE | \ - SND_PCM_FMT_U16_LE | SND_PCM_FMT_U16_BE | \ - SND_PCM_FMT_S24_LE | SND_PCM_FMT_S24_BE | \ - SND_PCM_FMT_U24_LE | SND_PCM_FMT_U24_BE | \ - SND_PCM_FMT_S32_LE | SND_PCM_FMT_S32_BE | \ - SND_PCM_FMT_U32_LE | SND_PCM_FMT_U32_BE) +#define SND_PCM_FMTBIT_LINEAR (SND_PCM_FMTBIT_S8 |SND_PCM_FMTBIT_U8 | \ + SND_PCM_FMTBIT_S16_LE|SND_PCM_FMTBIT_S16_BE | \ + SND_PCM_FMTBIT_U16_LE|SND_PCM_FMTBIT_U16_BE | \ + SND_PCM_FMTBIT_S24_LE|SND_PCM_FMTBIT_S24_BE | \ + SND_PCM_FMTBIT_U24_LE|SND_PCM_FMTBIT_U24_BE | \ + SND_PCM_FMTBIT_S32_LE|SND_PCM_FMTBIT_S32_BE | \ + SND_PCM_FMTBIT_U32_LE|SND_PCM_FMTBIT_U32_BE) extern snd_pcm_fast_ops_t snd_pcm_plugin_fast_ops; -#define muldiv64(a,b,d) (((int64_t)(a) * (b) + (b) / 2) / (d)) +static inline ssize_t muldiv_down(ssize_t a, ssize_t b, ssize_t d) +{ + return (int64_t) (a * b) / d; +} + +static inline ssize_t muldiv_up(ssize_t a, ssize_t b, ssize_t d) +{ + return (int64_t) (a * b + (d - 1)) / d; +} + +static inline ssize_t muldiv_near(ssize_t a, ssize_t b, ssize_t d) +{ + return (int64_t) (a * b + (d / 2)) / d; +} #define ROUTE_PLUGIN_FLOAT 1 #define ROUTE_PLUGIN_RESOLUTION 16 diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c index 8893f268..8684ccec 100644 --- a/src/pcm/pcm_rate.c +++ b/src/pcm/pcm_rate.c @@ -48,13 +48,8 @@ typedef struct { int put_idx; unsigned int pitch; rate_f func; - int req_sformat; - int req_srate; int sformat; - int cformat; int srate; - int crate; - int cxfer_mode, cmmap_shape; rate_state_t *states; } snd_pcm_rate_t; @@ -93,7 +88,7 @@ static size_t resample_expand(snd_pcm_channel_area_t *src_areas, #if 0 if (!src_area->enabled) { if (dst_area->wanted) - snd_pcm_area_silence(&dst_area->area, 0, dst_frames, plugin->dst_format.sfmt); + snd_pcm_area_silence(&dst_area->area, 0, dst_frames, plugin->dst_format); dst_area->enabled = 0; continue; } @@ -177,7 +172,7 @@ static size_t resample_shrink(snd_pcm_channel_area_t *src_areas, #if 0 if (!src_area->enabled) { if (dst_area->wanted) - snd_pcm_area_silence(&dst_area->area, 0, dst_frames, plugin->dst_format.sfmt); + snd_pcm_area_silence(&dst_area->area, 0, dst_frames, plugin->dst_format); dst_area->enabled = 0; continue; } @@ -238,142 +233,132 @@ static int snd_pcm_rate_close(snd_pcm_t *pcm) return 0; } -static int snd_pcm_rate_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info) +static int snd_pcm_rate_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info) { snd_pcm_rate_t *rate = pcm->private; - unsigned int req_mask = info->req_mask; - unsigned int sfmt = info->req.format.sfmt; - unsigned int crate = info->req.format.rate; - unsigned int srate; + snd_pcm_hw_info_t sinfo; + unsigned int access_mask; + size_t fragment_size_min, fragment_size_max; int err; - if (req_mask & SND_PCM_PARAMS_SFMT && - !snd_pcm_format_linear(sfmt)) { - info->req.fail_mask = SND_PCM_PARAMS_SFMT; - info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL; + info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | + SND_PCM_ACCBIT_RW_INTERLEAVED | + SND_PCM_ACCBIT_MMAP_NONINTERLEAVED | + SND_PCM_ACCBIT_RW_NONINTERLEAVED); + access_mask = info->access_mask; + if (access_mask == 0) return -EINVAL; + info->format_mask &= SND_PCM_FMTBIT_LINEAR; + if (info->format_mask == 0) + return -EINVAL; + if (info->rate_min < 4000) + info->rate_min = 4000; + if (info->rate_max > 192000) + info->rate_max = 192000; + if (info->rate_max < info->rate_min) + return -EINVAL; + if (info->fragment_size_max > 1024 * 1024) + info->fragment_size_max = 1024 * 1024; + if (info->fragment_size_max < info->fragment_size_min) + return -EINVAL; + sinfo = *info; + + sinfo.rate_min = rate->srate; + sinfo.rate_max = rate->srate; + if (rate->sformat >= 0) + sinfo.format_mask = 1U << rate->sformat; + sinfo.fragment_size_min = muldiv_down(info->fragment_size_min, sinfo.rate_min, info->rate_max); + sinfo.fragment_size_max = muldiv_up(info->fragment_size_max, sinfo.rate_max, info->rate_min); + + sinfo.access_mask = SND_PCM_ACCBIT_MMAP; + err = snd_pcm_hw_info(rate->plug.slave, &sinfo); + info->subformat_mask = sinfo.subformat_mask; + info->channels_min = sinfo.channels_min; + info->channels_max = sinfo.channels_max; + info->fragments_min = sinfo.fragments_min; + info->fragments_max = sinfo.fragments_max; + + if (!sinfo.access_mask) { + info->access_mask = 0; } - if (rate->req_sformat >= 0) { - info->req_mask |= SND_PCM_PARAMS_SFMT; - info->req.format.sfmt = rate->req_sformat; + if (!sinfo.format_mask) { + info->format_mask = 0; + } + if (sinfo.rate_min > sinfo.rate_max) { + info->rate_min = UINT_MAX; + info->rate_max = 0; + } + if (sinfo.fragment_size_min > sinfo.fragment_size_max) { + info->fragment_size_min = ULONG_MAX; + info->fragment_size_max = 0; } - info->req_mask |= SND_PCM_PARAMS_RATE; - info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE | - SND_PCM_PARAMS_XFER_MODE); - info->req.format.rate = rate->req_srate; - err = snd_pcm_params_info(rate->plug.slave, info); - info->req_mask = req_mask; - info->req.format.sfmt = sfmt; - info->req.format.rate = crate; if (err < 0) return err; - if (req_mask & SND_PCM_PARAMS_SFMT) - info->formats = 1 << sfmt; - else - info->formats = SND_PCM_LINEAR_FORMATS; - if (!(req_mask & SND_PCM_PARAMS_RATE)) { - info->min_rate = 4000; - info->max_rate = 192000; - return 0; - } - if (rate->req_srate - info->min_rate < info->max_rate - rate->req_srate) - srate = info->min_rate; - else - srate = info->max_rate; - info->min_rate = crate; - info->max_rate = crate; - if (info->buffer_size) - info->buffer_size = muldiv64(info->buffer_size, crate, srate); - if (info->min_fragment_size) - info->min_fragment_size = muldiv64(info->min_fragment_size, crate, srate); - if (info->max_fragment_size) - info->max_fragment_size = muldiv64(info->max_fragment_size, crate, srate); - if (info->fragment_align) - info->fragment_align = muldiv64(info->fragment_align, crate, srate); - info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); - info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED; + + fragment_size_min = muldiv_down(sinfo.fragment_size_min, info->rate_min, sinfo.rate_max); + fragment_size_max = muldiv_up(sinfo.fragment_size_max, info->rate_max, sinfo.rate_min); + if (fragment_size_min > info->fragment_size_min) + info->fragment_size_min = fragment_size_min; + if (fragment_size_max < info->fragment_size_max) + info->fragment_size_max = fragment_size_max; + rate->plug.saccess_mask = sinfo.access_mask; + info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); + snd_pcm_hw_info_complete(info); return 0; } -static int snd_pcm_rate_params(snd_pcm_t *pcm, snd_pcm_params_t * params) +static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) { snd_pcm_rate_t *rate = pcm->private; snd_pcm_t *slave = rate->plug.slave; - snd_pcm_params_t slave_params; - snd_pcm_params_info_t slave_info; - int srate, crate; + snd_pcm_hw_info_t sinfo; + unsigned int format, access, crate; + unsigned int src_format, dst_format; + unsigned int src_rate, dst_rate; + size_t fragment_size; + int mul, div; int err; - if (!snd_pcm_format_linear(params->format.sfmt)) { - params->fail_mask = SND_PCM_PARAMS_SFMT; - params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL; - return -EINVAL; - } - slave_params = *params; - rate->cformat = params->format.sfmt; - rate->crate = crate = params->format.rate; - rate->cxfer_mode = params->xfer_mode; - rate->cmmap_shape = params->mmap_shape; - - memset(&slave_info, 0, sizeof(slave_info)); - slave_info.req = *params; - if (rate->req_sformat >= 0) { - slave_info.req.format.sfmt = rate->req_sformat; - slave_params.format.sfmt = rate->req_sformat; - } - slave_info.req.format.rate = rate->req_srate; - slave_info.req_mask = ~0; - err = snd_pcm_params_info(slave, &slave_info); - if (err < 0) { - params->fail_mask = slave_info.req.fail_mask; - params->fail_reason = slave_info.req.fail_reason; - return err; - } - - if (rate->req_srate - slave_info.min_rate < slave_info.max_rate - rate->req_srate) - srate = slave_info.min_rate; + crate = params->rate; + format = params->format; + fragment_size = params->fragment_size; + access = params->access; + params->rate = rate->srate; + if (rate->sformat >= 0) + params->format = rate->sformat; + if (rate->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) + params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED; + else if (rate->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED) + params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; else - srate = slave_info.max_rate; - - slave_params.format.rate = srate; - slave_params.avail_min = muldiv64(params->avail_min, srate, crate); - slave_params.xfer_min = muldiv64(params->xfer_min, srate, crate); - slave_params.buffer_size = muldiv64(params->buffer_size, srate, crate); - slave_params.frag_size = muldiv64(params->frag_size, srate, crate); - slave_params.xfer_align = muldiv64(params->xfer_align, srate, crate); - /* FIXME: boundary? */ - slave_params.xfer_mode = SND_PCM_XFER_UNSPECIFIED; - slave_params.mmap_shape = SND_PCM_MMAP_UNSPECIFIED; - err = snd_pcm_params_mmap(slave, &slave_params); - params->fail_mask = slave_params.fail_mask; - params->fail_reason = slave_params.fail_reason; - return err; -} - -static int snd_pcm_rate_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup) -{ - snd_pcm_rate_t *rate = pcm->private; - int src_format, dst_format; - int src_rate, dst_rate; - int mul, div; - int err = snd_pcm_setup(rate->plug.slave, setup); + assert(0); + params->fragment_size = muldiv_near(params->fragment_size, params->rate, crate); + snd_pcm_hw_params_to_info(params, &sinfo); + sinfo.fragment_size_min = 0; + sinfo.fragment_size_max = ULONG_MAX; + err = snd_pcm_hw_info_rulesv(slave, &sinfo, params, + SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_FRAGMENT_SIZE, + -1); + snd_pcm_hw_info_to_params(&sinfo, params); + if (err >= 0) + err = snd_pcm_hw_params(slave, params); + params->format = format; + params->rate = crate; + params->access = access; + params->fragment_size = fragment_size; if (err < 0) return err; - if (rate->req_sformat >= 0) - assert(rate->req_sformat == setup->format.sfmt); - rate->sformat = setup->format.sfmt; - rate->srate = setup->format.rate; if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { - src_format = rate->cformat; - dst_format = rate->sformat; - src_rate = rate->crate; - dst_rate = rate->srate; + src_format = format; + dst_format = slave->format; + src_rate = crate; + dst_rate = slave->rate; } else { - src_format = rate->sformat; - dst_format = rate->cformat; - src_rate = rate->srate; - dst_rate = rate->crate; + src_format = slave->format; + dst_format = format; + src_rate = slave->rate; + dst_rate = crate; } - rate->get_idx = get_index(src_format, SND_PCM_SFMT_S16); - rate->put_idx = put_index(SND_PCM_SFMT_S16, dst_format); + rate->get_idx = get_index(src_format, SND_PCM_FORMAT_S16); + rate->put_idx = put_index(SND_PCM_FORMAT_S16, dst_format); if (src_rate < dst_rate) { rate->func = resample_expand; /* pitch is get_threshold */ @@ -389,43 +374,37 @@ static int snd_pcm_rate_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup) mul = rate->pitch; div = DIV; } - rate->crate = muldiv64(rate->srate, mul, div); - if (rate->cxfer_mode == SND_PCM_XFER_UNSPECIFIED) - setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED; - else - setup->xfer_mode = rate->cxfer_mode; - if (rate->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED) - setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED; - else - setup->mmap_shape = rate->cmmap_shape; - setup->format.sfmt = rate->cformat; - setup->format.rate = rate->crate; - /* FIXME */ - setup->rate_master = rate->crate; - setup->rate_divisor = 1; - setup->mmap_bytes = 0; - setup->avail_min = muldiv64(setup->avail_min, mul, div); - setup->xfer_min = muldiv64(setup->xfer_min, mul, div); - - /* FIXME: the three above are not a lot sensible */ - setup->buffer_size = muldiv64(setup->buffer_size, mul, div); - setup->frag_size = muldiv64(setup->frag_size, mul, div); - setup->xfer_align = muldiv64(setup->xfer_align, mul, div); - - /* FIXME */ - setup->boundary = LONG_MAX - LONG_MAX % setup->buffer_size; - if (rate->states) free(rate->states); - rate->states = malloc(setup->format.channels * sizeof(*rate->states)); + rate->states = malloc(params->channels * sizeof(*rate->states)); return 0; } +static int snd_pcm_rate_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params) +{ + snd_pcm_rate_t *rate = pcm->private; + snd_pcm_t *slave = rate->plug.slave; + size_t avail_min, xfer_min, xfer_align; + int err; + avail_min = params->avail_min; + xfer_min = params->xfer_min; + xfer_align = params->xfer_align; + params->avail_min = muldiv_near(params->avail_min, slave->rate, pcm->rate); + params->xfer_min = muldiv_near(params->xfer_min, slave->rate, pcm->rate); + params->xfer_align = muldiv_near(params->xfer_align, slave->rate, pcm->rate); + err = snd_pcm_sw_params(slave, params); + params->avail_min = avail_min; + params->xfer_min = xfer_min; + params->xfer_align = xfer_align; + params->boundary = LONG_MAX - pcm->buffer_size * 2 - LONG_MAX % pcm->buffer_size; + return err; +} + static int snd_pcm_rate_init(snd_pcm_t *pcm) { snd_pcm_rate_t *rate = pcm->private; unsigned int k; - for (k = 0; k < pcm->setup.format.channels; ++k) { + for (k = 0; k < pcm->channels; ++k) { rate->states[k].sum = 0; rate->states[k].sample = 0; if (rate->func == resample_expand) { @@ -463,7 +442,7 @@ static ssize_t snd_pcm_rate_write_areas(snd_pcm_t *pcm, src_frames = rate->func(areas, client_offset, src_frames, snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave), &dst_frames, - pcm->setup.format.channels, + pcm->channels, rate->get_idx, rate->put_idx, rate->pitch, rate->states); err = snd_pcm_mmap_forward(slave, dst_frames); @@ -509,7 +488,7 @@ static ssize_t snd_pcm_rate_read_areas(snd_pcm_t *pcm, src_frames = rate->func(snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave), src_frames, areas, client_offset, &dst_frames, - pcm->setup.format.channels, + pcm->channels, rate->get_idx, rate->put_idx, rate->pitch, rate->states); err = snd_pcm_mmap_forward(slave, src_frames); @@ -529,27 +508,27 @@ static ssize_t snd_pcm_rate_read_areas(snd_pcm_t *pcm, return err; } -size_t snd_pcm_rate_client_frames(snd_pcm_t *pcm, size_t frames) +ssize_t snd_pcm_rate_client_frames(snd_pcm_t *pcm, ssize_t frames) { snd_pcm_rate_t *rate = pcm->private; /* Round toward zero */ if (pcm->stream == SND_PCM_STREAM_PLAYBACK) - return (int64_t)frames * DIV / rate->pitch; + return muldiv_down(frames, DIV, rate->pitch); else - return (int64_t)frames * rate->pitch / DIV; + return muldiv_down(frames, rate->pitch, DIV); } static void snd_pcm_rate_dump(snd_pcm_t *pcm, FILE *fp) { snd_pcm_rate_t *rate = pcm->private; - if (rate->req_sformat < 0) + if (rate->sformat < 0) fprintf(fp, "Rate conversion PCM (%d)\n", - rate->req_srate); + rate->srate); else fprintf(fp, "Rate conversion PCM (%d, sformat=%s)\n", - rate->req_srate, - snd_pcm_format_name(rate->req_sformat)); - if (pcm->valid_setup) { + rate->srate, + snd_pcm_format_name(rate->sformat)); + if (pcm->setup) { fprintf(fp, "Its setup is:\n"); snd_pcm_dump_setup(pcm, fp); } @@ -560,12 +539,12 @@ static void snd_pcm_rate_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_rate_ops = { close: snd_pcm_rate_close, info: snd_pcm_plugin_info, - params_info: snd_pcm_rate_params_info, - params: snd_pcm_rate_params, - setup: snd_pcm_rate_setup, + hw_info: snd_pcm_rate_hw_info, + hw_params: snd_pcm_rate_hw_params, + sw_params: snd_pcm_rate_sw_params, + dig_info: snd_pcm_plugin_dig_info, + dig_params: snd_pcm_plugin_dig_params, channel_info: snd_pcm_plugin_channel_info, - channel_params: snd_pcm_plugin_channel_params, - channel_setup: snd_pcm_plugin_channel_setup, dump: snd_pcm_rate_dump, nonblock: snd_pcm_plugin_nonblock, async: snd_pcm_plugin_async, @@ -584,8 +563,8 @@ int snd_pcm_rate_open(snd_pcm_t **pcmp, char *name, int sformat, int srate, snd_ if (!rate) { return -ENOMEM; } - rate->req_srate = srate; - rate->req_sformat = sformat; + rate->srate = srate; + rate->sformat = sformat; rate->plug.read = snd_pcm_rate_read_areas; rate->plug.write = snd_pcm_rate_write_areas; rate->plug.client_frames = snd_pcm_rate_client_frames; diff --git a/src/pcm/pcm_route.c b/src/pcm/pcm_route.c index 4e841553..75a4b106 100644 --- a/src/pcm/pcm_route.c +++ b/src/pcm/pcm_route.c @@ -81,12 +81,8 @@ typedef union { typedef struct { /* This field need to be the first */ snd_pcm_plugin_t plug; - int req_sformat, req_schannels; int sformat; - int cformat; int schannels; - int cchannels; - int cxfer_mode, cmmap_shape; route_params_t params; } snd_pcm_route_t; @@ -429,110 +425,91 @@ static int snd_pcm_route_close(snd_pcm_t *pcm) return 0; } -static int snd_pcm_route_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info) +static int snd_pcm_route_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info) { snd_pcm_route_t *route = pcm->private; - unsigned int req_mask = info->req_mask; - unsigned int sfmt = info->req.format.sfmt; - unsigned int channels = info->req.format.channels; + unsigned int format_mask, access_mask, channels_min, channels_max; int err; - if (req_mask & SND_PCM_PARAMS_SFMT && - !snd_pcm_format_linear(sfmt)) { - info->req.fail_mask = SND_PCM_PARAMS_SFMT; - info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL; + info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | + SND_PCM_ACCBIT_RW_INTERLEAVED | + SND_PCM_ACCBIT_MMAP_NONINTERLEAVED | + SND_PCM_ACCBIT_RW_NONINTERLEAVED); + access_mask = info->access_mask; + if (access_mask == 0) return -EINVAL; + info->format_mask &= SND_PCM_FMTBIT_LINEAR; + format_mask = info->format_mask; + if (format_mask == 0) + return -EINVAL; + if (info->channels_min < 1) + info->channels_min = 1; + if (info->channels_max > 1024) + info->channels_max = 1024; + if (info->channels_max < info->channels_min) + return -EINVAL; + channels_min = info->channels_min; + channels_max = info->channels_max; + if (route->sformat >= 0) + info->format_mask = 1U << route->sformat; + if (route->schannels >= 0) + info->channels_min = info->channels_max = route->schannels; + + info->access_mask = SND_PCM_ACCBIT_MMAP; + err = snd_pcm_hw_info(route->plug.slave, info); + if (info->format_mask) + info->format_mask = format_mask; + if (info->channels_min <= info->channels_max) { + info->channels_min = channels_min; + info->channels_max = channels_max; } - if (route->req_sformat >= 0) { - info->req_mask |= SND_PCM_PARAMS_SFMT; - info->req.format.sfmt = route->req_sformat; - } - if (route->req_schannels >= 0) { - info->req_mask |= SND_PCM_PARAMS_CHANNELS; - info->req.format.channels = route->req_schannels; + if (info->access_mask) { + route->plug.saccess_mask = info->access_mask; + info->access_mask = access_mask; } - info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE | - SND_PCM_PARAMS_XFER_MODE); - err = snd_pcm_params_info(route->plug.slave, info); - info->req_mask = req_mask; - info->req.format.sfmt = sfmt; - info->req.format.channels = channels; + if (info->format_mask) + info->format_mask = format_mask; if (err < 0) return err; - if (req_mask & SND_PCM_PARAMS_SFMT) - info->formats = 1 << sfmt; - else - info->formats = SND_PCM_LINEAR_FORMATS; - if (req_mask & SND_PCM_PARAMS_CHANNELS) { - info->min_channels = channels; - info->max_channels = channels; - } else { - info->min_channels = 1; - info->max_channels = 1024; - } - info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); - info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED; - return err; + info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); + snd_pcm_hw_info_complete(info); + return 0; } -static int snd_pcm_route_params(snd_pcm_t *pcm, snd_pcm_params_t * params) +static int snd_pcm_route_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) { snd_pcm_route_t *route = pcm->private; snd_pcm_t *slave = route->plug.slave; + unsigned int format, access, channels; + unsigned int src_format, dst_format; int err; - if (!snd_pcm_format_linear(params->format.sfmt)) { - params->fail_mask = SND_PCM_PARAMS_SFMT; - params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL; - return -EINVAL; - } - route->cformat = params->format.sfmt; - route->cchannels = params->format.channels; - route->cxfer_mode = params->xfer_mode; - route->cmmap_shape = params->mmap_shape; - if (route->req_sformat >= 0) - params->format.sfmt = route->req_sformat; - if (route->req_schannels >= 0) - params->format.channels = route->req_schannels; - params->xfer_mode = SND_PCM_XFER_UNSPECIFIED; - params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED; - err = snd_pcm_params_mmap(slave, params); - params->format.sfmt = route->cformat; - params->format.channels = route->cchannels; - params->xfer_mode = route->cxfer_mode; - params->mmap_shape = route->cmmap_shape; - return err; -} - -static int snd_pcm_route_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup) -{ - snd_pcm_route_t *route = pcm->private; - int src_format, dst_format; - int err = snd_pcm_setup(route->plug.slave, setup); + format = params->format; + channels = params->channels; + access = params->access; + if (route->sformat >= 0) + params->format = route->sformat; + if (route->schannels >= 0) + params->channels = route->schannels; + if (route->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) + params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED; + else if (route->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED) + params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; + else + assert(0); + err = snd_pcm_hw_params(slave, params); + params->format = format; + params->channels = channels; + params->access = access; if (err < 0) return err; - if (route->req_sformat >= 0) - assert(route->req_sformat == setup->format.sfmt); - route->sformat = setup->format.sfmt; - route->schannels = setup->format.channels; - if (route->cxfer_mode == SND_PCM_XFER_UNSPECIFIED) - setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED; - else - setup->xfer_mode = route->cxfer_mode; - if (route->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED) - setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED; - else - setup->mmap_shape = route->cmmap_shape; - setup->format.sfmt = route->cformat; - setup->format.channels = route->cchannels; - setup->mmap_bytes = 0; if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { - src_format = route->cformat; - dst_format = route->sformat; + src_format = format; + dst_format = slave->format; } else { - src_format = route->sformat; - dst_format = route->cformat; + src_format = slave->format; + dst_format = format; } - route->params.get_idx = get_index(src_format, SND_PCM_SFMT_U16); - route->params.put_idx = put_index(SND_PCM_SFMT_U32, dst_format); + route->params.get_idx = get_index(src_format, SND_PCM_FORMAT_U16); + route->params.put_idx = put_index(SND_PCM_FORMAT_U32, dst_format); route->params.conv_idx = conv_index(src_format, dst_format); route->params.src_size = snd_pcm_format_width(src_format) / 8; route->params.dst_sfmt = dst_format; @@ -547,30 +524,6 @@ static int snd_pcm_route_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup) return 0; } -static int snd_pcm_route_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup) -{ -#if 0 - snd_pcm_plugin_t *plugin = pcm->private; - int err; - err = snd_pcm_channel_setup(plugin->slave, setup); - if (err < 0) - return err; -#endif - if (!pcm->mmap_info) - return 0; - if (pcm->setup.mmap_shape == SND_PCM_MMAP_INTERLEAVED) { - setup->running_area.addr = pcm->mmap_info->addr; - setup->running_area.first = setup->channel * pcm->bits_per_sample; - setup->running_area.step = pcm->bits_per_frame; - } else { - setup->running_area.addr = pcm->mmap_info->addr + setup->channel * pcm->setup.buffer_size * pcm->bits_per_sample / 8; - setup->running_area.first = 0; - setup->running_area.step = pcm->bits_per_sample; - } - setup->stopped_area = setup->running_area; - return 0; -} - static ssize_t snd_pcm_route_write_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, size_t offset, @@ -588,7 +541,7 @@ static ssize_t snd_pcm_route_write_areas(snd_pcm_t *pcm, size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer); route_transfer(areas, offset, snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave), - frames, route->schannels, &route->params); + frames, slave->channels, &route->params); err = snd_pcm_mmap_forward(slave, frames); if (err < 0) break; @@ -622,7 +575,7 @@ static ssize_t snd_pcm_route_read_areas(snd_pcm_t *pcm, size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer); route_transfer(snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave), areas, offset, - frames, route->cchannels, &route->params); + frames, pcm->channels, &route->params); err = snd_pcm_mmap_forward(slave, frames); if (err < 0) break; @@ -643,11 +596,11 @@ static void snd_pcm_route_dump(snd_pcm_t *pcm, FILE *fp) { snd_pcm_route_t *route = pcm->private; unsigned int dst; - if (route->req_sformat < 0) + if (route->sformat < 0) fprintf(fp, "Route conversion PCM\n"); else fprintf(fp, "Route conversion PCM (sformat=%s)\n", - snd_pcm_format_name(route->req_sformat)); + snd_pcm_format_name(route->sformat)); fputs("Transformation table:\n", fp); for (dst = 0; dst < route->params.ndsts; dst++) { ttable_dst_t *d = &route->params.dsts[dst]; @@ -669,7 +622,7 @@ static void snd_pcm_route_dump(snd_pcm_t *pcm, FILE *fp) } putc('\n', fp); } - if (pcm->valid_setup) { + if (pcm->setup) { fprintf(fp, "Its setup is:\n"); snd_pcm_dump_setup(pcm, fp); } @@ -680,12 +633,12 @@ static void snd_pcm_route_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_route_ops = { close: snd_pcm_route_close, info: snd_pcm_plugin_info, - params_info: snd_pcm_route_params_info, - params: snd_pcm_route_params, - setup: snd_pcm_route_setup, + hw_info: snd_pcm_route_hw_info, + hw_params: snd_pcm_route_hw_params, + sw_params: snd_pcm_plugin_sw_params, + dig_info: snd_pcm_plugin_dig_info, + dig_params: snd_pcm_plugin_dig_params, channel_info: snd_pcm_plugin_channel_info, - channel_params: snd_pcm_plugin_channel_params, - channel_setup: snd_pcm_route_channel_setup, dump: snd_pcm_route_dump, nonblock: snd_pcm_plugin_nonblock, async: snd_pcm_plugin_async, @@ -782,8 +735,8 @@ int snd_pcm_route_open(snd_pcm_t **pcmp, char *name, if (!route) { return -ENOMEM; } - route->req_sformat = sformat; - route->req_schannels = schannels; + route->sformat = sformat; + route->schannels = schannels; route->plug.read = snd_pcm_route_read_areas; route->plug.write = snd_pcm_route_write_areas; route->plug.slave = slave; diff --git a/src/pcm/pcm_share.c b/src/pcm/pcm_share.c index 85edc923..898f8647 100644 --- a/src/pcm/pcm_share.c +++ b/src/pcm/pcm_share.c @@ -21,6 +21,7 @@ #include <stdio.h> #include <stdlib.h> +#include <limits.h> #include <unistd.h> #include <string.h> #include <signal.h> @@ -66,8 +67,8 @@ typedef struct { struct list_head clients; struct list_head list; snd_pcm_t *pcm; - int sformat; - int srate; + int format; + int rate; size_t channels_count; size_t open_count; size_t setup_count; @@ -104,7 +105,6 @@ typedef struct { int ready; int client_socket; int slave_socket; - void *stopped_data; } snd_pcm_share_t; static void _snd_pcm_share_stop(snd_pcm_t *pcm, int state); @@ -115,9 +115,9 @@ static size_t snd_pcm_share_slave_avail(snd_pcm_share_slave_t *slave) snd_pcm_t *pcm = slave->pcm; avail = slave->hw_ptr - *pcm->appl_ptr; if (pcm->stream == SND_PCM_STREAM_PLAYBACK) - avail += pcm->setup.buffer_size; + avail += pcm->buffer_size; if (avail < 0) - avail += pcm->setup.boundary; + avail += pcm->boundary; return avail; } @@ -133,8 +133,8 @@ static size_t _snd_pcm_share_slave_forward(snd_pcm_share_slave_t *slave) size_t avail, slave_avail; size_t slave_hw_avail; slave_avail = snd_pcm_share_slave_avail(slave); - boundary = slave->pcm->setup.boundary; - buffer_size = slave->pcm->setup.buffer_size; + boundary = slave->pcm->boundary; + buffer_size = slave->pcm->buffer_size; min_frames = slave_avail; max_frames = 0; slave_appl_ptr = *slave->pcm->appl_ptr; @@ -191,7 +191,7 @@ static size_t _snd_pcm_share_missing(snd_pcm_t *pcm, int slave_xrun) snd_pcm_share_t *share = pcm->private; snd_pcm_share_slave_t *slave = share->slave; snd_pcm_t *spcm = slave->pcm; - size_t buffer_size = spcm->setup.buffer_size; + size_t buffer_size = spcm->buffer_size; int ready = 1, running = 0; size_t avail = 0, slave_avail; ssize_t hw_avail; @@ -208,7 +208,7 @@ static size_t _snd_pcm_share_missing(snd_pcm_t *pcm, int slave_xrun) default: return INT_MAX; } - if (slave_xrun && pcm->setup.xrun_mode != SND_PCM_XRUN_NONE) { + if (slave_xrun && pcm->xrun_mode != SND_PCM_XRUN_NONE) { _snd_pcm_share_stop(pcm, SND_PCM_STATE_XRUN); goto update_poll; } @@ -251,7 +251,7 @@ static size_t _snd_pcm_share_missing(snd_pcm_t *pcm, int slave_xrun) } break; case SND_PCM_STATE_RUNNING: - if (pcm->setup.xrun_mode != SND_PCM_XRUN_NONE) { + if (pcm->xrun_mode != SND_PCM_XRUN_NONE) { if (hw_avail <= 0) { _snd_pcm_share_stop(pcm, SND_PCM_STATE_XRUN); break; @@ -300,7 +300,7 @@ static size_t _snd_pcm_share_missing(snd_pcm_t *pcm, int slave_xrun) size_t cont = buffer_size - offset; if (cont < frames) frames = cont; - snd_pcm_areas_silence(pcm->running_areas, offset, pcm->setup.format.channels, frames, pcm->setup.format.sfmt); + snd_pcm_areas_silence(pcm->running_areas, offset, pcm->channels, frames, pcm->format); offset += frames; if (offset >= buffer_size) offset = 0; @@ -357,17 +357,17 @@ void *snd_pcm_share_slave_thread(void *data) size_t hw_ptr; ssize_t avail_min; hw_ptr = slave->hw_ptr + missing; - hw_ptr += spcm->setup.frag_size - 1; - if (hw_ptr >= spcm->setup.boundary) - hw_ptr -= spcm->setup.boundary; - hw_ptr -= hw_ptr % spcm->setup.frag_size; + hw_ptr += spcm->fragment_size - 1; + if (hw_ptr >= spcm->boundary) + hw_ptr -= spcm->boundary; + hw_ptr -= hw_ptr % spcm->fragment_size; avail_min = hw_ptr - *spcm->appl_ptr; if (spcm->stream == SND_PCM_STREAM_PLAYBACK) - avail_min += spcm->setup.buffer_size; + avail_min += spcm->buffer_size; if (avail_min < 0) - avail_min += spcm->setup.boundary; + avail_min += spcm->boundary; // printf("avail_min=%d\n", avail_min); - if ((size_t)avail_min != spcm->setup.avail_min) + if ((size_t)avail_min != spcm->avail_min) snd_pcm_set_avail_min(spcm, avail_min); slave->polling = 1; Pthread_mutex_unlock(&slave->mutex); @@ -403,16 +403,16 @@ static void _snd_pcm_share_update(snd_pcm_t *pcm) size_t hw_ptr; ssize_t avail_min; hw_ptr = slave->hw_ptr + missing; - hw_ptr += spcm->setup.frag_size - 1; - if (hw_ptr >= spcm->setup.boundary) - hw_ptr -= spcm->setup.boundary; - hw_ptr -= hw_ptr % spcm->setup.frag_size; + hw_ptr += spcm->fragment_size - 1; + if (hw_ptr >= spcm->boundary) + hw_ptr -= spcm->boundary; + hw_ptr -= hw_ptr % spcm->fragment_size; avail_min = hw_ptr - *spcm->appl_ptr; if (spcm->stream == SND_PCM_STREAM_PLAYBACK) - avail_min += spcm->setup.buffer_size; + avail_min += spcm->buffer_size; if (avail_min < 0) - avail_min += spcm->setup.boundary; - if ((size_t)avail_min < spcm->setup.avail_min) + avail_min += spcm->boundary; + if ((size_t)avail_min < spcm->avail_min) snd_pcm_set_avail_min(spcm, avail_min); } } @@ -442,211 +442,128 @@ static int snd_pcm_share_info(snd_pcm_t *pcm, snd_pcm_info_t *info) return snd_pcm_info(share->slave->pcm, info); } -static int snd_pcm_share_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info) +static int snd_pcm_share_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info) { snd_pcm_share_t *share = pcm->private; snd_pcm_share_slave_t *slave = share->slave; + unsigned int access_mask; int err = 0; - unsigned int req_mask = info->req_mask; - unsigned int channels = info->req.format.channels; - if ((req_mask & SND_PCM_PARAMS_CHANNELS) && - channels != share->channels_count) { - info->req.fail_mask |= SND_PCM_PARAMS_CHANNELS; - info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL; + info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | + SND_PCM_ACCBIT_RW_INTERLEAVED | + SND_PCM_ACCBIT_MMAP_NONINTERLEAVED | + SND_PCM_ACCBIT_RW_NONINTERLEAVED); + access_mask = info->access_mask; + if (access_mask == 0) return -EINVAL; - } - if (slave->sformat >= 0) { - if ((req_mask & SND_PCM_PARAMS_SFMT) && - info->req.format.sfmt != slave->sformat) { - info->req.fail_mask |= SND_PCM_PARAMS_SFMT; - info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL; + if (info->channels_min < share->channels_count) + info->channels_min = share->channels_count; + if (info->channels_max > share->channels_count) + info->channels_max = share->channels_count; + if (info->channels_max > info->channels_max) + return -EINVAL; + if (slave->format >= 0) { + info->format_mask &= 1U << slave->format; + if (!info->format_mask) return -EINVAL; - } - info->req.format.sfmt = slave->sformat; - info->req_mask |= SND_PCM_PARAMS_SFMT; - } - if (slave->srate >= 0) { - info->req.format.rate = slave->srate; - info->req_mask |= SND_PCM_PARAMS_RATE; - } - - info->req_mask |= SND_PCM_PARAMS_CHANNELS; - info->req.format.channels = slave->channels_count; - err = snd_pcm_params_info(slave->pcm, info); - info->req.format.channels = channels; - info->req_mask = req_mask; - Pthread_mutex_lock(&slave->mutex); - if (slave->setup_count > 1 || - (slave->setup_count == 1 && !pcm->valid_setup)) { - snd_pcm_setup_t *s = &slave->pcm->setup; - if ((req_mask & SND_PCM_PARAMS_SFMT) && - info->req.format.sfmt != s->format.sfmt) { - info->req.fail_mask |= SND_PCM_PARAMS_SFMT; - info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL; - err = -EINVAL; - goto _end; - } - info->formats = 1 << s->format.sfmt; - info->rates = SND_PCM_RATE_CONTINUOUS; - info->min_rate = info->max_rate = s->format.rate; - info->buffer_size = s->buffer_size; - info->min_fragment_size = info->max_fragment_size = s->frag_size; - info->min_fragments = info->max_fragments = s->frags; - info->fragment_align = s->frag_size; - info->req.fail_mask = 0; } - - info->min_channels = info->max_channels = share->channels_count; - if (info->flags & SND_PCM_INFO_INTERLEAVED) { - info->flags &= ~SND_PCM_INFO_INTERLEAVED; - info->flags |= SND_PCM_INFO_COMPLEX; + if (slave->rate >= 0) { + if (info->rate_min < (unsigned)slave->rate) + info->rate_min = slave->rate; + if (info->rate_max > (unsigned)slave->rate) + info->rate_max = slave->rate; + if (info->rate_max > info->rate_max) + return -EINVAL; } - _end: - Pthread_mutex_unlock(&slave->mutex); + info->access_mask = SND_PCM_ACCBIT_MMAP; + info->channels_min = info->channels_max = slave->channels_count; + err = snd_pcm_hw_info(slave->pcm, info); + if (info->channels_min <= info->channels_max) + info->channels_min = info->channels_max = share->channels_count; + if (info->access_mask) + info->access_mask = access_mask; + info->info |= SND_PCM_INFO_DOUBLE; return err; } -static int snd_pcm_share_mmap(snd_pcm_t *pcm) +static int snd_pcm_share_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { snd_pcm_share_t *share = pcm->private; snd_pcm_share_slave_t *slave = share->slave; - snd_pcm_mmap_info_t *i; - size_t count; + snd_pcm_t *spcm = slave->pcm; int err = 0; Pthread_mutex_lock(&slave->mutex); - if (slave->mmap_count == 0) { - err = snd_pcm_mmap(slave->pcm); + if (slave->setup_count > 1 || + (slave->setup_count == 1 && !pcm->setup)) { + if (params->access != spcm->access || + params->format != spcm->format || + params->subformat != spcm->subformat || + params->rate != spcm->rate || + params->fragments != spcm->fragments || + params->fragment_size != spcm->fragment_size) { + ERR("slave is already running with different setup"); + params->fail_mask |= SND_PCM_HW_PARBIT_FORMAT; + return -EBUSY; + } + } else { + snd_pcm_hw_params_t sparams = *params; + sparams.channels = slave->channels_count; + err = snd_pcm_hw_params(slave->pcm, &sparams); if (err < 0) goto _end; + /* >= 30 ms */ + slave->safety_threshold = sparams.rate * 30 / 1000; + slave->safety_threshold += sparams.fragment_size - 1; + slave->safety_threshold -= slave->safety_threshold % sparams.fragment_size; + slave->silence_frames = slave->safety_threshold; if (slave->pcm->stream == SND_PCM_STREAM_PLAYBACK) - snd_pcm_areas_silence(slave->pcm->running_areas, 0, slave->pcm->setup.format.channels, slave->pcm->setup.buffer_size, slave->pcm->setup.format.sfmt); + snd_pcm_areas_silence(slave->pcm->running_areas, 0, slave->pcm->channels, slave->pcm->buffer_size, slave->pcm->format); } - slave->mmap_count++; - count = slave->pcm->mmap_info_count; - i = malloc((count + 1) * sizeof(*i)); - if (!i) { - err = -ENOMEM; - goto _end; - } - err = snd_pcm_alloc_user_mmap(pcm, i); - if (err < 0) { - free(i); - return err; - } - share->stopped_data = i->addr; - memcpy(i + 1, slave->pcm->mmap_info, count * sizeof(*pcm->mmap_info)); - pcm->mmap_info_count = count + 1; - pcm->mmap_info = i; + share->state = SND_PCM_STATE_SETUP; + slave->setup_count++; _end: Pthread_mutex_unlock(&slave->mutex); - return 0; + return err; } -static int snd_pcm_share_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED) +static int snd_pcm_share_sw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_sw_params_t *params) { - snd_pcm_share_t *share = pcm->private; - snd_pcm_share_slave_t *slave = share->slave; - snd_pcm_mmap_info_t *i = pcm->mmap_info; - int err = 0; - Pthread_mutex_lock(&slave->mutex); - slave->mmap_count--; - if (slave->mmap_count == 0) { - err = snd_pcm_munmap(slave->pcm); - if (err < 0) - goto _end; + if (params->start_mode > SND_PCM_START_LAST) { + params->fail_mask = SND_PCM_SW_PARBIT_START_MODE; + return -EINVAL; } - err = snd_pcm_free_mmap(pcm, i); - if (err < 0) - goto _end; - free(i); - pcm->mmap_info_count = 0; - pcm->mmap_info = 0; - _end: - Pthread_mutex_unlock(&slave->mutex); - return err; + if (params->ready_mode > SND_PCM_READY_LAST) { + params->fail_mask = SND_PCM_SW_PARBIT_READY_MODE; + return -EINVAL; + } + if (params->xrun_mode > SND_PCM_XRUN_LAST) { + params->fail_mask = SND_PCM_SW_PARBIT_XRUN_MODE; + return -EINVAL; + } + return 0; } - -static int snd_pcm_share_params(snd_pcm_t *pcm, snd_pcm_params_t *params) + +static int snd_pcm_share_dig_info(snd_pcm_t *pcm, snd_pcm_dig_info_t *info) { snd_pcm_share_t *share = pcm->private; snd_pcm_share_slave_t *slave = share->slave; - unsigned int channels = params->format.channels; - int err = 0; - if (channels != share->channels_count) { - params->fail_mask = SND_PCM_PARAMS_CHANNELS; - params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL; - ERR("channels requested (%d) differs from configuration (%ld)", channels, (long)share->channels_count); - return -EINVAL; - } - share->xfer_mode = params->xfer_mode; - share->xrun_mode = params->xrun_mode; - share->avail_min = params->avail_min; + int err; + /* FIXME */ Pthread_mutex_lock(&slave->mutex); - if (slave->setup_count > 1 || - (slave->setup_count == 1 && !pcm->valid_setup)) { - snd_pcm_setup_t *s = &slave->pcm->setup; - if (params->format.sfmt != s->format.sfmt) { - ERR("slave is already running with different format"); - params->fail_mask |= SND_PCM_PARAMS_SFMT; - } - if (params->fail_mask) { - params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL; - err = -EINVAL; - goto _end; - } - } else { - snd_pcm_params_t sp = *params; - snd_pcm_setup_t *ss; - if (slave->sformat >= 0 && - params->format.sfmt != slave->sformat) { - params->fail_mask = SND_PCM_PARAMS_SFMT; - params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL; - ERR("format requested (%d) differs from configuration (%d)", params->format.sfmt, slave->sformat); - err = -EINVAL; - goto _end; - } - if (slave->srate >= 0) - sp.format.rate = slave->srate; - sp.xfer_mode = SND_PCM_XFER_UNSPECIFIED; - sp.xrun_mode = SND_PCM_XRUN_NONE; - sp.format.channels = slave->channels_count; - err = snd_pcm_params_mmap(slave->pcm, &sp); - if (err < 0) - goto _end; - ss = &slave->pcm->setup; - /* >= 30 ms */ - slave->safety_threshold = ss->format.rate * 30 / 1000; - slave->safety_threshold += ss->frag_size - 1; - slave->safety_threshold -= slave->safety_threshold % ss->frag_size; - slave->silence_frames = slave->safety_threshold; - } - share->state = SND_PCM_STATE_SETUP; - slave->setup_count++; - _end: + err = snd_pcm_dig_info(slave->pcm, info); Pthread_mutex_unlock(&slave->mutex); return err; } -static int snd_pcm_share_setup(snd_pcm_t *pcm, snd_pcm_setup_t *setup) +static int snd_pcm_share_dig_params(snd_pcm_t *pcm, snd_pcm_dig_params_t *params) { snd_pcm_share_t *share = pcm->private; snd_pcm_share_slave_t *slave = share->slave; int err; - err = snd_pcm_setup(slave->pcm, setup); - if (err < 0) - return err; - setup->xrun_mode = share->xrun_mode; - setup->format.channels = share->channels_count; - if (share->avail_min > setup->buffer_size) - share->avail_min = setup->buffer_size; - setup->avail_min = share->avail_min; - if (share->xfer_mode == SND_PCM_XFER_UNSPECIFIED) - setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED; - else - setup->xfer_mode = share->xfer_mode; - if (setup->mmap_shape != SND_PCM_MMAP_INTERLEAVED) - setup->mmap_shape = SND_PCM_MMAP_COMPLEX; - return 0; + /* FIXME */ + Pthread_mutex_lock(&slave->mutex); + err = snd_pcm_dig_params(slave->pcm, params); + Pthread_mutex_unlock(&slave->mutex); + return err; } static int snd_pcm_share_status(snd_pcm_t *pcm, snd_pcm_status_t *status) @@ -661,7 +578,7 @@ static int snd_pcm_share_status(snd_pcm_t *pcm, snd_pcm_status_t *status) if (share->state != SND_PCM_STATE_RUNNING && share->state != SND_PCM_STATE_DRAINING) goto _notrunning; - d = pcm->setup.buffer_size - status->avail; + d = pcm->buffer_size - status->avail; } else { status->avail = snd_pcm_mmap_capture_avail(pcm); if (share->state != SND_PCM_STATE_RUNNING) @@ -738,7 +655,7 @@ static ssize_t snd_pcm_share_avail_update(snd_pcm_t *pcm) } Pthread_mutex_unlock(&slave->mutex); avail = snd_pcm_mmap_avail(pcm); - if ((size_t)avail > pcm->setup.buffer_size) + if ((size_t)avail > pcm->buffer_size) return -EPIPE; return avail; } @@ -753,10 +670,10 @@ static ssize_t _snd_pcm_share_mmap_forward(snd_pcm_t *pcm, size_t size) if (pcm->stream == SND_PCM_STREAM_PLAYBACK && share->state == SND_PCM_STATE_RUNNING) { frames = *slave->pcm->appl_ptr - share->appl_ptr; - if (frames > (ssize_t)pcm->setup.buffer_size) - frames -= pcm->setup.boundary; - else if (frames < -(ssize_t)pcm->setup.buffer_size) - frames += pcm->setup.boundary; + if (frames > (ssize_t)pcm->buffer_size) + frames -= pcm->boundary; + else if (frames < -(ssize_t)pcm->buffer_size) + frames += pcm->boundary; if (frames > 0) { /* Latecomer PCM */ ret = snd_pcm_rewind(slave->pcm, frames); @@ -839,13 +756,13 @@ static int snd_pcm_share_start(snd_pcm_t *pcm) while (xfer < hw_avail) { size_t frames = hw_avail - xfer; size_t offset = snd_pcm_mmap_offset(pcm); - size_t cont = pcm->setup.buffer_size - offset; + size_t cont = pcm->buffer_size - offset; if (cont < frames) frames = cont; snd_pcm_areas_copy(pcm->stopped_areas, xfer, pcm->running_areas, offset, - pcm->setup.format.channels, frames, - pcm->setup.format.sfmt); + pcm->channels, frames, + pcm->format); xfer += frames; } snd_pcm_mmap_appl_forward(pcm, hw_avail); @@ -883,51 +800,6 @@ static int snd_pcm_share_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *in return err; } -static int snd_pcm_share_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params) -{ - snd_pcm_share_t *share = pcm->private; - snd_pcm_share_slave_t *slave = share->slave; - unsigned int channel = params->channel; - int c = share->slave_channels[channel]; - int err; - params->channel = c; - err = snd_pcm_channel_params(slave->pcm, params); - params->channel = channel; - return err; -} - -static int snd_pcm_share_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup) -{ - snd_pcm_share_t *share = pcm->private; - snd_pcm_share_slave_t *slave = share->slave; - unsigned int channel = setup->channel; - int c = share->slave_channels[channel]; - int err; - setup->channel = c; - err = snd_pcm_channel_setup(slave->pcm, setup); - setup->channel = channel; - if (err < 0) - return err; - if (!pcm->mmap_info) - return 0; - switch (pcm->setup.mmap_shape) { - case SND_PCM_MMAP_INTERLEAVED: - case SND_PCM_MMAP_COMPLEX: - setup->stopped_area.addr = share->stopped_data; - setup->stopped_area.first = channel * pcm->bits_per_sample; - setup->stopped_area.step = pcm->bits_per_frame; - break; - case SND_PCM_MMAP_NONINTERLEAVED: - setup->stopped_area.addr = share->stopped_data + c * pcm->setup.buffer_size * pcm->bits_per_sample / 8; - setup->stopped_area.first = 0; - setup->stopped_area.step = pcm->bits_per_sample; - break; - default: - assert(0); - } - return 0; -} - static ssize_t _snd_pcm_share_rewind(snd_pcm_t *pcm, size_t frames) { snd_pcm_share_t *share = pcm->private; @@ -984,7 +856,7 @@ static int snd_pcm_share_set_avail_min(snd_pcm_t *pcm, size_t frames) snd_pcm_share_t *share = pcm->private; snd_pcm_share_slave_t *slave = share->slave; Pthread_mutex_lock(&slave->mutex); - pcm->setup.avail_min = frames; + pcm->avail_min = frames; share->avail_min = frames; _snd_pcm_share_update(pcm); Pthread_mutex_unlock(&slave->mutex); @@ -996,7 +868,7 @@ static void _snd_pcm_share_stop(snd_pcm_t *pcm, int state) { snd_pcm_share_t *share = pcm->private; snd_pcm_share_slave_t *slave = share->slave; - if (!pcm->mmap_info) { + if (!pcm->mmap_channels) { /* PCM closing already begun in the main thread */ return; } @@ -1004,13 +876,13 @@ static void _snd_pcm_share_stop(snd_pcm_t *pcm, int state) if (pcm->stream == SND_PCM_STREAM_CAPTURE) { snd_pcm_areas_copy(pcm->running_areas, 0, pcm->stopped_areas, 0, - pcm->setup.format.channels, pcm->setup.buffer_size, - pcm->setup.format.sfmt); + pcm->channels, pcm->buffer_size, + pcm->format); } else if (slave->running_count > 1) { int err; ssize_t delay; - snd_pcm_areas_silence(pcm->running_areas, 0, pcm->setup.format.channels, - pcm->setup.buffer_size, pcm->setup.format.sfmt); + snd_pcm_areas_silence(pcm->running_areas, 0, pcm->channels, + pcm->buffer_size, pcm->format); err = snd_pcm_delay(slave->pcm, &delay); if (err >= 0 && delay > 0) snd_pcm_rewind(slave->pcm, delay); @@ -1116,7 +988,7 @@ static int snd_pcm_share_close(snd_pcm_t *pcm) int err = 0; Pthread_mutex_lock(&slaves_mutex); Pthread_mutex_lock(&slave->mutex); - if (pcm->valid_setup) + if (pcm->setup) slave->setup_count--; slave->open_count--; if (slave->open_count == 0) { @@ -1142,6 +1014,16 @@ static int snd_pcm_share_close(snd_pcm_t *pcm) return err; } +static int snd_pcm_share_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int snd_pcm_share_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED) +{ + return 0; +} + static void snd_pcm_share_dump(snd_pcm_t *pcm, FILE *fp) { snd_pcm_share_t *share = pcm->private; @@ -1151,7 +1033,7 @@ static void snd_pcm_share_dump(snd_pcm_t *pcm, FILE *fp) fprintf(fp, "\nChannel bindings:\n"); for (k = 0; k < share->channels_count; ++k) fprintf(fp, "%d: %d\n", k, share->slave_channels[k]); - if (pcm->valid_setup) { + if (pcm->setup) { fprintf(fp, "\nIts setup is:\n"); snd_pcm_dump_setup(pcm, fp); } @@ -1162,12 +1044,12 @@ static void snd_pcm_share_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_share_ops = { close: snd_pcm_share_close, info: snd_pcm_share_info, - params_info: snd_pcm_share_params_info, - params: snd_pcm_share_params, - setup: snd_pcm_share_setup, + hw_info: snd_pcm_share_hw_info, + hw_params: snd_pcm_share_hw_params, + sw_params: snd_pcm_share_sw_params, + dig_info: snd_pcm_share_dig_info, + dig_params: snd_pcm_share_dig_params, channel_info: snd_pcm_share_channel_info, - channel_params: snd_pcm_share_channel_params, - channel_setup: snd_pcm_share_channel_setup, dump: snd_pcm_share_dump, nonblock: snd_pcm_share_nonblock, async: snd_pcm_share_async, @@ -1311,8 +1193,8 @@ int snd_pcm_share_open(snd_pcm_t **pcmp, char *name, char *sname, INIT_LIST_HEAD(&slave->clients); slave->pcm = spcm; slave->channels_count = schannels_count; - slave->sformat = sformat; - slave->srate = srate; + slave->format = sformat; + slave->rate = srate; pthread_mutex_init(&slave->mutex, NULL); pthread_cond_init(&slave->poll_cond, NULL); list_add_tail(&slave->list, &slaves); @@ -1353,7 +1235,7 @@ int snd_pcm_share_open(snd_pcm_t **pcmp, char *name, char *sname, pcm->type = SND_PCM_TYPE_SHARE; pcm->stream = stream; pcm->mode = mode; - pcm->mmap_auto = 1; + pcm->mmap_rw = 1; pcm->ops = &snd_pcm_share_ops; pcm->op_arg = pcm; pcm->fast_ops = &snd_pcm_share_fast_ops; diff --git a/src/pcm/pcm_shm.c b/src/pcm/pcm_shm.c index 0878bc17..d609e742 100644 --- a/src/pcm/pcm_shm.c +++ b/src/pcm/pcm_shm.c @@ -22,6 +22,7 @@ #include <stdio.h> #include <stdlib.h> #include <stddef.h> +#include <limits.h> #include <unistd.h> #include <string.h> #include <errno.h> @@ -40,8 +41,8 @@ typedef struct { int socket; + unsigned int access_mask; volatile snd_pcm_shm_ctrl_t *ctrl; - snd_pcm_mmap_info_t *slave_mmap_info; } snd_pcm_shm_t; int receive_fd(int socket, void *data, size_t len, int *fd) @@ -147,108 +148,147 @@ static int snd_pcm_shm_info(snd_pcm_t *pcm, snd_pcm_info_t * info) return err; } -static int snd_pcm_shm_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info) +static int snd_pcm_shm_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info) { snd_pcm_shm_t *shm = pcm->private; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; int err; - ctrl->cmd = SND_PCM_IOCTL_PARAMS_INFO; - ctrl->u.params_info = *info; + unsigned int access_mask = info->access_mask; + ctrl->cmd = SND_PCM_IOCTL_HW_INFO; + ctrl->u.hw_info = *info; + ctrl->u.hw_info.access_mask |= SND_PCM_ACCBIT_MMAP; err = snd_pcm_shm_action(pcm); + *info = ctrl->u.hw_info; + if (info->access_mask) { + shm->access_mask = info->access_mask; + info->access_mask |= (SND_PCM_ACCESS_RW_INTERLEAVED | + SND_PCM_ACCESS_RW_NONINTERLEAVED); + info->access_mask &= access_mask; + } if (err < 0) return err; - *info = ctrl->u.params_info; return err; } -static int snd_pcm_shm_params(snd_pcm_t *pcm, snd_pcm_params_t * params) +static int snd_pcm_shm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) { snd_pcm_shm_t *shm = pcm->private; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; + unsigned int access = params->access; int err; - ctrl->cmd = SND_PCM_IOCTL_PARAMS; - ctrl->u.params = *params; + ctrl->cmd = SND_PCM_IOCTL_HW_PARAMS; + ctrl->u.hw_params = *params; + if (shm->access_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) + ctrl->u.hw_params.access = SND_PCM_ACCESS_MMAP_INTERLEAVED; + else if (shm->access_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED) + ctrl->u.hw_params.access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; + else + assert(0); err = snd_pcm_shm_action(pcm); + params->access = access; + *params = ctrl->u.hw_params; if (err < 0) return err; - *params = ctrl->u.params; return err; } -static int snd_pcm_shm_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup) +static int snd_pcm_shm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params) { snd_pcm_shm_t *shm = pcm->private; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; int err; - ctrl->cmd = SND_PCM_IOCTL_SETUP; - // ctrl->u.setup = *setup; + ctrl->cmd = SND_PCM_IOCTL_SW_PARAMS; + ctrl->u.sw_params = *params; err = snd_pcm_shm_action(pcm); + *params = ctrl->u.sw_params; if (err < 0) return err; - *setup = ctrl->u.setup; return err; } -static int snd_pcm_shm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info) +static int snd_pcm_shm_dig_info(snd_pcm_t *pcm, snd_pcm_dig_info_t * info) { snd_pcm_shm_t *shm = pcm->private; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; int err; - ctrl->cmd = SND_PCM_IOCTL_CHANNEL_INFO; - ctrl->u.channel_info = *info; + ctrl->cmd = SND_PCM_IOCTL_DIG_INFO; + ctrl->u.dig_info = *info; err = snd_pcm_shm_action(pcm); if (err < 0) return err; - *info = ctrl->u.channel_info; + *info = ctrl->u.dig_info; return err; } -static int snd_pcm_shm_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params) +static int snd_pcm_shm_dig_params(snd_pcm_t *pcm, snd_pcm_dig_params_t * params) { snd_pcm_shm_t *shm = pcm->private; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; int err; - ctrl->cmd = SND_PCM_IOCTL_CHANNEL_PARAMS; - ctrl->u.channel_params = *params; + ctrl->cmd = SND_PCM_IOCTL_DIG_PARAMS; + ctrl->u.dig_params = *params; err = snd_pcm_shm_action(pcm); + *params = ctrl->u.dig_params; if (err < 0) return err; - *params = ctrl->u.channel_params; return err; } -static void *convert_addr(void *addr, size_t count, snd_pcm_mmap_info_t *old, snd_pcm_mmap_info_t *new) +static int snd_pcm_shm_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED) { - size_t k; - size_t mindist = ULONG_MAX; - int idx = -1; - for (k = 0; k < count; ++k) { - if (addr >= old[k].addr) { - size_t dist = addr - old[k].addr; - if (dist < mindist) { - mindist = dist; - idx = k; - } + return 0; +} + +static int snd_pcm_shm_munmap(snd_pcm_t *pcm) +{ + unsigned int c; + for (c = 0; c < pcm->channels; ++c) { + snd_pcm_channel_info_t *i = &pcm->mmap_channels[c]; + unsigned int c1; + int err; + if (i->type != SND_PCM_AREA_MMAP) + continue; + if (i->u.mmap.fd < 0) + continue; + for (c1 = c + 1; c1 < pcm->channels; ++c1) { + snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1]; + if (i1->type != SND_PCM_AREA_MMAP) + continue; + if (i1->u.mmap.fd != i->u.mmap.fd) + continue; + i1->u.mmap.fd = -1; + } + err = close(i->u.mmap.fd); + if (err < 0) { + SYSERR("close failed"); + return -errno; } } - assert(idx >= 0); - return new[idx].addr + mindist; + return 0; } -static int snd_pcm_shm_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup) +static int snd_pcm_shm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info) { snd_pcm_shm_t *shm = pcm->private; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; int err; - ctrl->cmd = SND_PCM_IOCTL_CHANNEL_SETUP; - ctrl->u.channel_setup = *setup; - err = snd_pcm_shm_action(pcm); + int fd; + ctrl->cmd = SND_PCM_IOCTL_CHANNEL_INFO; + ctrl->u.channel_info = *info; + err = snd_pcm_shm_action_fd(pcm, &fd); if (err < 0) return err; - *setup = ctrl->u.channel_setup; - if (pcm->mmap_info) { - setup->running_area.addr = convert_addr(setup->running_area.addr, pcm->mmap_info_count, shm->slave_mmap_info, pcm->mmap_info); - setup->stopped_area.addr = convert_addr(setup->stopped_area.addr, pcm->mmap_info_count, shm->slave_mmap_info, pcm->mmap_info); + *info = ctrl->u.channel_info; + info->addr = 0; + switch (info->type) { + case SND_PCM_AREA_MMAP: + info->u.mmap.fd = fd; + break; + case SND_PCM_AREA_SHM: + break; + default: + assert(0); + break; } return err; } @@ -356,86 +396,6 @@ static ssize_t snd_pcm_shm_rewind(snd_pcm_t *pcm, size_t frames) return snd_pcm_shm_action(pcm); } -static int snd_pcm_shm_mmap(snd_pcm_t *pcm) -{ - snd_pcm_shm_t *shm = pcm->private; - volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; - int count, k, err, fd; - ctrl->cmd = SND_PCM_IOCTL_MMAP; - count = snd_pcm_shm_action(pcm); - if (count < 0) - return count; - pcm->mmap_info_count = count; - pcm->mmap_info = malloc(count * sizeof(*pcm->mmap_info)); - shm->slave_mmap_info = malloc(count * sizeof(*shm->slave_mmap_info)); - for (k = 0; k < count; ++k) { - snd_pcm_mmap_info_t *i = &pcm->mmap_info[k]; - void *ptr; - ctrl->cmd = SND_PCM_IOCTL_MMAP_INFO; - ctrl->u.mmap_info.index = k; - err = snd_pcm_shm_action_fd(pcm, &fd); - if (err < 0) - return err; - shm->slave_mmap_info[k] = ctrl->u.mmap_info; - *i = ctrl->u.mmap_info; - if (i->type == SND_PCM_MMAP_KERNEL) { - i->u.kernel.fd = fd; - ptr = mmap(NULL, i->size, PROT_WRITE | PROT_READ, - MAP_FILE | MAP_SHARED, - fd, SND_PCM_MMAP_OFFSET_DATA); - close(fd); - if (ptr == MAP_FAILED || ptr == NULL) { - SYSERR("mmap failed"); - free(pcm->mmap_info); - return -errno; - } - } else { - ptr = shmat(i->u.user.shmid, 0, 0); - if (ptr == (void*)-1) { - SYSERR("shmat failed"); - free(pcm->mmap_info); - return -errno; - } - } - i->addr = ptr; - } - return 0; -} - -static int snd_pcm_shm_munmap(snd_pcm_t *pcm) -{ - snd_pcm_shm_t *shm = pcm->private; - volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; - int err; - unsigned int k; - ctrl->cmd = SND_PCM_IOCTL_MUNMAP; - err = snd_pcm_shm_action(pcm); - if (err < 0) - return err; - for (k = 0; k < pcm->mmap_info_count; ++k) { - snd_pcm_mmap_info_t *i = &pcm->mmap_info[k]; - if (i->type == SND_PCM_MMAP_KERNEL) { - err = munmap(i->addr, i->size); - if (err < 0) { - SYSERR("munmap failed"); - return -errno; - } - } else { - err = shmdt(i->addr); - if (err < 0) { - SYSERR("shmdt failed"); - return -errno; - } - } - } - pcm->mmap_info_count = 0; - free(pcm->mmap_info); - free(shm->slave_mmap_info); - pcm->mmap_info = 0; - shm->slave_mmap_info = 0; - return ctrl->result; -} - static ssize_t snd_pcm_shm_mmap_forward(snd_pcm_t *pcm, size_t size) { snd_pcm_shm_t *shm = pcm->private; @@ -483,7 +443,7 @@ static int snd_pcm_shm_close(snd_pcm_t *pcm) static void snd_pcm_shm_dump(snd_pcm_t *pcm, FILE *fp) { fprintf(fp, "Shm PCM\n"); - if (pcm->valid_setup) { + if (pcm->setup) { fprintf(fp, "\nIts setup is:\n"); snd_pcm_dump_setup(pcm, fp); } @@ -492,12 +452,12 @@ static void snd_pcm_shm_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_shm_ops = { close: snd_pcm_shm_close, info: snd_pcm_shm_info, - params_info: snd_pcm_shm_params_info, - params: snd_pcm_shm_params, - setup: snd_pcm_shm_setup, + hw_info: snd_pcm_shm_hw_info, + hw_params: snd_pcm_shm_hw_params, + sw_params: snd_pcm_shm_sw_params, + dig_info: snd_pcm_shm_dig_info, + dig_params: snd_pcm_shm_dig_params, channel_info: snd_pcm_shm_channel_info, - channel_params: snd_pcm_shm_channel_params, - channel_setup: snd_pcm_shm_channel_setup, dump: snd_pcm_shm_dump, nonblock: snd_pcm_shm_nonblock, async: snd_pcm_shm_async, @@ -656,7 +616,7 @@ int snd_pcm_shm_open(snd_pcm_t **pcmp, char *name, char *socket, char *sname, in pcm->type = SND_PCM_TYPE_SHM; pcm->stream = stream; pcm->mode = mode; - pcm->mmap_auto = 1; + pcm->mmap_rw = 1; pcm->ops = &snd_pcm_shm_ops; pcm->op_arg = pcm; pcm->fast_ops = &snd_pcm_shm_fast_ops; diff --git a/src/rawmidi/Makefile.am b/src/rawmidi/Makefile.am index 1abeadd1..7b374de4 100644 --- a/src/rawmidi/Makefile.am +++ b/src/rawmidi/Makefile.am @@ -1,6 +1,8 @@ EXTRA_LTLIBRARIES=librawmidi.la -librawmidi_la_SOURCES = rawmidi.c +librawmidi_la_SOURCES = rawmidi.c rawmidi_hw.c +noinst_HEADERS = rawmidi_local.h + all: librawmidi.la diff --git a/src/rawmidi/rawmidi.c b/src/rawmidi/rawmidi.c index f18dc087..3bebdff9 100644 --- a/src/rawmidi/rawmidi.c +++ b/src/rawmidi/rawmidi.c @@ -1,7 +1,7 @@ /* * RawMIDI Interface - main file - * Copyright (c) 1998 by Jaroslav Kysela <perex@suse.cz> - * + * Copyright (c) 2000 by Jaroslav Kysela <perex@suse.cz> + * Abramo Bagnara <abramo@alsa-project.org> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as @@ -21,221 +21,214 @@ #include <stdio.h> #include <stdlib.h> +#include <stdarg.h> #include <unistd.h> #include <string.h> #include <errno.h> -#include <fcntl.h> -#include <sys/ioctl.h> +#include <dlfcn.h> +#include "rawmidi_local.h" #include "asoundlib.h" -#define SND_FILE_RAWMIDI "/dev/snd/midiC%iD%i" -#define SND_RAWMIDI_VERSION_MAX SND_PROTOCOL_VERSION(2, 0, 0) - -struct snd_rawmidi { - int card; - int device; - int fd; - int mode; -}; - -int snd_rawmidi_open_subdevice(snd_rawmidi_t **handle, int card, int device, int subdevice, int mode) -{ - int fd, ver, ret; - int attempt = 0; - char filename[32]; - snd_ctl_t *ctl; - snd_rawmidi_t *rmidi; - snd_rawmidi_info_t info; - - *handle = NULL; - - if (card < 0 || card >= SND_CARDS) - return -EINVAL; - - if ((ret = snd_ctl_hw_open(&ctl, NULL, card)) < 0) - return ret; - sprintf(filename, SND_FILE_RAWMIDI, card, device); - - __again: - if (attempt++ > 3) { - snd_ctl_close(ctl); - return -EBUSY; - } - ret = snd_ctl_rawmidi_prefer_subdevice(ctl, subdevice); - if (ret < 0) { - snd_ctl_close(ctl); - return ret; - } - if ((fd = open(filename, mode)) < 0) { - snd_card_load(card); - if ((fd = open(filename, mode)) < 0) { - snd_ctl_close(ctl); - return -errno; - } - } - if (ioctl(fd, SND_RAWMIDI_IOCTL_PVERSION, &ver) < 0) { - ret = -errno; - close(fd); - snd_ctl_close(ctl); - return ret; - } - if (SND_PROTOCOL_INCOMPATIBLE(ver, SND_RAWMIDI_VERSION_MAX)) { - close(fd); - snd_ctl_close(ctl); - return -SND_ERROR_INCOMPATIBLE_VERSION; - } - if (subdevice >= 0) { - memset(&info, 0, sizeof(info)); - if (ioctl(fd, SND_RAWMIDI_IOCTL_INFO, &info) < 0) { - ret = -errno; - close(fd); - snd_ctl_close(ctl); - return ret; - } - if (info.subdevice != subdevice) { - close(fd); - goto __again; - } - } - rmidi = (snd_rawmidi_t *) calloc(1, sizeof(snd_rawmidi_t)); - if (rmidi == NULL) { - close(fd); - snd_ctl_close(ctl); - return -ENOMEM; - } - rmidi->card = card; - rmidi->device = device; - rmidi->fd = fd; - rmidi->mode = mode; - *handle = rmidi; - return 0; -} - -int snd_rawmidi_open(snd_rawmidi_t **handle, int card, int device, int mode) -{ - return snd_rawmidi_open_subdevice(handle, card, device, -1, mode); -} - int snd_rawmidi_close(snd_rawmidi_t *rmidi) { - int res; - - if (!rmidi) - return -EINVAL; - res = close(rmidi->fd) < 0 ? -errno : 0; + int err; + assert(rmidi); + if ((err = rmidi->ops->close(rmidi)) < 0) + return err; + if (rmidi->name) + free(rmidi->name); free(rmidi); - return res; + return 0; } int snd_rawmidi_poll_descriptor(snd_rawmidi_t *rmidi) { - if (!rmidi) - return -EINVAL; - return rmidi->fd; + assert(rmidi); + return rmidi->poll_fd; } -int snd_rawmidi_block_mode(snd_rawmidi_t *rmidi, int enable) +int snd_rawmidi_nonblock(snd_rawmidi_t *rmidi, int nonblock) { - long flags; - - if (!rmidi) - return -EINVAL; - if (rmidi->mode == SND_RAWMIDI_OPEN_OUTPUT_APPEND || - rmidi->mode == SND_RAWMIDI_OPEN_DUPLEX_APPEND) - return -EINVAL; - if ((flags = fcntl(rmidi->fd, F_GETFL)) < 0) - return -errno; - if (enable) - flags &= ~O_NONBLOCK; + int err; + assert(rmidi); + assert(!(rmidi->mode & SND_RAWMIDI_APPEND)); + if ((err = rmidi->ops->nonblock(rmidi, nonblock)) < 0) + return err; + if (nonblock) + rmidi->mode |= SND_RAWMIDI_NONBLOCK; else - flags |= O_NONBLOCK; - if (fcntl(rmidi->fd, F_SETFL, flags) < 0) - return -errno; + rmidi->mode &= ~SND_RAWMIDI_NONBLOCK; return 0; } int snd_rawmidi_info(snd_rawmidi_t *rmidi, snd_rawmidi_info_t * info) { - if (!rmidi || !info) - return -EINVAL; - if (ioctl(rmidi->fd, SND_RAWMIDI_IOCTL_INFO, info) < 0) - return -errno; - return 0; + assert(rmidi && info); + return rmidi->ops->info(rmidi, info); } int snd_rawmidi_params(snd_rawmidi_t *rmidi, snd_rawmidi_params_t * params) { - if (!rmidi || !params) - return -EINVAL; - if (params->stream < 0 || params->stream > 1) - return -EINVAL; - if (ioctl(rmidi->fd, SND_RAWMIDI_IOCTL_PARAMS, params) < 0) - return -errno; - return 0; + assert(rmidi && params); + return rmidi->ops->params(rmidi, params); } int snd_rawmidi_status(snd_rawmidi_t *rmidi, snd_rawmidi_status_t * status) { - if (!rmidi || !status) - return -EINVAL; - if (status->stream < 0 || status->stream > 1) - return -EINVAL; - if (ioctl(rmidi->fd, SND_RAWMIDI_IOCTL_STATUS, status) < 0) - return -errno; - return 0; + assert(rmidi && status); + return rmidi->ops->status(rmidi, status); +} + +int snd_rawmidi_drop(snd_rawmidi_t *rmidi, int str) +{ + assert(rmidi); + assert(str >= 0 && str <= 1); + assert(rmidi->streams & (1 << str)); + return rmidi->ops->drop(rmidi, str); } int snd_rawmidi_output_drop(snd_rawmidi_t *rmidi) { - int str = SND_RAWMIDI_STREAM_OUTPUT; - if (!rmidi) - return -EINVAL; - if (ioctl(rmidi->fd, SND_RAWMIDI_IOCTL_DROP, &str) < 0) - return -errno; - return 0; + return snd_rawmidi_drop(rmidi, SND_RAWMIDI_STREAM_OUTPUT); } -int snd_rawmidi_stream_drain(snd_rawmidi_t *rmidi, int str) +int snd_rawmidi_drain(snd_rawmidi_t *rmidi, int str) { - if (!rmidi) - return -EINVAL; - if (str < 0 || str > 1) - return -EINVAL; - if (ioctl(rmidi->fd, SND_RAWMIDI_IOCTL_DRAIN, &str) < 0) - return -errno; - return 0; + assert(rmidi); + assert(str >= 0 && str <= 1); + assert(rmidi->streams & (1 << str)); + return rmidi->ops->drain(rmidi, str); } int snd_rawmidi_output_drain(snd_rawmidi_t *rmidi) { - return snd_rawmidi_stream_drain(rmidi, SND_RAWMIDI_STREAM_OUTPUT); + return snd_rawmidi_drain(rmidi, SND_RAWMIDI_STREAM_OUTPUT); } int snd_rawmidi_input_drain(snd_rawmidi_t *rmidi) { - return snd_rawmidi_stream_drain(rmidi, SND_RAWMIDI_STREAM_INPUT); + return snd_rawmidi_drain(rmidi, SND_RAWMIDI_STREAM_INPUT); } ssize_t snd_rawmidi_write(snd_rawmidi_t *rmidi, const void *buffer, size_t size) { - ssize_t result; - - if (!rmidi || (!buffer && size > 0)) - return -EINVAL; - result = write(rmidi->fd, buffer, size); - if (result < 0) - return -errno; - return result; + assert(rmidi && (buffer || size == 0)); + return rmidi->ops->write(rmidi, buffer, size); } ssize_t snd_rawmidi_read(snd_rawmidi_t *rmidi, void *buffer, size_t size) { - ssize_t result; + assert(rmidi && (buffer || size == 0)); + return rmidi->ops->read(rmidi, buffer, size); +} - if (!rmidi || (!buffer && size > 0)) +int snd_rawmidi_open(snd_rawmidi_t **rawmidip, char *name, + int streams, int mode) +{ + char *str; + int err; + snd_config_t *rawmidi_conf, *conf, *type_conf; + snd_config_iterator_t i; + char *lib = NULL, *open = NULL; + int (*open_func)(snd_rawmidi_t **rawmidip, char *name, snd_config_t *conf, + int streams, int mode); + void *h; + assert(rawmidip && name); + err = snd_config_update(); + if (err < 0) + return err; + err = snd_config_searchv(snd_config, &rawmidi_conf, "rawmidi", name, 0); + if (err < 0) { + int card, dev, subdev; + err = sscanf(name, "hw:%d,%d,%d", &card, &dev, &subdev); + if (err == 3) + return snd_rawmidi_hw_open(rawmidip, name, card, dev, subdev, streams, mode); + err = sscanf(name, "hw:%d,%d", &card, &dev); + if (err == 2) + return snd_rawmidi_hw_open(rawmidip, name, card, dev, -1, streams, mode); + ERR("Unknown RAWMIDI %s", name); + return -ENOENT; + } + if (snd_config_type(rawmidi_conf) != SND_CONFIG_TYPE_COMPOUND) { + ERR("Invalid type for RAWMIDI definition"); + return -EINVAL; + } + err = snd_config_search(rawmidi_conf, "streams", &conf); + if (err >= 0) { + err = snd_config_string_get(conf, &str); + if (err < 0) { + ERR("Invalid type for streams"); + return err; + } + if (strcmp(str, "output") == 0) { + if (streams == SND_RAWMIDI_OPEN_INPUT) + return -EINVAL; + } else if (strcmp(str, "input") == 0) { + if (streams == SND_RAWMIDI_OPEN_OUTPUT) + return -EINVAL; + } else if (strcmp(str, "duplex") == 0) { + if (streams != SND_RAWMIDI_OPEN_DUPLEX) + return -EINVAL; + } else { + ERR("Invalid value for streams"); + return -EINVAL; + } + } + err = snd_config_search(rawmidi_conf, "type", &conf); + if (err < 0) { + ERR("type is not defined"); + return err; + } + err = snd_config_string_get(conf, &str); + if (err < 0) { + ERR("Invalid type for type"); + return err; + } + err = snd_config_searchv(snd_config, &type_conf, "rawmiditype", str, 0); + if (err < 0) { + ERR("Unknown RAWMIDI type %s", str); + return err; + } + snd_config_foreach(i, type_conf) { + snd_config_t *n = snd_config_entry(i); + if (strcmp(n->id, "comment") == 0) + continue; + if (strcmp(n->id, "lib") == 0) { + err = snd_config_string_get(n, &lib); + if (err < 0) { + ERR("Invalid type for lib"); + return -EINVAL; + } + continue; + } + if (strcmp(n->id, "open") == 0) { + err = snd_config_string_get(n, &open); + if (err < 0) { + ERR("Invalid type for open"); + return -EINVAL; + } + continue; + ERR("Unknown field: %s", n->id); + return -EINVAL; + } + } + if (!open) { + ERR("open is not defined"); return -EINVAL; - result = read(rmidi->fd, buffer, size); - if (result < 0) - return -errno; - return result; + } + if (!lib) + lib = "libasound.so"; + h = dlopen(lib, RTLD_NOW); + if (!h) { + ERR("Cannot open shared library %s", lib); + return -ENOENT; + } + open_func = dlsym(h, open); + dlclose(h); + if (!open_func) { + ERR("symbol %s is not defined inside %s", open, lib); + return -ENXIO; + } + return open_func(rawmidip, name, rawmidi_conf, streams, mode); } + diff --git a/src/rawmidi/rawmidi_hw.c b/src/rawmidi/rawmidi_hw.c new file mode 100644 index 00000000..4ebe70a4 --- /dev/null +++ b/src/rawmidi/rawmidi_hw.c @@ -0,0 +1,320 @@ +/* + * RawMIDI - Hardware + * Copyright (c) 2000 by Jaroslav Kysela <perex@suse.cz> + * Abramo Bagnara <abramo@alsa-project.org> + * + * + * 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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include "../control/control_local.h" +#include "rawmidi_local.h" +#include "asoundlib.h" + +#define SND_FILE_RAWMIDI "/dev/snd/midiC%iD%i" +#define SND_RAWMIDI_VERSION_MAX SND_PROTOCOL_VERSION(2, 0, 0) + +typedef struct { + int fd; + int card, device, subdevice; +} snd_rawmidi_hw_t; + +static int snd_rawmidi_hw_close(snd_rawmidi_t *rmidi) +{ + snd_rawmidi_hw_t *hw = rmidi->private; + if (close(hw->fd)) { + SYSERR("close failed\n"); + return -errno; + } + free(hw); + return 0; +} + +static int snd_rawmidi_hw_nonblock(snd_rawmidi_t *rmidi, int nonblock) +{ + snd_rawmidi_hw_t *hw = rmidi->private; + long flags; + + if ((flags = fcntl(hw->fd, F_GETFL)) < 0) { + SYSERR("F_GETFL failed"); + return -errno; + } + if (nonblock) + flags &= ~O_NONBLOCK; + else + flags |= O_NONBLOCK; + if (fcntl(hw->fd, F_SETFL, flags) < 0) { + SYSERR("F_SETFL for O_NONBLOCK failed"); + return -errno; + } + return 0; +} + +static int snd_rawmidi_hw_info(snd_rawmidi_t *rmidi, snd_rawmidi_info_t * info) +{ + snd_rawmidi_hw_t *hw = rmidi->private; + if (ioctl(hw->fd, SND_RAWMIDI_IOCTL_INFO, info) < 0) { + SYSERR("SND_RAWMIDI_IOCTL_INFO failed"); + return -errno; + } + return 0; +} + +static int snd_rawmidi_hw_params(snd_rawmidi_t *rmidi, snd_rawmidi_params_t * params) +{ + snd_rawmidi_hw_t *hw = rmidi->private; + if (ioctl(hw->fd, SND_RAWMIDI_IOCTL_PARAMS, params) < 0) { + SYSERR("SND_RAWMIDI_IOCTL_PARAMS failed"); + return -errno; + } + return 0; +} + +static int snd_rawmidi_hw_status(snd_rawmidi_t *rmidi, snd_rawmidi_status_t * status) +{ + snd_rawmidi_hw_t *hw = rmidi->private; + if (ioctl(hw->fd, SND_RAWMIDI_IOCTL_STATUS, status) < 0) { + SYSERR("SND_RAWMIDI_IOCTL_STATUS failed"); + return -errno; + } + return 0; +} + +static int snd_rawmidi_hw_drop(snd_rawmidi_t *rmidi, int stream) +{ + snd_rawmidi_hw_t *hw = rmidi->private; + if (ioctl(hw->fd, SND_RAWMIDI_IOCTL_DROP, &stream) < 0) { + SYSERR("SND_RAWMIDI_IOCTL_DROP failed"); + return -errno; + } + return 0; +} + +static int snd_rawmidi_hw_drain(snd_rawmidi_t *rmidi, int stream) +{ + snd_rawmidi_hw_t *hw = rmidi->private; + if (ioctl(hw->fd, SND_RAWMIDI_IOCTL_DRAIN, &stream) < 0) { + SYSERR("SND_RAWMIDI_IOCTL_DRAIN failed"); + return -errno; + } + return 0; +} + +static ssize_t snd_rawmidi_hw_write(snd_rawmidi_t *rmidi, const void *buffer, size_t size) +{ + snd_rawmidi_hw_t *hw = rmidi->private; + ssize_t result; + result = write(hw->fd, buffer, size); + if (result < 0) + return -errno; + return result; +} + +static ssize_t snd_rawmidi_hw_read(snd_rawmidi_t *rmidi, void *buffer, size_t size) +{ + snd_rawmidi_hw_t *hw = rmidi->private; + ssize_t result; + result = read(hw->fd, buffer, size); + if (result < 0) + return -errno; + return result; +} + +snd_rawmidi_ops_t snd_rawmidi_hw_ops = { + close: snd_rawmidi_hw_close, + nonblock: snd_rawmidi_hw_nonblock, + info: snd_rawmidi_hw_info, + params: snd_rawmidi_hw_params, + status: snd_rawmidi_hw_status, + drop: snd_rawmidi_hw_drop, + drain: snd_rawmidi_hw_drain, + write: snd_rawmidi_hw_write, + read: snd_rawmidi_hw_read, +}; + + +int snd_rawmidi_hw_open(snd_rawmidi_t **handlep, char *name, int card, int device, int subdevice, int streams, int mode) +{ + int fd, ver, ret; + int attempt = 0; + char filename[32]; + snd_ctl_t *ctl; + snd_rawmidi_t *rmidi; + snd_rawmidi_hw_t *hw; + snd_rawmidi_info_t info; + int fmode; + + *handlep = NULL; + + assert(card >= 0 && card < SND_CARDS); + + if ((ret = snd_ctl_hw_open(&ctl, NULL, card)) < 0) + return ret; + sprintf(filename, SND_FILE_RAWMIDI, card, device); + + __again: + if (attempt++ > 3) { + snd_ctl_close(ctl); + return -EBUSY; + } + ret = snd_ctl_rawmidi_prefer_subdevice(ctl, subdevice); + if (ret < 0) { + snd_ctl_close(ctl); + return ret; + } + + switch (streams) { + case SND_RAWMIDI_OPEN_OUTPUT: + fmode = O_WRONLY; + break; + case SND_RAWMIDI_OPEN_INPUT: + fmode = O_RDONLY; + break; + case SND_RAWMIDI_OPEN_DUPLEX: + fmode = O_RDWR; + break; + default: + assert(0); + return -EINVAL; + } + + if (mode & SND_RAWMIDI_APPEND) { + assert(streams & SND_RAWMIDI_OPEN_OUTPUT); + fmode |= O_APPEND; + } + + if (mode & SND_RAWMIDI_NONBLOCK) { + fmode |= O_NONBLOCK; + } + + assert(!(mode & ~(SND_RAWMIDI_APPEND|SND_RAWMIDI_NONBLOCK))); + + if ((fd = open(filename, fmode)) < 0) { + snd_card_load(card); + if ((fd = open(filename, fmode)) < 0) { + snd_ctl_close(ctl); + SYSERR("open %s failed", filename); + return -errno; + } + } + if (ioctl(fd, SND_RAWMIDI_IOCTL_PVERSION, &ver) < 0) { + ret = -errno; + SYSERR("SND_RAWMIDI_IOCTL_PVERSION failed"); + close(fd); + snd_ctl_close(ctl); + return ret; + } + if (SND_PROTOCOL_INCOMPATIBLE(ver, SND_RAWMIDI_VERSION_MAX)) { + close(fd); + snd_ctl_close(ctl); + return -SND_ERROR_INCOMPATIBLE_VERSION; + } + if (subdevice >= 0) { + memset(&info, 0, sizeof(info)); + if (ioctl(fd, SND_RAWMIDI_IOCTL_INFO, &info) < 0) { + SYSERR("SND_RAWMIDI_IOCTL_INFO failed"); + ret = -errno; + close(fd); + snd_ctl_close(ctl); + return ret; + } + if (info.subdevice != subdevice) { + close(fd); + goto __again; + } + } + hw = calloc(1, sizeof(snd_rawmidi_hw_t)); + if (hw == NULL) { + close(fd); + snd_ctl_close(ctl); + return -ENOMEM; + } + rmidi = calloc(1, sizeof(snd_rawmidi_t)); + if (rmidi == NULL) { + free(hw); + close(fd); + snd_ctl_close(ctl); + return -ENOMEM; + } + hw->card = card; + hw->device = device; + hw->subdevice = subdevice; + hw->fd = fd; + if (name) + rmidi->name = strdup(name); + rmidi->type = SND_RAWMIDI_TYPE_HW; + rmidi->streams = streams; + rmidi->mode = mode; + rmidi->poll_fd = fd; + rmidi->ops = &snd_rawmidi_hw_ops; + rmidi->private = hw; + *handlep = rmidi; + return 0; +} + +int _snd_rawmidi_hw_open(snd_rawmidi_t **handlep, char *name, snd_config_t *conf, + int streams, int mode) +{ + snd_config_iterator_t i; + long card = -1, device = 0, subdevice = -1; + char *str; + int err; + snd_config_foreach(i, conf) { + snd_config_t *n = snd_config_entry(i); + if (strcmp(n->id, "comment") == 0) + continue; + if (strcmp(n->id, "type") == 0) + continue; + if (strcmp(n->id, "streams") == 0) + continue; + if (strcmp(n->id, "card") == 0) { + err = snd_config_integer_get(n, &card); + if (err < 0) { + err = snd_config_string_get(n, &str); + if (err < 0) + return -EINVAL; + card = snd_card_get_index(str); + if (card < 0) + return card; + } + continue; + } + if (strcmp(n->id, "device") == 0) { + err = snd_config_integer_get(n, &device); + if (err < 0) + return err; + continue; + } + if (strcmp(n->id, "subdevice") == 0) { + err = snd_config_integer_get(n, &subdevice); + if (err < 0) + return err; + continue; + } + return -EINVAL; + } + if (card < 0) + return -EINVAL; + return snd_rawmidi_hw_open(handlep, name, card, device, subdevice, streams, mode); +} + diff --git a/src/rawmidi/rawmidi_local.h b/src/rawmidi/rawmidi_local.h new file mode 100644 index 00000000..81b303de --- /dev/null +++ b/src/rawmidi/rawmidi_local.h @@ -0,0 +1,60 @@ +/* + * Rawmidi interface - local header file + * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> + * + * + * 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 <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <errno.h> +#include "asoundlib.h" + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) +#define ERR(...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, 0, __VA_ARGS__) +#define SYSERR(...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, errno, __VA_ARGS__) +#else +#define ERR(args...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, 0, ##args) +#define SYSERR(args...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, errno, ##args) +#endif + +typedef struct { + int (*close)(snd_rawmidi_t *rawmidi); + int (*nonblock)(snd_rawmidi_t *rawmidi, int nonblock); + int (*info)(snd_rawmidi_t *rawmidi, snd_rawmidi_info_t *info); + int (*params)(snd_rawmidi_t *rawmidi, snd_rawmidi_params_t *params); + int (*status)(snd_rawmidi_t *rawmidi, snd_rawmidi_status_t *status); + int (*drop)(snd_rawmidi_t *rawmidi, int stream); + int (*drain)(snd_rawmidi_t *rawmidi, int stream); + ssize_t (*write)(snd_rawmidi_t *rawmidi, const void *buffer, size_t size); + ssize_t (*read)(snd_rawmidi_t *rawmidi, void *buffer, size_t size); +} snd_rawmidi_ops_t; + +struct _snd_rawmidi { + char *name; + snd_rawmidi_type_t type; + int streams; + int mode; + int poll_fd; + snd_rawmidi_ops_t *ops; + void *private; +}; + +int snd_rawmidi_hw_open(snd_rawmidi_t **handle, char *name, int card, int device, int subdevice, int streams, int mode); + diff --git a/src/seq/Makefile.am b/src/seq/Makefile.am index 637dfec8..530820e0 100644 --- a/src/seq/Makefile.am +++ b/src/seq/Makefile.am @@ -1,7 +1,7 @@ EXTRA_LTLIBRARIES=libseq.la -libseq_la_SOURCES = seq.c seqmid.c -noinst_HEADERS = seq_priv.h +libseq_la_SOURCES = seq_hw.c seq.c seqmid.c +noinst_HEADERS = seq_local.h all: libseq.la diff --git a/src/seq/seq.c b/src/seq/seq.c index c0774d21..ab4f269f 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -1,6 +1,7 @@ /* * Sequencer Interface - main file - * Copyright (c) 1998 by Jaroslav Kysela <perex@suse.cz> + * Copyright (c) 2000 by Jaroslav Kysela <perex@suse.cz> + * Abramo Bagnara <abramo@alsa-project.org> * * * This library is free software; you can redistribute it and/or modify @@ -19,81 +20,114 @@ * */ -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <fcntl.h> -#include <sys/ioctl.h> -#include <sys/time.h> +#include <sys/poll.h> +#include <dlfcn.h> +#include "seq_local.h" #include "asoundlib.h" -#include "seq_priv.h" -#define SND_FILE_SEQ "/dev/snd/seq" -#define SND_FILE_ALOADSEQ "/dev/aloadSEQ" -#define SND_SEQ_VERSION_MAX SND_PROTOCOL_VERSION(1, 0, 0) -#define SND_SEQ_OBUF_SIZE (16*1024) /* default size */ -#define SND_SEQ_IBUF_SIZE 500 /* in event_size aligned */ -#define DEFAULT_TMPBUF_SIZE 20 - -/* - * open a sequencer device so that it creates a user-client. - */ -int snd_seq_open(snd_seq_t **handle, int mode) +int snd_seq_open(snd_seq_t **seqp, char *name, + int streams, int mode) { - int fd, ver, client, flg; - char filename[32]; - snd_seq_t *seq; - - *handle = NULL; - - sprintf(filename, SND_FILE_SEQ); - if ((fd = open(filename, mode)) < 0) { - close(open(SND_FILE_ALOADSEQ, O_RDWR)); - if ((fd = open(filename, mode)) < 0) - return -errno; + char *str; + int err; + snd_config_t *seq_conf, *conf, *type_conf; + snd_config_iterator_t i; + char *lib = NULL, *open = NULL; + int (*open_func)(snd_seq_t **seqp, char *name, snd_config_t *conf, + int streams, int mode); + void *h; + assert(seqp && name); + err = snd_config_update(); + if (err < 0) + return err; + err = snd_config_searchv(snd_config, &seq_conf, "seq", name, 0); + if (err < 0) { + if (strcmp(name, "hw") == 0) + return snd_seq_hw_open(seqp, name, streams, mode); + ERR("Unknown SEQ %s", name); + return -ENOENT; } - if (ioctl(fd, SND_SEQ_IOCTL_PVERSION, &ver) < 0) { - close(fd); - return -errno; + if (snd_config_type(seq_conf) != SND_CONFIG_TYPE_COMPOUND) { + ERR("Invalid type for SEQ definition"); + return -EINVAL; + } + err = snd_config_search(seq_conf, "streams", &conf); + if (err >= 0) { + err = snd_config_string_get(conf, &str); + if (err < 0) { + ERR("Invalid type for streams"); + return err; + } + if (strcmp(str, "output") == 0) { + if (streams == SND_SEQ_OPEN_INPUT) + return -EINVAL; + } else if (strcmp(str, "input") == 0) { + if (streams == SND_SEQ_OPEN_OUTPUT) + return -EINVAL; + } else if (strcmp(str, "duplex") == 0) { + if (streams != SND_SEQ_OPEN_DUPLEX) + return -EINVAL; + } else { + ERR("Invalid value for streams"); + return -EINVAL; + } } - if (SND_PROTOCOL_INCOMPATIBLE(ver, SND_SEQ_VERSION_MAX)) { - close(fd); - return -SND_ERROR_INCOMPATIBLE_VERSION; + err = snd_config_search(seq_conf, "type", &conf); + if (err < 0) { + ERR("type is not defined"); + return err; } - if (ioctl(fd, SND_SEQ_IOCTL_CLIENT_ID, &client) < 0) { - close(fd); - return -errno; + err = snd_config_string_get(conf, &str); + if (err < 0) { + ERR("Invalid type for type"); + return err; } - seq = (snd_seq_t *) calloc(1, sizeof(snd_seq_t)); - if (seq == NULL) { - close(fd); - return -ENOMEM; + err = snd_config_searchv(snd_config, &type_conf, "seqtype", str, 0); + if (err < 0) { + ERR("Unknown SEQ type %s", str); + return err; } - seq->client = client; - seq->fd = fd; - flg = 3; - if (mode == SND_SEQ_OPEN_OUT || mode == SND_SEQ_OPEN) - seq->obuf = (char *) malloc(seq->obufsize = SND_SEQ_OBUF_SIZE); - else - flg &= ~1; - if (mode == SND_SEQ_OPEN_IN || mode == SND_SEQ_OPEN) - seq->ibuf = (snd_seq_event_t *) calloc(sizeof(snd_seq_event_t), seq->ibufsize = SND_SEQ_IBUF_SIZE); - else - flg &= ~2; - if ((!seq->obuf && (flg & 1)) || (!seq->ibuf && (flg & 2))) { - if (seq->obuf) - free(seq->obuf); - if (seq->ibuf) - free(seq->ibuf); - free(seq); - return -ENOMEM; + snd_config_foreach(i, type_conf) { + snd_config_t *n = snd_config_entry(i); + if (strcmp(n->id, "comment") == 0) + continue; + if (strcmp(n->id, "lib") == 0) { + err = snd_config_string_get(n, &lib); + if (err < 0) { + ERR("Invalid type for lib"); + return -EINVAL; + } + continue; + } + if (strcmp(n->id, "open") == 0) { + err = snd_config_string_get(n, &open); + if (err < 0) { + ERR("Invalid type for open"); + return -EINVAL; + } + continue; + ERR("Unknown field: %s", n->id); + return -EINVAL; + } } - seq->tmpbuf = NULL; - seq->tmpbufsize = 0; - *handle = seq; - return 0; + if (!open) { + ERR("open is not defined"); + return -EINVAL; + } + if (!lib) + lib = "libasound.so"; + h = dlopen(lib, RTLD_NOW); + if (!h) { + ERR("Cannot open shared library %s", lib); + return -ENOENT; + } + open_func = dlsym(h, open); + dlclose(h); + if (!open_func) { + ERR("symbol %s is not defined inside %s", open, lib); + return -ENXIO; + } + return open_func(seqp, name, seq_conf, streams, mode); } /* @@ -101,19 +135,21 @@ int snd_seq_open(snd_seq_t **handle, int mode) */ int snd_seq_close(snd_seq_t *seq) { - int res; - - if (!seq) - return -EINVAL; - res = close(seq->fd) < 0 ? -errno : 0; + int err; + assert(seq); + err = seq->ops->close(seq); + if (err < 0) + return err; if (seq->obuf) free(seq->obuf); if (seq->ibuf) free(seq->ibuf); if (seq->tmpbuf) free(seq->tmpbuf); + if (seq->name) + free(seq->name); free(seq); - return res; + return 0; } /* @@ -121,28 +157,24 @@ int snd_seq_close(snd_seq_t *seq) */ int snd_seq_poll_descriptor(snd_seq_t *seq) { - if (!seq) - return -EINVAL; - return seq->fd; + assert(seq); + return seq->poll_fd; } /* * set blocking behavior */ -int snd_seq_block_mode(snd_seq_t *seq, int enable) +int snd_seq_nonblock(snd_seq_t *seq, int nonblock) { - long flags; - - if (!seq) - return -EINVAL; - if ((flags = fcntl(seq->fd, F_GETFL)) < 0) - return -errno; - if (enable) - flags &= ~O_NONBLOCK; + int err; + assert(seq); + err = seq->ops->nonblock(seq, nonblock); + if (err < 0) + return err; + if (nonblock) + seq->mode |= SND_SEQ_NONBLOCK; else - flags |= O_NONBLOCK; - if (fcntl(seq->fd, F_SETFL, flags) < 0) - return -errno; + seq->mode &= ~SND_SEQ_NONBLOCK; return 0; } @@ -151,8 +183,7 @@ int snd_seq_block_mode(snd_seq_t *seq, int enable) */ int snd_seq_client_id(snd_seq_t *seq) { - if (!seq) - return -EINVAL; + assert(seq); return seq->client; } @@ -161,8 +192,7 @@ int snd_seq_client_id(snd_seq_t *seq) */ int snd_seq_output_buffer_size(snd_seq_t *seq) { - if (!seq) - return -EINVAL; + assert(seq); if (!seq->obuf) return 0; return seq->obufsize; @@ -173,8 +203,7 @@ int snd_seq_output_buffer_size(snd_seq_t *seq) */ int snd_seq_input_buffer_size(snd_seq_t *seq) { - if (!seq) - return -EINVAL; + assert(seq); if (!seq->ibuf) return 0; return seq->ibufsize * sizeof(snd_seq_event_t); @@ -185,10 +214,8 @@ int snd_seq_input_buffer_size(snd_seq_t *seq) */ int snd_seq_resize_output_buffer(snd_seq_t *seq, size_t size) { - if (!seq || !seq->obuf) - return -EINVAL; - if (size < sizeof(snd_seq_event_t)) - return -EINVAL; + assert(seq && seq->obuf); + assert(size >= sizeof(snd_seq_event_t)); snd_seq_drop_output(seq); if (size != seq->obufsize) { char *newbuf; @@ -207,10 +234,8 @@ int snd_seq_resize_output_buffer(snd_seq_t *seq, size_t size) */ int snd_seq_resize_input_buffer(snd_seq_t *seq, size_t size) { - if (!seq || !seq->ibuf) - return -EINVAL; - if (size < sizeof(snd_seq_event_t)) - return -EINVAL; + assert(seq && seq->ibuf); + assert(size >= sizeof(snd_seq_event_t)); snd_seq_drop_input(seq); size = (size + sizeof(snd_seq_event_t) - 1) / sizeof(snd_seq_event_t); if (size != seq->ibufsize) { @@ -230,35 +255,27 @@ int snd_seq_resize_input_buffer(snd_seq_t *seq, size_t size) */ int snd_seq_system_info(snd_seq_t *seq, snd_seq_system_info_t * info) { - if (!seq || !info) - return -EINVAL; - if (ioctl(seq->fd, SND_SEQ_IOCTL_SYSTEM_INFO, info) < 0) - return -errno; - return 0; + assert(seq && info); + return seq->ops->system_info(seq, info); } /* - * obtain the current client information + * obtain the information of given client */ -int snd_seq_get_client_info(snd_seq_t *seq, snd_seq_client_info_t * info) +int snd_seq_get_any_client_info(snd_seq_t *seq, int client, snd_seq_client_info_t * info) { - if (!seq || !info) - return -EINVAL; - return snd_seq_get_any_client_info(seq, seq->client, info); + assert(seq && info && client >= 0); + memset(info, 0, sizeof(snd_seq_client_info_t)); + info->client = client; + return seq->ops->get_client_info(seq, info); } /* - * obtain the information of given client + * obtain the current client information */ -int snd_seq_get_any_client_info(snd_seq_t *seq, int client, snd_seq_client_info_t * info) +int snd_seq_get_client_info(snd_seq_t *seq, snd_seq_client_info_t * info) { - if (!seq || !info || client < 0) - return -EINVAL; - bzero(info, sizeof(snd_seq_client_info_t)); - info->client = client; - if (ioctl(seq->fd, SND_SEQ_IOCTL_GET_CLIENT_INFO, info) < 0) - return -errno; - return 0; + return snd_seq_get_any_client_info(seq, seq->client, info); } /* @@ -266,13 +283,10 @@ int snd_seq_get_any_client_info(snd_seq_t *seq, int client, snd_seq_client_info_ */ int snd_seq_set_client_info(snd_seq_t *seq, snd_seq_client_info_t * info) { - if (!seq || !info) - return -EINVAL; + assert(seq && info); info->client = seq->client; info->type = USER_CLIENT; - if (ioctl(seq->fd, SND_SEQ_IOCTL_SET_CLIENT_INFO, info) < 0) - return -errno; - return 0; + return seq->ops->set_client_info(seq, info); } /*----------------------------------------------------------------*/ @@ -283,51 +297,37 @@ int snd_seq_set_client_info(snd_seq_t *seq, snd_seq_client_info_t * info) int snd_seq_create_port(snd_seq_t *seq, snd_seq_port_info_t * port) { - if (!seq || !port) - return -EINVAL; + assert(seq && port); port->client = seq->client; - if (ioctl(seq->fd, SND_SEQ_IOCTL_CREATE_PORT, port) < 0) - return -errno; - return 0; + return seq->ops->create_port(seq, port); } int snd_seq_delete_port(snd_seq_t *seq, snd_seq_port_info_t * port) { - if (!seq || !port) - return -EINVAL; + assert(seq && port); port->client = seq->client; - if (ioctl(seq->fd, SND_SEQ_IOCTL_DELETE_PORT, port) < 0) - return -errno; - return 0; -} - -int snd_seq_get_port_info(snd_seq_t *seq, int port, snd_seq_port_info_t * info) -{ - if (!seq || !info || port < 0) - return -EINVAL; - return snd_seq_get_any_port_info(seq, seq->client, port, info); + return seq->ops->delete_port(seq, port); } int snd_seq_get_any_port_info(snd_seq_t *seq, int client, int port, snd_seq_port_info_t * info) { - if (!seq || !info || client < 0 || port < 0) - return -EINVAL; - bzero(info, sizeof(snd_seq_port_info_t)); + assert(seq && info && client >= 0 && port >= 0); + memset(info, 0, sizeof(snd_seq_port_info_t)); info->client = client; info->port = port; - if (ioctl(seq->fd, SND_SEQ_IOCTL_GET_PORT_INFO, info) < 0) - return -errno; - return 0; + return seq->ops->get_port_info(seq, info); +} + +int snd_seq_get_port_info(snd_seq_t *seq, int port, snd_seq_port_info_t * info) +{ + return snd_seq_get_any_port_info(seq, seq->client, port, info); } int snd_seq_set_port_info(snd_seq_t *seq, int port, snd_seq_port_info_t * info) { - if (!seq || !info || port < 0) - return -EINVAL; + assert(seq && info && port >= 0); info->port = port; - if (ioctl(seq->fd, SND_SEQ_IOCTL_SET_PORT_INFO, info) < 0) - return -errno; - return 0; + return seq->ops->set_port_info(seq, info); } /*----------------------------------------------------------------*/ @@ -338,38 +338,26 @@ int snd_seq_set_port_info(snd_seq_t *seq, int port, snd_seq_port_info_t * info) int snd_seq_get_port_subscription(snd_seq_t *seq, snd_seq_port_subscribe_t * sub) { - if (!seq || !sub) - return -EINVAL; - if (ioctl(seq->fd, SND_SEQ_IOCTL_GET_SUBSCRIPTION, sub) < 0) - return -errno; - return 0; + assert(seq && sub); + return seq->ops->get_port_subscription(seq, sub); } int snd_seq_subscribe_port(snd_seq_t *seq, snd_seq_port_subscribe_t * sub) { - if (!seq || !sub) - return -EINVAL; - if (ioctl(seq->fd, SND_SEQ_IOCTL_SUBSCRIBE_PORT, sub) < 0) - return -errno; - return 0; + assert(seq && sub); + return seq->ops->subscribe_port(seq, sub); } int snd_seq_unsubscribe_port(snd_seq_t *seq, snd_seq_port_subscribe_t * sub) { - if (!seq || !sub) - return -EINVAL; - if (ioctl(seq->fd, SND_SEQ_IOCTL_UNSUBSCRIBE_PORT, sub) < 0) - return -errno; - return 0; + assert(seq && sub); + return seq->ops->unsubscribe_port(seq, sub); } int snd_seq_query_port_subscribers(snd_seq_t *seq, snd_seq_query_subs_t * subs) { - if (!seq || !subs) - return -EINVAL; - if (ioctl(seq->fd, SND_SEQ_IOCTL_QUERY_SUBS, subs) < 0) - return -errno; - return 0; + assert(seq && subs); + return seq->ops->query_port_subscribers(seq, subs); } /*----------------------------------------------------------------*/ @@ -380,118 +368,88 @@ int snd_seq_query_port_subscribers(snd_seq_t *seq, snd_seq_query_subs_t * subs) int snd_seq_get_queue_status(snd_seq_t *seq, int q, snd_seq_queue_status_t * status) { - if (!seq || !status) - return -EINVAL; - bzero(status, sizeof(snd_seq_queue_status_t)); + assert(seq && status); + memset(status, 0, sizeof(snd_seq_queue_status_t)); status->queue = q; - if (ioctl(seq->fd, SND_SEQ_IOCTL_GET_QUEUE_STATUS, status) < 0) - return -errno; - return 0; + return seq->ops->get_queue_status(seq, status); } int snd_seq_get_queue_tempo(snd_seq_t *seq, int q, snd_seq_queue_tempo_t * tempo) { - if (!seq || !tempo) - return -EINVAL; - bzero(tempo, sizeof(snd_seq_queue_tempo_t)); + assert(seq && tempo); + memset(tempo, 0, sizeof(snd_seq_queue_tempo_t)); tempo->queue = q; - if (ioctl(seq->fd, SND_SEQ_IOCTL_GET_QUEUE_TEMPO, tempo) < 0) - return -errno; - return 0; + return seq->ops->get_queue_tempo(seq, tempo); } int snd_seq_set_queue_tempo(snd_seq_t *seq, int q, snd_seq_queue_tempo_t * tempo) { - if (!seq || !tempo) - return -EINVAL; + assert(seq && tempo); tempo->queue = q; - if (ioctl(seq->fd, SND_SEQ_IOCTL_SET_QUEUE_TEMPO, tempo) < 0) - return -errno; - return 0; + return seq->ops->set_queue_tempo(seq, tempo); } int snd_seq_get_queue_owner(snd_seq_t *seq, int q, snd_seq_queue_owner_t * owner) { - if (!seq || !owner) - return -EINVAL; - bzero(owner, sizeof(snd_seq_queue_owner_t)); + assert(seq && owner); + memset(owner, 0, sizeof(snd_seq_queue_owner_t)); owner->queue = q; - if (ioctl(seq->fd, SND_SEQ_IOCTL_GET_QUEUE_OWNER, owner) < 0) - return -errno; - return 0; + return seq->ops->get_queue_owner(seq, owner); } int snd_seq_set_queue_owner(snd_seq_t *seq, int q, snd_seq_queue_owner_t * owner) { - if (!seq || !owner) - return -EINVAL; + assert(seq && owner); owner->queue = q; - if (ioctl(seq->fd, SND_SEQ_IOCTL_SET_QUEUE_OWNER, owner) < 0) - return -errno; - return 0; + return seq->ops->set_queue_owner(seq, owner); } int snd_seq_get_queue_timer(snd_seq_t *seq, int q, snd_seq_queue_timer_t * timer) { - if (!seq || !timer) - return -EINVAL; - bzero(timer, sizeof(snd_seq_queue_timer_t)); + assert(seq && timer); + memset(timer, 0, sizeof(snd_seq_queue_timer_t)); timer->queue = q; - if (ioctl(seq->fd, SND_SEQ_IOCTL_GET_QUEUE_TIMER, timer) < 0) - return -errno; - return 0; + return seq->ops->get_queue_timer(seq, timer); } int snd_seq_set_queue_timer(snd_seq_t *seq, int q, snd_seq_queue_timer_t * timer) { - if (!seq || !timer) - return -EINVAL; + assert(seq && timer); timer->queue = q; - if (ioctl(seq->fd, SND_SEQ_IOCTL_SET_QUEUE_TIMER, timer) < 0) - return -errno; - return 0; + return seq->ops->set_queue_timer(seq, timer); } int snd_seq_get_queue_client(snd_seq_t *seq, int q, snd_seq_queue_client_t * info) { - if (!seq || !info) - return -EINVAL; - bzero(info, sizeof(snd_seq_queue_client_t)); + assert(seq && info); + memset(info, 0, sizeof(snd_seq_queue_client_t)); info->queue = q; info->client = seq->client; - if (ioctl(seq->fd, SND_SEQ_IOCTL_GET_QUEUE_CLIENT, info) < 0) - return -errno; - return 0; + return seq->ops->get_queue_client(seq, info); } int snd_seq_set_queue_client(snd_seq_t *seq, int q, snd_seq_queue_client_t * info) { - if (!seq || !info) - return -EINVAL; + assert(seq && info); info->queue = q; info->client = seq->client; - if (ioctl(seq->fd, SND_SEQ_IOCTL_SET_QUEUE_CLIENT, info) < 0) - return -errno; - return 0; + return seq->ops->set_queue_client(seq, info); } int snd_seq_create_queue(snd_seq_t *seq, snd_seq_queue_info_t *info) { - if (!seq) - return -EINVAL; + int err; + assert(seq && info); info->owner = seq->client; - if (ioctl(seq->fd, SND_SEQ_IOCTL_CREATE_QUEUE, info) < 0) - return -errno; + err = seq->ops->create_queue(seq, info); + if (err < 0) + return err; return info->queue; } int snd_seq_alloc_named_queue(snd_seq_t *seq, char *name) { snd_seq_queue_info_t info; - - if (!seq) - return -EINVAL; - memset(&info, 0, sizeof(info)); info.locked = 1; if (name) @@ -508,10 +466,6 @@ int snd_seq_alloc_queue(snd_seq_t *seq) int snd_seq_alloc_sync_queue(snd_seq_t *seq, char *name) { snd_seq_queue_info_t info; - - if (!seq) - return -EINVAL; - memset(&info, 0, sizeof(info)); info.locked = 1; if (name) @@ -524,47 +478,35 @@ int snd_seq_alloc_sync_queue(snd_seq_t *seq, char *name) int snd_seq_free_queue(snd_seq_t *seq, int q) { snd_seq_queue_info_t info; - - if (!seq) - return -EINVAL; - + assert(seq); memset(&info, 0, sizeof(info)); info.queue = q; - if (ioctl(seq->fd, SND_SEQ_IOCTL_DELETE_QUEUE, &info) < 0) - return -errno; - - return 0; + return seq->ops->delete_queue(seq, &info); } int snd_seq_get_queue_info(snd_seq_t *seq, int q, snd_seq_queue_info_t *info) { - if (!seq || !info) - return -EINVAL; + assert(seq && info); info->queue = q; - if (ioctl(seq->fd, SND_SEQ_IOCTL_GET_QUEUE_INFO, info) < 0) - return -errno; - return 0; + return seq->ops->get_queue_info(seq, info); } int snd_seq_set_queue_info(snd_seq_t *seq, int q, snd_seq_queue_info_t *info) { - if (!seq || !info) - return -EINVAL; + assert(seq && info); info->queue = q; - if (ioctl(seq->fd, SND_SEQ_IOCTL_SET_QUEUE_INFO, info) < 0) - return -errno; - return 0; + return seq->ops->set_queue_info(seq, info); } int snd_seq_get_named_queue(snd_seq_t *seq, char *name) { + int err; snd_seq_queue_info_t info; - - if (!seq) - return -EINVAL; + assert(seq && name); strncpy(info.name, name, sizeof(info.name)); - if (ioctl(seq->fd, SND_SEQ_IOCTL_GET_NAMED_QUEUE, &info) < 0) - return -errno; + err = seq->ops->get_named_queue(seq, &info); + if (err < 0) + return err; return info.queue; } @@ -687,9 +629,7 @@ int snd_seq_free_event(snd_seq_event_t *ev ATTRIBUTE_UNUSED) ssize_t snd_seq_event_length(snd_seq_event_t *ev) { ssize_t len = sizeof(snd_seq_event_t); - - if (!ev) - return -EINVAL; + assert(ev); if (snd_seq_ev_is_variable(ev)) len += ev->data.ext.len; return len; @@ -726,9 +666,7 @@ int snd_seq_event_output(snd_seq_t *seq, snd_seq_event_t *ev) int snd_seq_event_output_buffer(snd_seq_t *seq, snd_seq_event_t *ev) { int len; - - if (!seq || !ev) - return -EINVAL; + assert(seq && ev); len = snd_seq_event_length(ev); if (len < 0) return -EINVAL; @@ -773,7 +711,7 @@ static int alloc_tmpbuf(snd_seq_t *seq, size_t len) */ int snd_seq_event_output_direct(snd_seq_t *seq, snd_seq_event_t *ev) { - ssize_t len, result; + ssize_t len; void *buf; len = snd_seq_event_length(ev); @@ -789,9 +727,7 @@ int snd_seq_event_output_direct(snd_seq_t *seq, snd_seq_event_t *ev) buf = seq->tmpbuf; } - result = write(seq->fd, buf, len); - - return (result < 0) ? -errno : (int)result; + return seq->ops->write(seq, buf, len); } /* @@ -799,8 +735,7 @@ int snd_seq_event_output_direct(snd_seq_t *seq, snd_seq_event_t *ev) */ int snd_seq_event_output_pending(snd_seq_t *seq) { - if (!seq) - return -EINVAL; + assert(seq); return seq->obufused; } @@ -809,14 +744,12 @@ int snd_seq_event_output_pending(snd_seq_t *seq) */ int snd_seq_drain_output(snd_seq_t *seq) { - int result; - - if (!seq) - return -EINVAL; + ssize_t result; + assert(seq); while (seq->obufused > 0) { - result = write(seq->fd, seq->obuf, seq->obufused); + result = seq->ops->write(seq, seq->obuf, seq->obufused); if (result < 0) - return -errno; + return -result; if ((size_t)result < seq->obufused) memmove(seq->obuf, seq->obuf + result, seq->obufused - result); seq->obufused -= result; @@ -832,9 +765,7 @@ int snd_seq_extract_output(snd_seq_t *seq, snd_seq_event_t **ev_res) { size_t len, olen; snd_seq_event_t ev; - - if (!seq) - return -EINVAL; + assert(seq); if (ev_res) *ev_res = NULL; if ((olen = seq->obufused) < sizeof(snd_seq_event_t)) @@ -865,9 +796,9 @@ int snd_seq_extract_output(snd_seq_t *seq, snd_seq_event_t **ev_res) static ssize_t snd_seq_event_read_buffer(snd_seq_t *seq) { ssize_t len; - len = read(seq->fd, seq->ibuf, seq->ibufsize * sizeof(snd_seq_event_t)); + len = seq->ops->read(seq, seq->ibuf, seq->ibufsize * sizeof(snd_seq_event_t)); if (len < 0) - return -errno; + return len; seq->ibuflen = len / sizeof(snd_seq_event_t); seq->ibufptr = 0; return seq->ibuflen; @@ -901,11 +832,8 @@ static int snd_seq_event_retrieve_buffer(snd_seq_t *seq, snd_seq_event_t **retp) int snd_seq_event_input(snd_seq_t *seq, snd_seq_event_t **ev) { int err; - + assert(seq); *ev = NULL; - if (!seq) - return -EINVAL; - if (seq->ibuflen <= 0) { if ((err = snd_seq_event_read_buffer(seq)) < 0) return err; @@ -917,15 +845,18 @@ int snd_seq_event_input(snd_seq_t *seq, snd_seq_event_t **ev) /* * read input data from sequencer if available */ -static int snd_seq_event_input_feed(snd_seq_t *seq, struct timeval *timeout) +static int snd_seq_event_input_feed(snd_seq_t *seq, int timeout) { - fd_set rfds; - - FD_ZERO(&rfds); - FD_SET(seq->fd, &rfds); - if (select(seq->fd + 1, &rfds, NULL, NULL, timeout) < 0) + struct pollfd pfd; + int err; + pfd.fd = seq->poll_fd; + pfd.events = POLLIN; + err = poll(&pfd, 1, timeout); + if (err < 0) { + SYSERR("poll"); return -errno; - if (FD_ISSET(seq->fd, &rfds)) + } + if (pfd.revents & POLLIN) return snd_seq_event_read_buffer(seq); return seq->ibuflen; } @@ -936,10 +867,7 @@ static int snd_seq_event_input_feed(snd_seq_t *seq, struct timeval *timeout) int snd_seq_event_input_pending(snd_seq_t *seq, int fetch_sequencer) { if (seq->ibuflen == 0 && fetch_sequencer) { - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 0; - return snd_seq_event_input_feed(seq, &tv); + return snd_seq_event_input_feed(seq, 0); } return seq->ibuflen; } @@ -955,8 +883,7 @@ int snd_seq_event_input_pending(snd_seq_t *seq, int fetch_sequencer) */ int snd_seq_drop_output_buffer(snd_seq_t *seq) { - if (!seq) - return -EINVAL; + assert(seq); seq->obufused = 0; return 0; } @@ -966,8 +893,7 @@ int snd_seq_drop_output_buffer(snd_seq_t *seq) */ int snd_seq_drop_input_buffer(snd_seq_t *seq) { - if (!seq) - return -EINVAL; + assert(seq); seq->ibufptr = 0; seq->ibuflen = 0; return 0; @@ -979,10 +905,7 @@ int snd_seq_drop_input_buffer(snd_seq_t *seq) int snd_seq_drop_output(snd_seq_t *seq) { snd_seq_remove_events_t rminfo; - - if (!seq) - return -EINVAL; - + assert(seq); seq->obufused = 0; /* drain output buffer */ memset(&rminfo, 0, sizeof(rminfo)); @@ -997,9 +920,7 @@ int snd_seq_drop_output(snd_seq_t *seq) int snd_seq_drop_input(snd_seq_t *seq) { snd_seq_remove_events_t rminfo; - - if (!seq) - return -EINVAL; + assert(seq); seq->ibufptr = 0; /* drain input buffer */ seq->ibuflen = 0; @@ -1131,10 +1052,7 @@ int snd_seq_remove_events(snd_seq_t *seq, snd_seq_remove_events_t *rmp) } } - if (ioctl(seq->fd, SND_SEQ_IOCTL_REMOVE_EVENTS, rmp) < 0) - return -errno; - - return 0; + return seq->ops->remove_events(seq, rmp); } /*----------------------------------------------------------------*/ @@ -1145,22 +1063,16 @@ int snd_seq_remove_events(snd_seq_t *seq, snd_seq_remove_events_t *rmp) int snd_seq_get_client_pool(snd_seq_t *seq, snd_seq_client_pool_t *info) { - if (!seq || !info) - return -EINVAL; + assert(seq && info); info->client = seq->client; - if (ioctl(seq->fd, SND_SEQ_IOCTL_GET_CLIENT_POOL, info) < 0) - return -errno; - return 0; + return seq->ops->get_client_pool(seq, info); } int snd_seq_set_client_pool(snd_seq_t *seq, snd_seq_client_pool_t *info) { - if (!seq || !info) - return -EINVAL; + assert(seq && info); info->client = seq->client; - if (ioctl(seq->fd, SND_SEQ_IOCTL_SET_CLIENT_POOL, info) < 0) - return -errno; - return 0; + return seq->ops->set_client_pool(seq, info); } /*----------------------------------------------------------------*/ @@ -1171,20 +1083,14 @@ int snd_seq_set_client_pool(snd_seq_t *seq, snd_seq_client_pool_t *info) int snd_seq_query_next_client(snd_seq_t *seq, snd_seq_client_info_t *info) { - if (!seq || !info) - return -EINVAL; - if (ioctl(seq->fd, SND_SEQ_IOCTL_QUERY_NEXT_CLIENT, info) < 0) - return -errno; - return 0; + assert(seq && info); + return seq->ops->query_next_client(seq, info); } int snd_seq_query_next_port(snd_seq_t *seq, snd_seq_port_info_t *info) { - if (!seq || !info) - return -EINVAL; - if (ioctl(seq->fd, SND_SEQ_IOCTL_QUERY_NEXT_PORT, info) < 0) - return -errno; - return 0; + assert(seq && info); + return seq->ops->query_next_port(seq, info); } /*----------------------------------------------------------------*/ diff --git a/src/seq/seq_hw.c b/src/seq/seq_hw.c new file mode 100644 index 00000000..4f7e243c --- /dev/null +++ b/src/seq/seq_hw.c @@ -0,0 +1,545 @@ +/* + * Sequencer Interface - main file + * Copyright (c) 2000 by Jaroslav Kysela <perex@suse.cz> + * Abramo Bagnara <abramo@alsa-project.org> + * + * + * 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 <fcntl.h> +#include <sys/ioctl.h> +#include "seq_local.h" +#include "asoundlib.h" + +#define SND_FILE_SEQ "/dev/snd/seq" +#define SND_FILE_ALOADSEQ "/dev/aloadSEQ" +#define SND_SEQ_VERSION_MAX SND_PROTOCOL_VERSION(1, 0, 0) + +typedef struct { + int fd; +} snd_seq_hw_t; + +static int snd_seq_hw_close(snd_seq_t *seq) +{ + snd_seq_hw_t *hw = seq->private; + if (close(hw->fd)) { + SYSERR("close failed\n"); + return -errno; + } + free(hw); + return 0; +} + +static int snd_seq_hw_nonblock(snd_seq_t *seq, int nonblock) +{ + snd_seq_hw_t *hw = seq->private; + long flags; + + if ((flags = fcntl(hw->fd, F_GETFL)) < 0) { + SYSERR("F_GETFL failed"); + return -errno; + } + if (nonblock) + flags &= ~O_NONBLOCK; + else + flags |= O_NONBLOCK; + if (fcntl(hw->fd, F_SETFL, flags) < 0) { + SYSERR("F_SETFL for O_NONBLOCK failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_client_id(snd_seq_t *seq) +{ + snd_seq_hw_t *hw = seq->private; + int client; + if (ioctl(hw->fd, SND_SEQ_IOCTL_CLIENT_ID, &client) < 0) { + SYSERR("SND_SEQ_IOCTL_CLIENT_ID failed"); + return -errno; + } + return client; +} + +static int snd_seq_hw_system_info(snd_seq_t *seq, snd_seq_system_info_t * info) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_SYSTEM_INFO, info) < 0) { + SYSERR("SND_SEQ_IOCTL_SYSTEM_INFO failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_get_client_info(snd_seq_t *seq, snd_seq_client_info_t * info) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_GET_CLIENT_INFO, info) < 0) { + SYSERR("SND_SEQ_IOCTL_GET_CLIENT_INFO failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_set_client_info(snd_seq_t *seq, snd_seq_client_info_t * info) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_SET_CLIENT_INFO, info) < 0) { + SYSERR("SND_SEQ_IOCTL_SET_CLIENT_INFO failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_create_port(snd_seq_t *seq, snd_seq_port_info_t * port) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_CREATE_PORT, port) < 0) { + SYSERR("SND_SEQ_IOCTL_CREATE_PORT failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_delete_port(snd_seq_t *seq, snd_seq_port_info_t * port) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_DELETE_PORT, port) < 0) { + SYSERR("SND_SEQ_IOCTL_DELETE_PORT failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_get_port_info(snd_seq_t *seq, snd_seq_port_info_t * info) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_GET_PORT_INFO, info) < 0) { + SYSERR("SND_SEQ_IOCTL_GET_PORT_INFO failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_set_port_info(snd_seq_t *seq, snd_seq_port_info_t * info) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_SET_PORT_INFO, info) < 0) { + SYSERR("SND_SEQ_IOCTL_SET_PORT_INFO failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_get_port_subscription(snd_seq_t *seq, snd_seq_port_subscribe_t * sub) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_GET_SUBSCRIPTION, sub) < 0) { + SYSERR("SND_SEQ_IOCTL_GET_SUBSCRIPTION failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_subscribe_port(snd_seq_t *seq, snd_seq_port_subscribe_t * sub) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_SUBSCRIBE_PORT, sub) < 0) { + SYSERR("SND_SEQ_IOCTL_SUBSCRIBE_PORT failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_unsubscribe_port(snd_seq_t *seq, snd_seq_port_subscribe_t * sub) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_UNSUBSCRIBE_PORT, sub) < 0) { + SYSERR("SND_SEQ_IOCTL_UNSUBSCRIBE_PORT failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_query_port_subscribers(snd_seq_t *seq, snd_seq_query_subs_t * subs) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_QUERY_SUBS, subs) < 0) { + SYSERR("SND_SEQ_IOCTL_QUERY_SUBS failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_get_queue_status(snd_seq_t *seq, snd_seq_queue_status_t * status) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_GET_QUEUE_STATUS, status) < 0) { + SYSERR("SND_SEQ_IOCTL_GET_QUEUE_STATUS failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_get_queue_tempo(snd_seq_t *seq, snd_seq_queue_tempo_t * tempo) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_GET_QUEUE_TEMPO, tempo) < 0) { + SYSERR("SND_SEQ_IOCTL_GET_QUEUE_TEMPO failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_set_queue_tempo(snd_seq_t *seq, snd_seq_queue_tempo_t * tempo) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_SET_QUEUE_TEMPO, tempo) < 0) { + SYSERR("SND_SEQ_IOCTL_SET_QUEUE_TEMPO failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_get_queue_owner(snd_seq_t *seq, snd_seq_queue_owner_t * owner) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_GET_QUEUE_OWNER, owner) < 0) { + SYSERR("SND_SEQ_IOCTL_GET_QUEUE_OWNER failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_set_queue_owner(snd_seq_t *seq, snd_seq_queue_owner_t * owner) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_SET_QUEUE_OWNER, owner) < 0) { + SYSERR("SND_SEQ_IOCTL_SET_QUEUE_OWNER failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_get_queue_timer(snd_seq_t *seq, snd_seq_queue_timer_t * timer) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_GET_QUEUE_TIMER, timer) < 0) { + SYSERR("SND_SEQ_IOCTL_GET_QUEUE_TIMER failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_set_queue_timer(snd_seq_t *seq, snd_seq_queue_timer_t * timer) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_SET_QUEUE_TIMER, timer) < 0) { + SYSERR("SND_SEQ_IOCTL_SET_QUEUE_TIMER failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_get_queue_client(snd_seq_t *seq, snd_seq_queue_client_t * info) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_GET_QUEUE_CLIENT, info) < 0) { + SYSERR("SND_SEQ_IOCTL_GET_QUEUE_CLIENT failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_set_queue_client(snd_seq_t *seq, snd_seq_queue_client_t * info) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_SET_QUEUE_CLIENT, info) < 0) { + SYSERR("SND_SEQ_IOCTL_SET_QUEUE_CLIENT failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_create_queue(snd_seq_t *seq, snd_seq_queue_info_t *info) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_CREATE_QUEUE, info) < 0) { + SYSERR("SND_SEQ_IOCTL_CREATE_QUEUE failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_delete_queue(snd_seq_t *seq, snd_seq_queue_info_t *info) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_DELETE_QUEUE, info) < 0) { + SYSERR("SND_SEQ_IOCTL_DELETE_QUEUE failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_get_queue_info(snd_seq_t *seq, snd_seq_queue_info_t *info) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_GET_QUEUE_INFO, info) < 0) { + SYSERR("SND_SEQ_IOCTL_GET_QUEUE_INFO failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_set_queue_info(snd_seq_t *seq, snd_seq_queue_info_t *info) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_SET_QUEUE_INFO, info) < 0) { + SYSERR("SND_SEQ_IOCTL_SET_QUEUE_INFO failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_get_named_queue(snd_seq_t *seq, snd_seq_queue_info_t *info) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_GET_NAMED_QUEUE, info) < 0) { + SYSERR("SND_SEQ_IOCTL_GET_NAMED_QUEUE failed"); + return -errno; + } + return 0; +} + +static ssize_t snd_seq_hw_write(snd_seq_t *seq, void *buf, size_t len) +{ + snd_seq_hw_t *hw = seq->private; + ssize_t result = write(hw->fd, buf, len); + if (result < 0) + return -errno; + return result; +} + +static ssize_t snd_seq_hw_read(snd_seq_t *seq, void *buf, size_t len) +{ + snd_seq_hw_t *hw = seq->private; + ssize_t result = read(hw->fd, buf, len); + if (result < 0) + return -errno; + return result; +} + +static int snd_seq_hw_remove_events(snd_seq_t *seq, snd_seq_remove_events_t *rmp) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_REMOVE_EVENTS, rmp) < 0) { + SYSERR("SND_SEQ_IOCTL_REMOVE_EVENTS failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_get_client_pool(snd_seq_t *seq, snd_seq_client_pool_t *info) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_GET_CLIENT_POOL, info) < 0) { + SYSERR("SND_SEQ_IOCTL_GET_CLIENT_POOL failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_set_client_pool(snd_seq_t *seq, snd_seq_client_pool_t *info) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_SET_CLIENT_POOL, info) < 0) { + SYSERR("SND_SEQ_IOCTL_SET_CLIENT_POOL failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_query_next_client(snd_seq_t *seq, snd_seq_client_info_t *info) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_QUERY_NEXT_CLIENT, info) < 0) { + SYSERR("SND_SEQ_IOCTL_QUERY_NEXT_CLIENT failed"); + return -errno; + } + return 0; +} + +static int snd_seq_hw_query_next_port(snd_seq_t *seq, snd_seq_port_info_t *info) +{ + snd_seq_hw_t *hw = seq->private; + if (ioctl(hw->fd, SND_SEQ_IOCTL_QUERY_NEXT_PORT, info) < 0) { + SYSERR("SND_SEQ_IOCTL_QUERY_NEXT_PORT failed"); + return -errno; + } + return 0; +} + +snd_seq_ops_t snd_seq_hw_ops = { + close: snd_seq_hw_close, + nonblock: snd_seq_hw_nonblock, + system_info: snd_seq_hw_system_info, + get_client_info: snd_seq_hw_get_client_info, + set_client_info: snd_seq_hw_set_client_info, + create_port: snd_seq_hw_create_port, + delete_port: snd_seq_hw_delete_port, + get_port_info: snd_seq_hw_get_port_info, + set_port_info: snd_seq_hw_set_port_info, + get_port_subscription: snd_seq_hw_get_port_subscription, + subscribe_port: snd_seq_hw_subscribe_port, + unsubscribe_port: snd_seq_hw_unsubscribe_port, + query_port_subscribers: snd_seq_hw_query_port_subscribers, + get_queue_status: snd_seq_hw_get_queue_status, + get_queue_tempo: snd_seq_hw_get_queue_tempo, + set_queue_tempo: snd_seq_hw_set_queue_tempo, + get_queue_owner: snd_seq_hw_get_queue_owner, + set_queue_owner: snd_seq_hw_set_queue_owner, + get_queue_timer: snd_seq_hw_get_queue_timer, + set_queue_timer: snd_seq_hw_set_queue_timer, + get_queue_client: snd_seq_hw_get_queue_client, + set_queue_client: snd_seq_hw_set_queue_client, + create_queue: snd_seq_hw_create_queue, + delete_queue: snd_seq_hw_delete_queue, + get_queue_info: snd_seq_hw_get_queue_info, + set_queue_info: snd_seq_hw_set_queue_info, + get_named_queue: snd_seq_hw_get_named_queue, + write: snd_seq_hw_write, + read: snd_seq_hw_read, + remove_events: snd_seq_hw_remove_events, + get_client_pool: snd_seq_hw_get_client_pool, + set_client_pool: snd_seq_hw_set_client_pool, + query_next_client: snd_seq_hw_query_next_client, + query_next_port: snd_seq_hw_query_next_port, +}; + +int snd_seq_hw_open(snd_seq_t **handle, char *name, int streams, int mode) +{ + int fd, ver, client, fmode; + char filename[32]; + snd_seq_t *seq; + snd_seq_hw_t *hw; + + *handle = NULL; + + switch (streams) { + case SND_SEQ_OPEN_OUTPUT: + fmode = O_WRONLY; + break; + case SND_SEQ_OPEN_INPUT: + fmode = O_RDONLY; + break; + case SND_SEQ_OPEN_DUPLEX: + fmode = O_RDWR; + break; + default: + assert(0); + return -EINVAL; + } + + if (mode & SND_SEQ_NONBLOCK) + fmode |= O_NONBLOCK; + + sprintf(filename, SND_FILE_SEQ); + if ((fd = open(filename, fmode)) < 0) { + close(open(SND_FILE_ALOADSEQ, O_RDWR)); + if ((fd = open(filename, fmode)) < 0) { + SYSERR("open %s failed", filename); + return -errno; + } + } + if (ioctl(fd, SND_SEQ_IOCTL_PVERSION, &ver) < 0) { + SYSERR("SND_SEQ_IOCTL_PVERSION failed"); + close(fd); + return -errno; + } + if (SND_PROTOCOL_INCOMPATIBLE(ver, SND_SEQ_VERSION_MAX)) { + close(fd); + return -SND_ERROR_INCOMPATIBLE_VERSION; + } + hw = calloc(1, sizeof(snd_seq_hw_t)); + if (hw == NULL) { + close(fd); + return -ENOMEM; + } + + seq = calloc(1, sizeof(snd_seq_t)); + if (seq == NULL) { + free(hw); + close(fd); + return -ENOMEM; + } + hw->fd = fd; + if (streams & SND_SEQ_OPEN_OUTPUT) { + seq->obuf = (char *) malloc(seq->obufsize = SND_SEQ_OBUF_SIZE); + if (!seq->obuf) { + free(hw); + free(seq); + close(fd); + return -ENOMEM; + } + } + if (streams & SND_SEQ_OPEN_INPUT) { + seq->ibuf = (snd_seq_event_t *) calloc(sizeof(snd_seq_event_t), seq->ibufsize = SND_SEQ_IBUF_SIZE); + if (!seq->ibuf) { + free(seq->ibuf); + free(hw); + free(seq); + close(fd); + return -ENOMEM; + } + } + if (name) + seq->name = strdup(name); + seq->type = SND_SEQ_TYPE_HW; + seq->streams = streams; + seq->mode = mode; + seq->tmpbuf = NULL; + seq->tmpbufsize = 0; + seq->poll_fd = fd; + seq->ops = &snd_seq_hw_ops; + seq->private = hw; + client = snd_seq_hw_client_id(seq); + if (client < 0) { + snd_seq_close(seq); + return client; + } else + seq->client = client; + *handle = seq; + return 0; +} + +int _snd_seq_hw_open(snd_seq_t **handlep, char *name, snd_config_t *conf, + int streams, int mode) +{ + snd_config_iterator_t i; + snd_config_foreach(i, conf) { + snd_config_t *n = snd_config_entry(i); + if (strcmp(n->id, "comment") == 0) + continue; + if (strcmp(n->id, "type") == 0) + continue; + if (strcmp(n->id, "streams") == 0) + continue; + return -EINVAL; + } + return snd_seq_hw_open(handlep, name, streams, mode); +} + diff --git a/src/seq/seq_local.h b/src/seq/seq_local.h new file mode 100644 index 00000000..24b815c7 --- /dev/null +++ b/src/seq/seq_local.h @@ -0,0 +1,105 @@ +/* + * Sequencer Interface - definition of sequencer event handler + * Copyright (c) 2000 by Jaroslav Kysela <perex@suse.cz> + * Abramo Bagnara <abramo@alsa-project.org> + * + * + * 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. + * + */ + +#ifndef __SEQ_LOCAL_H +#define __SEQ_LOCAL_H + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <errno.h> +#include "asoundlib.h" + +#define SND_SEQ_OBUF_SIZE (16*1024) /* default size */ +#define SND_SEQ_IBUF_SIZE 500 /* in event_size aligned */ +#define DEFAULT_TMPBUF_SIZE 20 + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) +#define ERR(...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, 0, __VA_ARGS__) +#define SYSERR(...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, errno, __VA_ARGS__) +#else +#define ERR(args...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, 0, ##args) +#define SYSERR(args...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, errno, ##args) +#endif + +typedef struct { + int (*close)(snd_seq_t *seq); + int (*nonblock)(snd_seq_t *seq, int nonblock); + int (*system_info)(snd_seq_t *seq, snd_seq_system_info_t * info); + int (*get_client_info)(snd_seq_t *seq, snd_seq_client_info_t * info); + int (*set_client_info)(snd_seq_t *seq, snd_seq_client_info_t * info); + int (*create_port)(snd_seq_t *seq, snd_seq_port_info_t * port); + int (*delete_port)(snd_seq_t *seq, snd_seq_port_info_t * port); + int (*get_port_info)(snd_seq_t *seq, snd_seq_port_info_t * info); + int (*set_port_info)(snd_seq_t *seq, snd_seq_port_info_t * info); + int (*get_port_subscription)(snd_seq_t *seq, snd_seq_port_subscribe_t * sub); + int (*subscribe_port)(snd_seq_t *seq, snd_seq_port_subscribe_t * sub); + int (*unsubscribe_port)(snd_seq_t *seq, snd_seq_port_subscribe_t * sub); + int (*query_port_subscribers)(snd_seq_t *seq, snd_seq_query_subs_t * subs); + int (*get_queue_status)(snd_seq_t *seq, snd_seq_queue_status_t * status); + int (*get_queue_tempo)(snd_seq_t *seq, snd_seq_queue_tempo_t * tempo); + int (*set_queue_tempo)(snd_seq_t *seq, snd_seq_queue_tempo_t * tempo); + int (*get_queue_owner)(snd_seq_t *seq, snd_seq_queue_owner_t * owner); + int (*set_queue_owner)(snd_seq_t *seq, snd_seq_queue_owner_t * owner); + int (*get_queue_timer)(snd_seq_t *seq, snd_seq_queue_timer_t * timer); + int (*set_queue_timer)(snd_seq_t *seq, snd_seq_queue_timer_t * timer); + int (*get_queue_client)(snd_seq_t *seq, snd_seq_queue_client_t * client); + int (*set_queue_client)(snd_seq_t *seq, snd_seq_queue_client_t * client); + int (*create_queue)(snd_seq_t *seq, snd_seq_queue_info_t *info); + int (*delete_queue)(snd_seq_t *seq, snd_seq_queue_info_t *info); + int (*get_queue_info)(snd_seq_t *seq, snd_seq_queue_info_t *info); + int (*set_queue_info)(snd_seq_t *seq, snd_seq_queue_info_t *info); + int (*get_named_queue)(snd_seq_t *seq, snd_seq_queue_info_t *info); + ssize_t (*write)(snd_seq_t *seq, void *buf, size_t len); + ssize_t (*read)(snd_seq_t *seq, void *buf, size_t len); + int (*remove_events)(snd_seq_t *seq, snd_seq_remove_events_t *rmp); + int (*get_client_pool)(snd_seq_t *seq, snd_seq_client_pool_t *info); + int (*set_client_pool)(snd_seq_t *seq, snd_seq_client_pool_t *info); + int (*query_next_client)(snd_seq_t *seq, snd_seq_client_info_t *info); + int (*query_next_port)(snd_seq_t *seq, snd_seq_port_info_t *info); +} snd_seq_ops_t; + +struct _snd_seq { + char *name; + int type; + int streams; + int mode; + int poll_fd; + snd_seq_ops_t *ops; + void *private; + int client; /* client number */ + /* buffers */ + char *obuf; /* output buffer */ + size_t obufsize; /* output buffer size */ + size_t obufused; /* output buffer used size */ + snd_seq_event_t *ibuf; /* input buffer */ + size_t ibufptr; /* current pointer of input buffer */ + size_t ibuflen; /* queued length */ + size_t ibufsize; /* input buffer size */ + snd_seq_event_t *tmpbuf; /* temporary event for extracted event */ + size_t tmpbufsize; /* size of errbuf */ +}; + +int snd_seq_hw_open(snd_seq_t **handle, char *name, int streams, int mode); + +#endif diff --git a/src/seq/seq_priv.h b/src/seq/seq_priv.h deleted file mode 100644 index 7dbb1b3e..00000000 --- a/src/seq/seq_priv.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Sequencer Interface - definition of sequencer event handler - * Copyright (c) 1998 by Jaroslav Kysela <perex@suse.cz> - * - * - * 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. - * - */ - -#ifndef __SEQ_PRIV_H -#define __SEQ_PRIV_H - -struct snd_seq { - int client; /* client number */ - int fd; - /* buffers */ - char *obuf; /* output buffer */ - size_t obufsize; /* output buffer size */ - size_t obufused; /* output buffer used size */ - snd_seq_event_t *ibuf; /* input buffer */ - size_t ibufptr; /* current pointer of input buffer */ - size_t ibuflen; /* queued length */ - size_t ibufsize; /* input buffer size */ - snd_seq_event_t *tmpbuf; /* temporary event for extracted event */ - size_t tmpbufsize; /* size of errbuf */ -}; - -#endif diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c index dcef2223..9b9ff89b 100644 --- a/src/seq/seqmid.c +++ b/src/seq/seqmid.c @@ -28,7 +28,7 @@ #include <fcntl.h> #include <sys/ioctl.h> #include "asoundlib.h" -#include "seq_priv.h" +#include "seq_local.h" /* direct passing (without queued) */ void snd_seq_ev_set_direct(snd_seq_event_t *ev) diff --git a/src/timer/timer.c b/src/timer/timer.c index 7201b0cf..535e79d5 100644 --- a/src/timer/timer.c +++ b/src/timer/timer.c @@ -31,7 +31,7 @@ #define SND_FILE_TIMER "/dev/snd/timer" #define SND_TIMER_VERSION_MAX SND_PROTOCOL_VERSION(2, 0, 0) -struct snd_timer { +struct _snd_timer { int fd; }; @@ -136,18 +136,6 @@ int snd_timer_params(snd_timer_t *handle, snd_timer_params_t * params) return 0; } -int snd_timer_setup(snd_timer_t *handle, snd_timer_setup_t * setup) -{ - snd_timer_t *tmr; - - tmr = handle; - if (!tmr || !setup) - return -EINVAL; - if (ioctl(tmr->fd, SND_TIMER_IOCTL_SETUP, setup) < 0) - return -errno; - return 0; -} - int snd_timer_status(snd_timer_t *handle, snd_timer_status_t * status) { snd_timer_t *tmr; diff --git a/test/latency.c b/test/latency.c index 7d9071c6..ed6382d0 100644 --- a/test/latency.c +++ b/test/latency.c @@ -47,7 +47,7 @@ int setparams(snd_pcm_t *phandle, snd_pcm_t *chandle, int *bufsize) params.mode = SND_PCM_MODE_FRAME; #endif params.format.interleave = 1; - params.format.sfmt = SND_PCM_SFMT_S16_LE; + params.format.sfmt = SND_PCM_FORMAT_S16_LE; params.format.channels = 2; params.format.rate = USED_RATE; params.start_mode = SND_PCM_START_EXPLICIT; @@ -216,7 +216,7 @@ int main(void) frames_in = frames_out = 0; if (setparams(phandle, chandle, &latency) < 0) break; - if (snd_pcm_format_set_silence(SND_PCM_SFMT_S16_LE, buffer, latency*2) < 0) { + if (snd_pcm_format_set_silence(SND_PCM_FORMAT_S16_LE, buffer, latency*2) < 0) { fprintf(stderr, "silence error\n"); break; } diff --git a/test/pause.c b/test/pause.c index 0da03421..4d7e26df 100644 --- a/test/pause.c +++ b/test/pause.c @@ -39,7 +39,7 @@ int main(void) fprintf(stderr, "open failed: %s\n", snd_strerror(err)); return 0; } - format.sfmt = SND_PCM_SFMT_MU_LAW; + format.sfmt = SND_PCM_FORMAT_MU_LAW; format.rate = 8000; format.channels = 1; if ((err = snd_pcm_playback_format(handle, &format)) < 0) { @@ -18,7 +18,7 @@ void setformat(void *phandle, void *rhandle) snd_pcm_format_t format; bzero(&format, sizeof(format)); - format.sfmt = SND_PCM_SFMT_S16_LE; + format.sfmt = SND_PCM_FORMAT_S16_LE; format.channels = 2; format.rate = 22050; if ((err = snd_pcm_playback_format(phandle, &format)) < 0) { diff --git a/test/pcmtest.c b/test/pcmtest.c index 6f50a85c..c32d5af7 100644 --- a/test/pcmtest.c +++ b/test/pcmtest.c @@ -52,53 +52,53 @@ void info_channel(snd_pcm_t *handle, int channel, char *id) printf(" overrange"); printf("\n"); printf(" formats :"); - if (stream_info.formats & SND_PCM_FMT_MU_LAW) + if (stream_info.formats & SND_PCM_FMTBIT_MU_LAW) printf(" mu-Law"); - if (stream_info.formats & SND_PCM_FMT_A_LAW) + if (stream_info.formats & SND_PCM_FMTBIT_A_LAW) printf(" a-Law"); - if (stream_info.formats & SND_PCM_FMT_IMA_ADPCM) + if (stream_info.formats & SND_PCM_FMTBIT_IMA_ADPCM) printf(" IMA-ADPCM"); - if (stream_info.formats & SND_PCM_FMT_U8) + if (stream_info.formats & SND_PCM_FMTBIT_U8) printf(" U8"); - if (stream_info.formats & SND_PCM_FMT_S16_LE) + if (stream_info.formats & SND_PCM_FMTBIT_S16_LE) printf(" S16-LE"); - if (stream_info.formats & SND_PCM_FMT_S16_BE) + if (stream_info.formats & SND_PCM_FMTBIT_S16_BE) printf(" S16-BE"); - if (stream_info.formats & SND_PCM_FMT_S8) + if (stream_info.formats & SND_PCM_FMTBIT_S8) printf(" S8"); - if (stream_info.formats & SND_PCM_FMT_U16_LE) + if (stream_info.formats & SND_PCM_FMTBIT_U16_LE) printf(" U16-LE"); - if (stream_info.formats & SND_PCM_FMT_U16_BE) + if (stream_info.formats & SND_PCM_FMTBIT_U16_BE) printf(" U16-BE"); - if (stream_info.formats & SND_PCM_FMT_MPEG) + if (stream_info.formats & SND_PCM_FMTBIT_MPEG) printf(" MPEG"); - if (stream_info.formats & SND_PCM_FMT_GSM) + if (stream_info.formats & SND_PCM_FMTBIT_GSM) printf(" GSM"); - if (stream_info.formats & SND_PCM_FMT_S24_LE) + if (stream_info.formats & SND_PCM_FMTBIT_S24_LE) printf(" S24-LE"); - if (stream_info.formats & SND_PCM_FMT_S24_BE) + if (stream_info.formats & SND_PCM_FMTBIT_S24_BE) printf(" S24-BE"); - if (stream_info.formats & SND_PCM_FMT_U24_LE) + if (stream_info.formats & SND_PCM_FMTBIT_U24_LE) printf(" U24-LE"); - if (stream_info.formats & SND_PCM_FMT_U24_BE) + if (stream_info.formats & SND_PCM_FMTBIT_U24_BE) printf(" U24-BE"); - if (stream_info.formats & SND_PCM_FMT_S32_LE) + if (stream_info.formats & SND_PCM_FMTBIT_S32_LE) printf(" S32-LE"); - if (stream_info.formats & SND_PCM_FMT_S32_BE) + if (stream_info.formats & SND_PCM_FMTBIT_S32_BE) printf(" S32-BE"); - if (stream_info.formats & SND_PCM_FMT_U32_LE) + if (stream_info.formats & SND_PCM_FMTBIT_U32_LE) printf(" U32-LE"); - if (stream_info.formats & SND_PCM_FMT_U32_BE) + if (stream_info.formats & SND_PCM_FMTBIT_U32_BE) printf(" U32-BE"); - if (stream_info.formats & SND_PCM_FMT_FLOAT) + if (stream_info.formats & SND_PCM_FMTBIT_FLOAT) printf(" Float"); - if (stream_info.formats & SND_PCM_FMT_FLOAT64) + if (stream_info.formats & SND_PCM_FMTBIT_FLOAT64) printf(" Float64"); - if (stream_info.formats & SND_PCM_FMT_IEC958_SUBFRAME_LE) + if (stream_info.formats & SND_PCM_FMTBIT_IEC958_SUBFRAME_LE) printf(" IEC958-LE"); - if (stream_info.formats & SND_PCM_FMT_IEC958_SUBFRAME_BE) + if (stream_info.formats & SND_PCM_FMTBIT_IEC958_SUBFRAME_BE) printf(" IEC958-BE"); - if (stream_info.formats & SND_PCM_FMT_SPECIAL) + if (stream_info.formats & SND_PCM_FMTBIT_SPECIAL) printf(" Special"); printf("\n"); printf(" rates :"); diff --git a/test/seq-sender.c b/test/seq-sender.c index b7c1f6c8..546f0a91 100644 --- a/test/seq-sender.c +++ b/test/seq-sender.c @@ -14,7 +14,7 @@ void set_format(snd_pcm_t *phandle) snd_pcm_format_t format; bzero(&format, sizeof(format)); - format.sfmt = SND_PCM_SFMT_S16_LE; + format.sfmt = SND_PCM_FORMAT_S16_LE; format.channels = 2; format.rate = 44100; if ((err = snd_pcm_playback_format(phandle, &format)) < 0) { |