#if !defined(SND_PROTOCOL_VERSION) || !defined(SND_PROTOCOL_INCOMPATIBLE)
#error not found
#else
-#if !defined(SND_MIXER_IOCTL_ELEMENTS)
+#if !defined(SND_PCM_IOCTL_CHANNEL_UPDATE)
#error wrong version
#endif
exit(0);
],
AC_MSG_RESULT(present),
[AC_MSG_RESULT(not found or wrong version);
- AC_MSG_ERROR([Install alsa-driver v0.3.0pre5+ package first...])]
+ AC_MSG_ERROR([Install alsa-driver v0.6.0 package first...])]
)
CFLAGS="$OLD_CFLAGS"
])
aclocal $ACLOCAL_FLAGS
automake --foreign
autoconf
-export CFLAGS='-O2 -Wall -pipe -g'
+export CFLAGS='-O2 -Wall -W -pipe -g'
echo "CFLAGS=$CFLAGS"
echo "./configure $@"
./configure $@
+#define UNUSED __attribute__ ((unused))
+
/*
*
*/
extern "C" {
#endif
-int snd_instr_simple_convert_to_stream(snd_instr_simple_t *simple, const char *name, snd_seq_instr_put_t **put, long *size);
-int snd_instr_simple_convert_from_stream(snd_seq_instr_get_t *data, long size, snd_instr_simple_t **simple);
+int snd_instr_simple_convert_to_stream(snd_instr_simple_t *simple, const char *name, snd_seq_instr_put_t **put, size_t *size);
+int snd_instr_simple_convert_from_stream(snd_seq_instr_get_t *data, size_t size, snd_instr_simple_t **simple);
int snd_instr_simple_free(snd_instr_simple_t *simple);
#ifdef __cplusplus
int snd_instr_iwffff_open_rom_file(snd_iwffff_handle_t **handle, const char *name, int bank, int file);
int snd_instr_iwffff_close(snd_iwffff_handle_t *handle);
int snd_instr_iwffff_load(snd_iwffff_handle_t *handle, int bank, int prg, snd_instr_iwffff_t **iwffff);
-int snd_instr_iwffff_convert_to_stream(snd_instr_iwffff_t *iwffff, const char *name, snd_seq_instr_put_t **data, long *size);
-int snd_instr_iwffff_convert_from_stream(snd_seq_instr_get_t *data, long size, snd_instr_iwffff_t **iwffff);
+int snd_instr_iwffff_convert_to_stream(snd_instr_iwffff_t *iwffff, const char *name, snd_seq_instr_put_t **data, size_t *size);
+int snd_instr_iwffff_convert_from_stream(snd_seq_instr_get_t *data, size_t size, snd_instr_iwffff_t **iwffff);
int snd_instr_iwffff_free(snd_instr_iwffff_t *iwffff);
#ifdef __cplusplus
#define SND_PCM_OPEN_PLAYBACK 0x0001
#define SND_PCM_OPEN_CAPTURE 0x0002
#define SND_PCM_OPEN_DUPLEX 0x0003
-#define SND_PCM_OPEN_NONBLOCK 0x1000
+#define SND_PCM_NONBLOCK_PLAYBACK 0x1000
+#define SND_PCM_NONBLOCK_CAPTURE 0x2000
+#define SND_PCM_NONBLOCK 0x3000
#ifdef __cplusplus
extern "C" {
#endif
+typedef unsigned int bitset_t;
+
+static inline size_t bitset_size(int nbits)
+{
+ return (nbits + sizeof(bitset_t) * 8 - 1) / (sizeof(bitset_t) * 8);
+}
+
+static inline bitset_t *bitset_alloc(int nbits)
+{
+ return calloc(bitset_size(nbits), sizeof(bitset_t));
+}
+
+static inline void bitset_set(bitset_t *bitmap, unsigned int pos)
+{
+ int bits = sizeof(*bitmap) * 8;
+ bitmap[pos / bits] |= 1 << (pos % bits);
+}
+
+static inline void bitset_reset(bitset_t *bitmap, unsigned int pos)
+{
+ int bits = sizeof(*bitmap) * 8;
+ bitmap[pos / bits] &= ~(1 << (pos % bits));
+}
+
+static inline int bitset_get(bitset_t *bitmap, unsigned int pos)
+{
+ int bits = sizeof(*bitmap) * 8;
+ return !!(bitmap[pos / bits] & (1 << (pos % bits)));
+}
+
+static inline void bitset_copy(bitset_t *dst, bitset_t *src, unsigned int nbits)
+{
+ memcpy(dst, src, bitset_size(nbits) * sizeof(bitset_t));
+}
+
+static inline void bitset_and(bitset_t *dst, bitset_t *bs, unsigned int nbits)
+{
+ bitset_t *end = dst + bitset_size(nbits);
+ while (dst < end)
+ *dst++ &= *bs++;
+}
+
+static inline void bitset_or(bitset_t *dst, bitset_t *bs, unsigned int nbits)
+{
+ bitset_t *end = dst + bitset_size(nbits);
+ while (dst < end)
+ *dst++ |= *bs++;
+}
+
+static inline void bitset_zero(bitset_t *dst, unsigned int nbits)
+{
+ bitset_t *end = dst + bitset_size(nbits);
+ while (dst < end)
+ *dst++ = 0;
+}
+
+static inline void bitset_one(bitset_t *dst, unsigned int nbits)
+{
+ bitset_t *end = dst + bitset_size(nbits);
+ while (dst < end)
+ *dst++ = -1;
+}
+
typedef struct snd_pcm snd_pcm_t;
typedef struct snd_pcm_loopback snd_pcm_loopback_t;
+typedef enum { SND_PCM_TYPE_HW, SND_PCM_TYPE_PLUG } snd_pcm_type_t;
+
int snd_pcm_open(snd_pcm_t **handle, int card, int device, int mode);
int snd_pcm_open_subdevice(snd_pcm_t **handle, int card, int device, int subdevice, int mode);
+
+snd_pcm_type_t snd_pcm_type(snd_pcm_t *handle);
int snd_pcm_close(snd_pcm_t *handle);
+int snd_pcm_channel_close(snd_pcm_t *handle, int channel);
int snd_pcm_file_descriptor(snd_pcm_t *handle, int channel);
-int snd_pcm_nonblock_mode(snd_pcm_t *handle, int nonblock);
-int snd_pcm_info(snd_pcm_t *handle, snd_pcm_info_t * info);
-int snd_pcm_channel_info(snd_pcm_t *handle, snd_pcm_channel_info_t * info);
-int snd_pcm_channel_params(snd_pcm_t *handle, snd_pcm_channel_params_t * params);
-int snd_pcm_channel_setup(snd_pcm_t *handle, snd_pcm_channel_setup_t * setup);
-int snd_pcm_voice_setup(snd_pcm_t *handle, int channel, snd_pcm_voice_setup_t * setup);
-int snd_pcm_channel_status(snd_pcm_t *handle, snd_pcm_channel_status_t * status);
+int snd_pcm_channel_nonblock(snd_pcm_t *handle, int channel, int nonblock);
+int snd_pcm_info(snd_pcm_t *handle, snd_pcm_info_t *info);
+int snd_pcm_channel_info(snd_pcm_t *handle, snd_pcm_channel_info_t *info);
+int snd_pcm_channel_params(snd_pcm_t *handle, snd_pcm_channel_params_t *params);
+int snd_pcm_channel_setup(snd_pcm_t *handle, snd_pcm_channel_setup_t *setup);
+int snd_pcm_voice_setup(snd_pcm_t *handle, int channel, snd_pcm_voice_setup_t *setup);
+int snd_pcm_all_voices_setup(snd_pcm_t *handle, int channel, snd_pcm_voice_setup_t *setup);
+int snd_pcm_channel_status(snd_pcm_t *handle, snd_pcm_channel_status_t *status);
int snd_pcm_playback_prepare(snd_pcm_t *handle);
int snd_pcm_capture_prepare(snd_pcm_t *handle);
int snd_pcm_channel_prepare(snd_pcm_t *handle, int channel);
int snd_pcm_channel_go(snd_pcm_t *handle, int channel);
int snd_pcm_sync_go(snd_pcm_t *handle, snd_pcm_sync_t *sync);
int snd_pcm_playback_drain(snd_pcm_t *handle);
+int snd_pcm_channel_drain(snd_pcm_t *handle, int channel);
int snd_pcm_playback_flush(snd_pcm_t *handle);
int snd_pcm_capture_flush(snd_pcm_t *handle);
int snd_pcm_channel_flush(snd_pcm_t *handle, int channel);
int snd_pcm_playback_pause(snd_pcm_t *handle, int enable);
+int snd_pcm_channel_pause(snd_pcm_t *handle, int channel, int enable);
ssize_t snd_pcm_transfer_size(snd_pcm_t *handle, int channel);
ssize_t snd_pcm_write(snd_pcm_t *handle, const void *buffer, size_t size);
ssize_t snd_pcm_read(snd_pcm_t *handle, void *buffer, size_t size);
-ssize_t snd_pcm_writev(snd_pcm_t *pcm, const struct iovec *vector, int count);
-ssize_t snd_pcm_readv(snd_pcm_t *pcm, const struct iovec *vector, int count);
+ssize_t snd_pcm_writev(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count);
+ssize_t snd_pcm_readv(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count);
int snd_pcm_mmap(snd_pcm_t *handle, int channel, snd_pcm_mmap_control_t **control, void **buffer);
int snd_pcm_munmap(snd_pcm_t *handle, int channel);
+int snd_pcm_mmap_control(snd_pcm_t *handle, int channel, snd_pcm_mmap_control_t **control);
+int snd_pcm_mmap_data(snd_pcm_t *handle, int channel, void **buffer);
+int snd_pcm_munmap_control(snd_pcm_t *handle, int channel);
+int snd_pcm_munmap_data(snd_pcm_t *handle, int channel);
+int snd_pcm_voices_mask(snd_pcm_t *pcm, int channel, bitset_t *client_vmask);
+int snd_pcm_mmap_frags_used(snd_pcm_t *pcm, int channel, ssize_t *frags);
+int snd_pcm_mmap_frags_free(snd_pcm_t *pcm, int channel, ssize_t *frags);
+int snd_pcm_mmap_bytes_used(snd_pcm_t *pcm, int channel, ssize_t *bytes);
+int snd_pcm_mmap_bytes_free(snd_pcm_t *pcm, int channel, ssize_t *bytes);
+int snd_pcm_mmap_ready(snd_pcm_t *pcm, int channel);
+ssize_t snd_pcm_mmap_write(snd_pcm_t *handle, const void *buffer, size_t size);
+ssize_t snd_pcm_mmap_read(snd_pcm_t *handle, void *buffer, size_t size);
+ssize_t snd_pcm_mmap_writev(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count);
+ssize_t snd_pcm_mmap_readv(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count);
+ssize_t snd_pcm_bytes_per_second(snd_pcm_t *pcm, int channel);
/* misc */
int snd_pcm_format_physical_width(int format); /* in bits */
int snd_pcm_build_linear_format(int width, int unsignd, int big_endian);
ssize_t snd_pcm_format_size(int format, size_t samples);
-unsigned char snd_pcm_format_silence(int format);
+ssize_t snd_pcm_format_bytes_per_second(snd_pcm_format_t *format);
+u_int8_t snd_pcm_format_silence(int format);
+u_int16_t snd_pcm_format_silence_16(int format);
+u_int32_t snd_pcm_format_silence_32(int format);
+u_int64_t snd_pcm_format_silence_64(int format);
+ssize_t snd_pcm_format_set_silence(int format, void *buf, size_t count);
const char *snd_pcm_get_format_name(int format);
#ifdef __cplusplus
PREPARE = 1,
DRAIN = 2,
FLUSH = 3,
+ PAUSE = 4,
} snd_pcm_plugin_action_t;
typedef struct snd_stru_pcm_plugin_voice {
void *addr; /* address to voice samples */
unsigned int first; /* offset to first sample in bits */
unsigned int step; /* samples distance in bits */
+ unsigned int enabled:1; /* voice need to be processed */
+ unsigned int wanted:1; /* voice is wanted */
} snd_pcm_plugin_voice_t;
struct snd_stru_pcm_plugin {
char *name; /* plug-in name */
+ int channel;
snd_pcm_format_t src_format; /* source format */
snd_pcm_format_t dst_format; /* destination format */
int src_width; /* sample width in bits */
int dst_width; /* sample width in bits */
ssize_t (*src_samples)(snd_pcm_plugin_t *plugin, size_t dst_samples);
ssize_t (*dst_samples)(snd_pcm_plugin_t *plugin, size_t src_samples);
- int (*src_voices)(snd_pcm_plugin_t *plugin,
- snd_pcm_plugin_voice_t **voices,
- size_t samples);
- int (*dst_voices)(snd_pcm_plugin_t *plugin,
- snd_pcm_plugin_voice_t **voices,
- size_t samples);
+ int (*client_voices)(snd_pcm_plugin_t *plugin,
+ size_t samples,
+ snd_pcm_plugin_voice_t **voices);
+ int (*src_voices_mask)(snd_pcm_plugin_t *plugin,
+ bitset_t *dst_vmask,
+ bitset_t **src_vmask);
+ int (*dst_voices_mask)(snd_pcm_plugin_t *plugin,
+ bitset_t *src_vmask,
+ bitset_t **dst_vmask);
ssize_t (*transfer)(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
size_t samples);
int (*action)(snd_pcm_plugin_t *plugin,
snd_pcm_plugin_action_t action,
snd_pcm_plugin_handle_t *handle;
void *private_data;
void (*private_free)(snd_pcm_plugin_t *plugin, void *private_data);
- snd_pcm_plugin_voice_t *voices;
- void *extra_data;
+ snd_pcm_plugin_voice_t *src_voices;
+ snd_pcm_plugin_voice_t *dst_voices;
+ bitset_t *src_vmask;
+ bitset_t *dst_vmask;
+ char extra_data[0];
};
-snd_pcm_plugin_t *snd_pcm_plugin_build(snd_pcm_plugin_handle_t *handle,
- const char *name,
- snd_pcm_format_t *src_format,
- snd_pcm_format_t *dst_format,
- int extra);
+int snd_pcm_plug_connect(snd_pcm_t **handle, snd_pcm_t *slave, int mode, int close_slave);
+int snd_pcm_plug_open_subdevice(snd_pcm_t **handle, int card, int device, int subdevice, int mode);
+int snd_pcm_plug_open(snd_pcm_t **handle, int card, int device, int mode);
+
int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin);
-int snd_pcm_plugin_clear(snd_pcm_t *handle, int channel);
-int snd_pcm_plugin_insert(snd_pcm_t *handle, int channel, snd_pcm_plugin_t *plugin);
-int snd_pcm_plugin_append(snd_pcm_t *handle, int channel, snd_pcm_plugin_t *plugin);
-int snd_pcm_plugin_remove_to(snd_pcm_t *handle, int channel, snd_pcm_plugin_t *plugin);
-int snd_pcm_plugin_remove_first(snd_pcm_t *handle, int channel);
-snd_pcm_plugin_t *snd_pcm_plugin_first(snd_pcm_t *handle, int channel);
-snd_pcm_plugin_t *snd_pcm_plugin_last(snd_pcm_t *handle, int channel);
-ssize_t snd_pcm_plugin_client_samples(snd_pcm_t *handle, int channel, size_t drv_samples);
-ssize_t snd_pcm_plugin_hardware_samples(snd_pcm_t *handle, int channel, size_t clt_samples);
-ssize_t snd_pcm_plugin_client_size(snd_pcm_t *handle, int channel, size_t drv_size);
-ssize_t snd_pcm_plugin_hardware_size(snd_pcm_t *handle, int channel, size_t clt_size);
-int snd_pcm_plugin_info(snd_pcm_t *handle, snd_pcm_channel_info_t *info);
-int snd_pcm_plugin_params(snd_pcm_t *handle, snd_pcm_channel_params_t *params);
-int snd_pcm_plugin_setup(snd_pcm_t *handle, snd_pcm_channel_setup_t *setup);
-int snd_pcm_plugin_voice_setup(snd_pcm_t *handle, int channel, snd_pcm_voice_setup_t * setup);
-int snd_pcm_plugin_status(snd_pcm_t *handle, snd_pcm_channel_status_t *status);
-int snd_pcm_plugin_prepare(snd_pcm_t *handle, int channel);
-int snd_pcm_plugin_playback_drain(snd_pcm_t *handle);
-int snd_pcm_plugin_flush(snd_pcm_t *handle, int channel);
-ssize_t snd_pcm_plugin_transfer_size(snd_pcm_t *handle, int channel);
-int snd_pcm_plugin_pointer(snd_pcm_t *pcm, int channel, void **ptr, size_t *size);
-ssize_t snd_pcm_plugin_write(snd_pcm_t *handle, const void *buffer, size_t size);
-ssize_t snd_pcm_plugin_read(snd_pcm_t *handle, void *bufer, size_t size);
-int snd_pcm_plugin_pointerv(snd_pcm_t *pcm, int channel, struct iovec **vector, int *count);
-ssize_t snd_pcm_plugin_writev(snd_pcm_t *pcm, const struct iovec *vector, int count);
-ssize_t snd_pcm_plugin_readv(snd_pcm_t *pcm, const struct iovec *vector, int count);
-ssize_t snd_pcm_plugin_write_continue(snd_pcm_t *pcm);
-int snd_pcm_plugin_mmap(snd_pcm_t *handle, int channel, snd_pcm_mmap_control_t **control, void **buffer);
-int snd_pcm_plugin_munmap(snd_pcm_t *handle, int channel);
-int snd_pcm_plugin_go(snd_pcm_t *handle, int channel);
-int snd_pcm_plugin_sync_go(snd_pcm_t *handle, snd_pcm_sync_t *sync);
-int snd_pcm_plugin_pause(snd_pcm_t *pcm, int enable);
-int snd_pcm_plugin_voice_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t *setup);
+int snd_pcm_plug_clear(snd_pcm_t *handle, int channel);
+int snd_pcm_plugin_insert(snd_pcm_plugin_t *plugin);
+int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin);
+#if 0
+int snd_pcm_plugin_remove_to(snd_pcm_plugin_t *plugin);
+int snd_pcm_plug_remove_first(snd_pcm_t *handle, int channel);
+#endif
+snd_pcm_plugin_t *snd_pcm_plug_first(snd_pcm_t *handle, int channel);
+snd_pcm_plugin_t *snd_pcm_plug_last(snd_pcm_t *handle, int channel);
+int snd_pcm_plug_direct(snd_pcm_t *pcm, int channel);
+ssize_t snd_pcm_plug_client_samples(snd_pcm_t *handle, int channel, size_t drv_samples);
+ssize_t snd_pcm_plug_slave_samples(snd_pcm_t *handle, int channel, size_t clt_samples);
+ssize_t snd_pcm_plug_client_size(snd_pcm_t *handle, int channel, size_t drv_size);
+ssize_t snd_pcm_plug_slave_size(snd_pcm_t *handle, int channel, size_t clt_size);
/*
* Plug-In helpers
ssize_t snd_pcm_plugin_dst_samples_to_size(snd_pcm_plugin_t *plugin, size_t samples);
ssize_t snd_pcm_plugin_src_size_to_samples(snd_pcm_plugin_t *plugin, size_t size);
ssize_t snd_pcm_plugin_dst_size_to_samples(snd_pcm_plugin_t *plugin, size_t size);
-int snd_pcm_plugin_src_voices(snd_pcm_plugin_t *plugin,
- snd_pcm_plugin_voice_t **voices,
- size_t samples);
-int snd_pcm_plugin_dst_voices(snd_pcm_plugin_t *plugin,
- snd_pcm_plugin_voice_t **voices,
- size_t samples);
/*
* Plug-In constructors
*/
+int snd_pcm_plugin_build(snd_pcm_plugin_handle_t *handle,
+ int channel,
+ const char *name,
+ snd_pcm_format_t *src_format,
+ snd_pcm_format_t *dst_format,
+ int extra,
+ snd_pcm_plugin_t **ret);
/* basic I/O */
-int snd_pcm_plugin_build_stream(snd_pcm_plugin_handle_t *handle, int channel,
+int snd_pcm_plugin_build_stream(snd_pcm_plugin_handle_t *handle,
+ int channel,
+ snd_pcm_t *slave,
snd_pcm_format_t *format,
snd_pcm_plugin_t **r_plugin);
-int snd_pcm_plugin_build_block(snd_pcm_plugin_handle_t *handle, int channel,
+int snd_pcm_plugin_build_block(snd_pcm_plugin_handle_t *handle,
+ int channel,
+ snd_pcm_t *slave,
snd_pcm_format_t *format,
snd_pcm_plugin_t **r_plugin);
-int snd_pcm_plugin_build_mmap(snd_pcm_plugin_handle_t *handle, int channel,
+int snd_pcm_plugin_build_mmap(snd_pcm_plugin_handle_t *handle,
+ int channel,
+ snd_pcm_t *slave,
snd_pcm_format_t *format,
snd_pcm_plugin_t **r_plugin);
/* conversion plugins */
int snd_pcm_plugin_build_interleave(snd_pcm_plugin_handle_t *handle,
+ int channel,
snd_pcm_format_t *src_format,
snd_pcm_format_t *dst_format,
snd_pcm_plugin_t **r_plugin);
int snd_pcm_plugin_build_linear(snd_pcm_plugin_handle_t *handle,
+ int channel,
snd_pcm_format_t *src_format,
snd_pcm_format_t *dst_format,
snd_pcm_plugin_t **r_plugin);
int snd_pcm_plugin_build_mulaw(snd_pcm_plugin_handle_t *handle,
+ int channel,
snd_pcm_format_t *src_format,
snd_pcm_format_t *dst_format,
snd_pcm_plugin_t **r_plugin);
int snd_pcm_plugin_build_alaw(snd_pcm_plugin_handle_t *handle,
+ int channel,
snd_pcm_format_t *src_format,
snd_pcm_format_t *dst_format,
snd_pcm_plugin_t **r_plugin);
int snd_pcm_plugin_build_adpcm(snd_pcm_plugin_handle_t *handle,
+ int channel,
snd_pcm_format_t *src_format,
snd_pcm_format_t *dst_format,
snd_pcm_plugin_t **r_plugin);
int snd_pcm_plugin_build_rate(snd_pcm_plugin_handle_t *handle,
+ int channel,
snd_pcm_format_t *src_format,
snd_pcm_format_t *dst_format,
snd_pcm_plugin_t **r_plugin);
int snd_pcm_plugin_build_route(snd_pcm_plugin_handle_t *handle,
+ int channel,
snd_pcm_format_t *src_format,
snd_pcm_format_t *dst_format,
float *ttable,
snd_pcm_plugin_t **r_plugin);
-
-#ifdef __cplusplus
-}
-#endif
-
-/*
- * Loopback interface
- */
-
-#define SND_PCM_LB_OPEN_PLAYBACK 0
-#define SND_PCM_LB_OPEN_CAPTURE 1
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct snd_pcm_loopback_callbacks {
- void *private_data; /* should be used with an application */
- size_t max_buffer_size; /* zero = default (64kB) */
- void (*data) (void *private_data, char *buffer, size_t count);
- void (*position_change) (void *private_data, unsigned int pos);
- void (*format_change) (void *private_data, snd_pcm_format_t *format);
- void (*silence) (void *private_data, size_t count);
- void *reserved[31]; /* reserved for the future use - must be NULL!!! */
-} snd_pcm_loopback_callbacks_t;
-
-int snd_pcm_loopback_open(snd_pcm_loopback_t **handle, int card, int device, int subdev, int mode);
-int snd_pcm_loopback_close(snd_pcm_loopback_t *handle);
-int snd_pcm_loopback_file_descriptor(snd_pcm_loopback_t *handle);
-int snd_pcm_loopback_block_mode(snd_pcm_loopback_t *handle, int enable);
-int snd_pcm_loopback_stream_mode(snd_pcm_loopback_t *handle, int mode);
-int snd_pcm_loopback_format(snd_pcm_loopback_t *handle, snd_pcm_format_t * format);
-int snd_pcm_loopback_status(snd_pcm_loopback_t *handle, snd_pcm_loopback_status_t * status);
-ssize_t snd_pcm_loopback_read(snd_pcm_loopback_t *handle, snd_pcm_loopback_callbacks_t * callbacks);
+int snd_pcm_plugin_build_copy(snd_pcm_plugin_handle_t *handle,
+ int channel,
+ snd_pcm_format_t *format,
+ snd_pcm_plugin_t **r_plugin);
#ifdef __cplusplus
}
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);
-int snd_seq_resize_output_buffer(snd_seq_t *handle, int size);
-int snd_seq_resize_input_buffer(snd_seq_t *handle, int size);
+int snd_seq_resize_output_buffer(snd_seq_t *handle, size_t size);
+int snd_seq_resize_input_buffer(snd_seq_t *handle, size_t size);
int snd_seq_system_info(snd_seq_t *handle, snd_seq_system_info_t *info);
int snd_seq_get_client_info(snd_seq_t *handle, snd_seq_client_info_t *info);
int snd_seq_get_any_client_info(snd_seq_t *handle, int client, snd_seq_client_info_t *info);
/* event routines */
snd_seq_event_t *snd_seq_create_event(void);
int snd_seq_free_event(snd_seq_event_t *ev);
-int snd_seq_event_length(snd_seq_event_t *ev);
+ssize_t snd_seq_event_length(snd_seq_event_t *ev);
int snd_seq_event_output(snd_seq_t *handle, snd_seq_event_t *ev);
int snd_seq_event_output(snd_seq_t *handle, snd_seq_event_t *ev);
int snd_seq_event_output_buffer(snd_seq_t *handle, snd_seq_event_t *ev);
COMPATNUM=@LIBTOOL_VERSION_INFO@
lib_LTLIBRARIES = libasound.la
-libasound_la_SOURCES = error.c
+libasound_la_SOURCES = error.c
libasound_la_LIBADD = control/libcontrol.la mixer/libmixer.la pcm/libpcm.la \
rawmidi/librawmidi.la timer/libtimer.la hwdep/libhwdep.la \
seq/libseq.la instr/libinstr.la
if (errnum < SND_ERROR_BEGIN)
return (const char *) strerror(errnum);
errnum -= SND_ERROR_BEGIN;
- if (errnum >= sizeof(snd_error_codes) / sizeof(const char *))
+ if ((unsigned int) errnum >= sizeof(snd_error_codes) / sizeof(const char *))
return "Unknown error";
return snd_error_codes[errnum];
}
{
ssize_t result;
- if (!hwdep || (!buffer && size > 0) || size < 0)
+ if (!hwdep || (!buffer && size > 0))
return -EINVAL;
result = write(hwdep->fd, buffer, size);
if (result < 0)
{
ssize_t result;
- if (!hwdep || (!buffer && size > 0) || size < 0)
+ if (!hwdep || (!buffer && size > 0))
return -EINVAL;
result = read(hwdep->fd, buffer, size);
if (result < 0)
struct snd_iwffff_handle {
int rom;
unsigned char *fff_data;
- unsigned int fff_size;
+ size_t fff_size;
char *fff_filename;
char *dat_filename;
unsigned int start_addr;
close(fd);
return -ENOMEM;
}
- if (read(fd, iwf->fff_data, iwf->fff_size) != iwf->fff_size) {
+ if (read(fd, iwf->fff_data, iwf->fff_size) != (ssize_t) iwf->fff_size) {
free(iwf->fff_data);
free(iwf);
close(fd);
close(fd);
return -ENOMEM;
}
- if (read(fd, iwf->fff_data, ffff.length) != ffff.length) {
+ if (read(fd, iwf->fff_data, ffff.length) != (ssize_t) ffff.length) {
free(iwf->fff_data);
free(iwf);
close(fd);
return 0;
}
-static char *look_for_id(snd_iwffff_handle_t *iwf, unsigned char *start,
+static char *look_for_id(snd_iwffff_handle_t *iwf UNUSED, unsigned char *start,
unsigned char *end, ID id)
{
if (!start)
close(fd);
return -ENOMEM;
}
- if (read(fd, result, size) != size) {
+ if (read(fd, result, size) != (ssize_t) size) {
free(*result);
*result = NULL;
close(fd);
return 0;
}
-int snd_instr_iwffff_convert_from_stream(snd_seq_instr_get_t *data,
- long size,
- snd_instr_iwffff_t **iwffff)
+int snd_instr_iwffff_convert_from_stream(snd_seq_instr_get_t *data UNUSED,
+ size_t size UNUSED,
+ snd_instr_iwffff_t **iwffff UNUSED)
{
/* TODO */
return -ENXIO;
int snd_instr_simple_convert_to_stream(snd_instr_simple_t *simple,
const char *name,
snd_seq_instr_put_t **__data,
- long *__size)
+ size_t *__size)
{
snd_seq_instr_put_t *put;
snd_seq_instr_data_t *data;
return 0;
}
-int snd_instr_simple_convert_from_stream(snd_seq_instr_get_t *__data,
- long size,
- snd_instr_simple_t **simple)
+int snd_instr_simple_convert_from_stream(snd_seq_instr_get_t *__data UNUSED,
+ size_t size UNUSED,
+ snd_instr_simple_t **simple UNUSED)
{
/* TODO */
return -ENXIO;
if ((err = snd_mixer_element_info(handle, element)) < 0)
return err;
break;
- case SND_MIXER_ETYPE_SWITCH3:
- element->data.switch3.voices_size = element->data.switch3.voices_over;
- element->data.switch3.voices = element->data.switch3.voices_over = 0;
- element->data.switch3.pvoices = (snd_mixer_voice_t *)malloc(element->data.switch3.voices_size * sizeof(snd_mixer_voice_t));
- if (!element->data.switch3.pvoices)
- return -ENOMEM;
- if ((err = snd_mixer_element_info(handle, element)) < 0)
- return err;
- break;
case SND_MIXER_ETYPE_VOLUME1:
element->data.volume1.range_size = element->data.volume1.range_over;
element->data.volume1.range = element->data.volume1.range_over = 0;
case SND_MIXER_ETYPE_PLAYBACK1:
safe_free((void **)&element->data.pcm1.pdevices);
break;
- case SND_MIXER_ETYPE_SWITCH3:
- safe_free((void **)&element->data.switch3.pvoices);
- break;
case SND_MIXER_ETYPE_VOLUME1:
safe_free((void **)&element->data.volume1.prange);
break;
#include <search.h>
#define SND_FILE_MIXER "/dev/snd/mixerC%iD%i"
-#define SND_MIXER_VERSION_MAX SND_PROTOCOL_VERSION(1, 0, 1)
+#define SND_MIXER_VERSION_MAX SND_PROTOCOL_VERSION(2, 0, 0)
struct snd_mixer {
int card;
/* Compare using name and index */
int snd_mixer_compare_gid_name_index(const snd_mixer_gid_t *a,
const snd_mixer_gid_t *b,
- void* ignored)
+ void *ignored UNUSED)
{
int r = strcmp(a->name, b->name);
if (r != 0)
/* Compare using name and index */
int snd_mixer_compare_eid_name_index(const snd_mixer_eid_t *a,
const snd_mixer_eid_t *b,
- void* ignored)
+ void *ignored UNUSED)
{
int r = strcmp(a->name, b->name);
if (r != 0)
EXTRA_LTLIBRARIES = libpcm.la
-libpcm_la_SOURCES = pcm.c pcm_plugin.c pcm_plugin_build.c pcm_misc.c \
- pcm_loopback.c
+libpcm_la_SOURCES = pcm.c pcm_hw.c pcm_plug.c pcm_plugin_build.c pcm_misc.c \
+ pcm_mmap.c
libpcm_la_LIBADD = plugin/libpcmplugin.la
noinst_HEADERS = pcm_local.h
/*
* PCM Interface - main file
* Copyright (c) 1998 by Jaroslav Kysela <perex@suse.cz>
- *
+ * 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
*/
#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
+#include <malloc.h>
#include <errno.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/uio.h>
#include "pcm_local.h"
-#define SND_FILE_PCM_PLAYBACK "/dev/snd/pcmC%iD%ip"
-#define SND_FILE_PCM_CAPTURE "/dev/snd/pcmC%iD%ic"
-#define SND_PCM_VERSION_MAX SND_PROTOCOL_VERSION(2, 0, 0)
-
-int snd_pcm_open(snd_pcm_t **handle, int card, int device, int mode)
+int snd_pcm_abstract_open(snd_pcm_t **handle, int mode,
+ snd_pcm_type_t type, size_t extra)
{
- return snd_pcm_open_subdevice(handle, card, device, -1, mode);
-}
+ snd_pcm_t *pcm;
-static int snd_pcm_open_channel(int card, int device, int channel, int subdevice, int fmode, snd_ctl_t *ctl, int *ver)
-{
- char filename[32];
- char *filefmt;
- int err, fd;
- int attempt = 0;
- snd_pcm_channel_info_t info;
- switch (channel) {
- case SND_PCM_CHANNEL_PLAYBACK:
- filefmt = SND_FILE_PCM_PLAYBACK;
- break;
- case SND_PCM_CHANNEL_CAPTURE:
- filefmt = SND_FILE_PCM_CAPTURE;
- break;
- default:
- return -EINVAL;
- }
- if ((err = snd_ctl_pcm_channel_prefer_subdevice(ctl, device, channel, subdevice)) < 0)
- return err;
- sprintf(filename, filefmt, card, device);
-
- __again:
- if (attempt++ > 3) {
- snd_ctl_close(ctl);
- return -EBUSY;
- }
- if ((fd = open(filename, fmode)) < 0) {
- err = -errno;
- return err;
- }
- if (ioctl(fd, SND_PCM_IOCTL_PVERSION, ver) < 0) {
- err = -errno;
- close(fd);
- return err;
- }
- if (SND_PROTOCOL_INCOMPATIBLE(*ver, SND_PCM_VERSION_MAX)) {
- close(fd);
- return -SND_ERROR_INCOMPATIBLE_VERSION;
+ if (!handle)
+ return -EFAULT;
+ *handle = NULL;
+
+ pcm = (snd_pcm_t *) calloc(1, sizeof(snd_pcm_t) + extra);
+ if (pcm == NULL)
+ return -ENOMEM;
+ if (mode & SND_PCM_OPEN_PLAYBACK) {
+ struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK];
+ chan->open = 1;
+ chan->mode = (mode & SND_PCM_NONBLOCK_PLAYBACK) ? SND_PCM_NONBLOCK : 0;
}
- if (subdevice >= 0) {
- memset(&info, 0, sizeof(info));
- if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_INFO, &info) < 0) {
- err = -errno;
- close(fd);
- return err;
- }
- if (info.subdevice != subdevice) {
- close(fd);
- goto __again;
- }
+ if (mode & SND_PCM_OPEN_CAPTURE) {
+ struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE];
+ chan->open = 1;
+ chan->mode = (mode & SND_PCM_NONBLOCK_CAPTURE) ? SND_PCM_NONBLOCK : 0;
}
- return fd;
+ pcm->type = type;
+ pcm->mode = mode & SND_PCM_OPEN_DUPLEX;
+ *handle = pcm;
+ return 0;
}
-int snd_pcm_open_subdevice(snd_pcm_t **handle, int card, int device, int subdevice, int mode)
+snd_pcm_type_t snd_pcm_type(snd_pcm_t *handle)
{
- int fmode, ver, err;
- snd_pcm_t *pcm;
- snd_ctl_t *ctl;
- int pfd = -1, cfd = -1;
+ return handle->type;
+}
- *handle = NULL;
-
- if (card < 0 || card >= SND_CARDS)
+int snd_pcm_channel_close(snd_pcm_t *pcm, int channel)
+{
+ int ret = 0;
+ int err;
+ struct snd_pcm_chan *chan;
+ if (!pcm)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
return -EINVAL;
- if ((err = snd_ctl_open(&ctl, card)) < 0)
- return err;
- fmode = O_RDWR;
- if (mode & SND_PCM_OPEN_NONBLOCK)
- fmode |= O_NONBLOCK;
- if (mode & SND_PCM_OPEN_PLAYBACK) {
- pfd = snd_pcm_open_channel(card, device, SND_PCM_CHANNEL_PLAYBACK,
- subdevice, fmode, ctl, &ver);
- if (pfd < 0) {
- snd_ctl_close(ctl);
- return pfd;
- }
+ chan = &pcm->chan[channel];
+ if (!chan->open)
+ return -EBADFD;
+ if (chan->mmap_control) {
+ if ((err = snd_pcm_munmap_control(pcm, channel)) < 0)
+ ret = err;
}
- if (mode & SND_PCM_OPEN_CAPTURE) {
- cfd = snd_pcm_open_channel(card, device, SND_PCM_CHANNEL_CAPTURE,
- subdevice, fmode, ctl, &ver);
- if (cfd < 0) {
- if (pfd >= 0)
- close(pfd);
- snd_ctl_close(ctl);
- return cfd;
- }
+ if (chan->mmap_data) {
+ if ((err = snd_pcm_munmap_data(pcm, channel)) < 0)
+ ret = err;
}
- snd_ctl_close(ctl);
- if (pfd < 0 && cfd < 0)
- return -EINVAL;
- pcm = (snd_pcm_t *) calloc(1, sizeof(snd_pcm_t));
- if (pcm == NULL) {
- if (pfd >= 0)
- close(pfd);
- if (cfd >= 0)
- close(cfd);
- return -ENOMEM;
+ if ((err = pcm->ops->channel_close(pcm, channel)) < 0)
+ ret = err;
+ chan->open = 0;
+ chan->valid_setup = 0;
+ if (chan->valid_voices_setup) {
+ chan->valid_voices_setup = 0;
+ free(chan->voices_setup);
}
- pcm->card = card;
- pcm->device = device;
- pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd = pfd;
- pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd = cfd;
- pcm->mode = mode;
- pcm->ver = ver;
- *handle = pcm;
- return 0;
-}
+ return ret;
+}
int snd_pcm_close(snd_pcm_t *pcm)
{
- int res = 0;
+ int err, ret = 0;
int channel;
if (!pcm)
- return -EINVAL;
+ return -EFAULT;
for (channel = 0; channel < 2; ++channel) {
- snd_pcm_plugin_munmap(pcm, channel);
- snd_pcm_plugin_clear(pcm, channel);
- snd_pcm_munmap(pcm, channel);
- if (pcm->chan[channel].fd >= 0)
- if (close(pcm->chan[channel].fd))
- res = -errno;
+ if (pcm->chan[channel].open) {
+ if ((err = snd_pcm_channel_close(pcm, channel)) < 0)
+ ret = err;
+ }
}
free(pcm);
- return res;
+ return ret;
}
-int snd_pcm_file_descriptor(snd_pcm_t *pcm, int channel)
+int snd_pcm_channel_nonblock(snd_pcm_t *pcm, int channel, int nonblock)
{
+ int err;
if (!pcm)
- return -EINVAL;
+ return -EFAULT;
if (channel < 0 || channel > 1)
return -EINVAL;
- return pcm->chan[channel].fd;
-}
-
-int snd_pcm_nonblock_mode(snd_pcm_t *pcm, int nonblock)
-{
- long flags;
- int fd, channel;
-
- if (!pcm)
- return -EINVAL;
- for (channel = 0; channel < 2; ++channel) {
- fd = pcm->chan[channel].fd;
- if (fd < 0)
- continue;
- if ((flags = fcntl(fd, F_GETFL)) < 0)
- return -errno;
- if (nonblock)
- flags |= O_NONBLOCK;
- else
- flags &= ~O_NONBLOCK;
- if (fcntl(fd, F_SETFL, flags) < 0)
- return -errno;
- if (nonblock)
- pcm->mode |= SND_PCM_OPEN_NONBLOCK;
- else
- pcm->mode &= ~SND_PCM_OPEN_NONBLOCK;
- }
+ if (!pcm->chan[channel].open)
+ return -EBADFD;
+ if ((err = pcm->ops->channel_nonblock(pcm, channel, nonblock)) < 0)
+ return err;
+ if (nonblock)
+ pcm->chan[channel].mode |= SND_PCM_NONBLOCK;
+ else
+ pcm->chan[channel].mode &= ~SND_PCM_NONBLOCK;
return 0;
}
-int snd_pcm_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
+int snd_pcm_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
{
- int fd, channel;
if (!pcm || !info)
- return -EINVAL;
- for (channel = 0; channel < 2; ++channel) {
- fd = pcm->chan[channel].fd;
- if (fd >= 0)
- break;
- }
- if (ioctl(fd, SND_PCM_IOCTL_INFO, info) < 0)
- return -errno;
- return 0;
+ return -EFAULT;
+ return pcm->ops->info(pcm, info);
}
-int snd_pcm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
+int snd_pcm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
{
- int fd;
if (!pcm || !info)
- return -EINVAL;
+ return -EFAULT;
if (info->channel < 0 || info->channel > 1)
return -EINVAL;
- fd = pcm->chan[info->channel].fd;
- if (fd < 0)
- return -EINVAL;
- if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_INFO, info) < 0)
- return -errno;
- return 0;
+ if (!pcm->chan[info->channel].open)
+ return -EBADFD;
+ return pcm->ops->channel_info(pcm, info);
}
-int snd_pcm_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params)
+int snd_pcm_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
{
int err;
- int fd;
+ snd_pcm_channel_setup_t setup;
struct snd_pcm_chan *chan;
-
if (!pcm || !params)
- return -EINVAL;
+ return -EFAULT;
if (params->channel < 0 || params->channel > 1)
return -EINVAL;
chan = &pcm->chan[params->channel];
- fd = chan->fd;
- if (fd < 0)
- return -EINVAL;
- if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_PARAMS, params) < 0)
- return -errno;
- chan->setup_is_valid = 0;
- memset(&chan->setup, 0, sizeof(snd_pcm_channel_setup_t));
- chan->setup.channel = params->channel;
- if ((err = snd_pcm_channel_setup(pcm, &chan->setup))<0)
+ if (!chan->open)
+ return -EBADFD;
+ if (chan->mmap_control)
+ return -EBADFD;
+ if ((err = pcm->ops->channel_params(pcm, params)) < 0)
return err;
- return 0;
+ chan->valid_setup = 0;
+ setup.channel = params->channel;
+ return snd_pcm_channel_setup(pcm, &setup);
}
-int snd_pcm_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup)
+int snd_pcm_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup)
{
- int fd;
+ int err;
struct snd_pcm_chan *chan;
-
if (!pcm || !setup)
- return -EINVAL;
+ return -EFAULT;
if (setup->channel < 0 || setup->channel > 1)
return -EINVAL;
chan = &pcm->chan[setup->channel];
- fd = chan->fd;
- if (fd < 0)
- return -EINVAL;
- if (chan->setup_is_valid) {
+ if (!chan->open)
+ return -EBADFD;
+ if (chan->valid_setup) {
memcpy(setup, &chan->setup, sizeof(*setup));
return 0;
}
- if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_SETUP, setup) < 0)
- return -errno;
+ if ((err = pcm->ops->channel_setup(pcm, setup)) < 0)
+ return err;
memcpy(&chan->setup, setup, sizeof(*setup));
- chan->setup_is_valid = 1;
+ chan->valid_setup = 1;
return 0;
}
-int snd_pcm_voice_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t * setup)
+const snd_pcm_channel_setup_t* snd_pcm_channel_cached_setup(snd_pcm_t *pcm, int channel)
+{
+ struct snd_pcm_chan *chan;
+ if (!pcm)
+ return 0;
+ if (channel < 0 || channel > 1)
+ return 0;
+ chan = &pcm->chan[channel];
+ if (!chan->open || !chan->valid_setup)
+ return 0;
+ return &chan->setup;
+}
+
+int snd_pcm_voice_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t *setup)
{
- int fd;
+ struct snd_pcm_chan *chan;
if (!pcm || !setup)
- return -EINVAL;
+ return -EFAULT;
if (channel < 0 || channel > 1)
return -EINVAL;
- fd = pcm->chan[channel].fd;
- if (fd < 0)
+ chan = &pcm->chan[channel];
+ if (!chan->open || !chan->valid_setup)
+ return -EBADFD;
+ if (chan->valid_voices_setup) {
+ if (setup->voice >= chan->setup.format.voices)
+ return -EINVAL;
+ memcpy(setup, &chan->voices_setup[setup->voice], sizeof(*setup));
+ return 0;
+ }
+ return pcm->ops->voice_setup(pcm, channel, setup);
+}
+
+const snd_pcm_voice_setup_t* snd_pcm_channel_cached_voice_setup(snd_pcm_t *pcm, int channel, unsigned int voice)
+{
+ struct snd_pcm_chan *chan;
+ if (!pcm)
+ return 0;
+ if (channel < 0 || channel > 1)
+ return 0;
+ chan = &pcm->chan[channel];
+ if (!chan->open || !chan->valid_setup)
+ return 0;
+ if (voice >= chan->setup.format.voices)
+ return 0;
+ return &chan->voices_setup[voice];
+}
+
+int snd_pcm_all_voices_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t *setup)
+{
+ struct snd_pcm_chan *chan;
+ snd_pcm_voice_setup_t *vs, *v;
+ unsigned int voice;
+ int err;
+ if (!pcm)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
return -EINVAL;
- if (ioctl(fd, SND_PCM_IOCTL_VOICE_SETUP, setup) < 0)
- return -errno;
+ chan = &pcm->chan[channel];
+ if (!chan->open || !chan->valid_setup)
+ return -EBADFD;
+ vs = calloc(chan->setup.format.voices, sizeof(*setup));
+ for (voice = 0, v = vs; voice < chan->setup.format.voices; ++voice, ++v) {
+ v->voice = voice;
+ err = snd_pcm_voice_setup(pcm, channel, v);
+ if (err < 0) {
+ free(vs);
+ return err;
+ }
+ if (setup) {
+ memcpy(setup, v, sizeof(*setup));
+ setup++;
+ }
+ }
+ chan->voices_setup = vs;
+ chan->valid_voices_setup = 1;
return 0;
}
-int snd_pcm_channel_status(snd_pcm_t *pcm, snd_pcm_channel_status_t * status)
+int snd_pcm_channel_status(snd_pcm_t *pcm, snd_pcm_channel_status_t *status)
{
- int fd;
if (!pcm || !status)
+ return -EFAULT;
+ if (status->channel < 0 || status->channel > 1)
return -EINVAL;
- fd = pcm->chan[status->channel].fd;
- if (fd < 0)
- return -EINVAL;
- if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_STATUS, status) < 0)
- return -errno;
- return 0;
+ if (!pcm->chan[status->channel].open)
+ return -EBADFD;
+ return pcm->ops->channel_status(pcm, status);
}
-int snd_pcm_playback_prepare(snd_pcm_t *pcm)
+int snd_pcm_channel_prepare(snd_pcm_t *pcm, int channel)
{
+ int err;
if (!pcm)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
return -EINVAL;
- if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
- return -EINVAL;
- if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_CHANNEL_PREPARE) < 0)
- return -errno;
+ if (!pcm->chan[channel].open)
+ return -EBADFD;
+ err = pcm->ops->channel_prepare(pcm, channel);
+ if (err < 0)
+ return err;
+ snd_pcm_mmap_status_change(pcm, channel, SND_PCM_STATUS_PREPARED);
return 0;
}
+int snd_pcm_playback_prepare(snd_pcm_t *pcm)
+{
+ return snd_pcm_channel_prepare(pcm, SND_PCM_CHANNEL_PLAYBACK);
+}
+
int snd_pcm_capture_prepare(snd_pcm_t *pcm)
{
- if (!pcm)
- return -EINVAL;
- if (pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd < 0)
- return -EINVAL;
- if (ioctl(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, SND_PCM_IOCTL_CHANNEL_PREPARE) < 0)
- return -errno;
- return 0;
+ return snd_pcm_channel_prepare(pcm, SND_PCM_CHANNEL_CAPTURE);
}
-int snd_pcm_channel_prepare(snd_pcm_t *pcm, int channel)
+static int mmap_playback_go(snd_pcm_t *pcm, int channel)
{
- switch (channel) {
- case SND_PCM_CHANNEL_PLAYBACK:
- return snd_pcm_playback_prepare(pcm);
- case SND_PCM_CHANNEL_CAPTURE:
- return snd_pcm_capture_prepare(pcm);
- default:
+ struct snd_pcm_chan *chan = &pcm->chan[channel];
+ if (chan->mmap_control->status != SND_PCM_STATUS_PREPARED)
+ return -EBADFD;
+ if (chan->mmap_control->frag_data == 0)
return -EIO;
- }
+ chan->mmap_control->status = SND_PCM_STATUS_RUNNING;
+ pthread_mutex_lock(&chan->mutex);
+ pthread_cond_signal(&chan->status_cond);
+ pthread_cond_wait(&chan->ready_cond, &chan->mutex);
+ pthread_mutex_unlock(&chan->mutex);
+ return 0;
}
-int snd_pcm_playback_go(snd_pcm_t *pcm)
+int snd_pcm_channel_go(snd_pcm_t *pcm, int channel)
{
+ int err;
+ struct snd_pcm_chan *chan;
if (!pcm)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
return -EINVAL;
- if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
- return -EINVAL;
- if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_CHANNEL_GO) < 0)
- return -errno;
+ chan = &pcm->chan[channel];
+ if (!chan->open)
+ return -EBADFD;
+ if (channel == SND_PCM_CHANNEL_PLAYBACK &&
+ chan->mmap_data_emulation) {
+ err = mmap_playback_go(pcm, channel);
+ if (err < 0)
+ return err;
+ }
+ err = pcm->ops->channel_go(pcm, channel);
+ if (err < 0)
+ return err;
+ if (channel == SND_PCM_CHANNEL_CAPTURE)
+ snd_pcm_mmap_status_change(pcm, channel, SND_PCM_STATUS_RUNNING);
return 0;
}
-int snd_pcm_capture_go(snd_pcm_t *pcm)
+int snd_pcm_playback_go(snd_pcm_t *pcm)
{
- if (!pcm)
- return -EINVAL;
- if (pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd < 0)
- return -EINVAL;
- if (ioctl(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, SND_PCM_IOCTL_CHANNEL_GO) < 0)
- return -errno;
- return 0;
+ return snd_pcm_channel_go(pcm, SND_PCM_CHANNEL_PLAYBACK);
}
-int snd_pcm_channel_go(snd_pcm_t *pcm, int channel)
+int snd_pcm_capture_go(snd_pcm_t *pcm)
{
- switch (channel) {
- case SND_PCM_CHANNEL_PLAYBACK:
- return snd_pcm_playback_go(pcm);
- case SND_PCM_CHANNEL_CAPTURE:
- return snd_pcm_capture_go(pcm);
- default:
- return -EIO;
- }
+ return snd_pcm_channel_go(pcm, SND_PCM_CHANNEL_CAPTURE);
}
int snd_pcm_sync_go(snd_pcm_t *pcm, snd_pcm_sync_t *sync)
{
+ int err;
if (!pcm || !sync)
- return -EINVAL;
- if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
- return -EINVAL;
- if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_SYNC_GO, sync) < 0)
- return -errno;
+ return -EFAULT;
+ if (!pcm->chan[SND_PCM_CHANNEL_PLAYBACK].open &&
+ !pcm->chan[SND_PCM_CHANNEL_CAPTURE].open)
+ return -EBADFD;
+ err = pcm->ops->sync_go(pcm, sync);
+ if (err < 0)
+ return err;
+ /* NYI: mmap emulation */
return 0;
}
-int snd_pcm_playback_drain(snd_pcm_t *pcm)
+int snd_pcm_channel_drain(snd_pcm_t *pcm, int channel)
{
+ int err;
if (!pcm)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
return -EINVAL;
- if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
- return -EINVAL;
- if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_CHANNEL_DRAIN) < 0)
- return -errno;
+ if (!pcm->chan[channel].open)
+ return -EBADFD;
+ if (channel != SND_PCM_CHANNEL_PLAYBACK)
+ return -EBADFD;
+ err = pcm->ops->channel_drain(pcm, channel);
+ if (err < 0)
+ return err;
+ snd_pcm_mmap_status_change(pcm, channel, SND_PCM_STATUS_READY);
return 0;
}
-int snd_pcm_playback_flush(snd_pcm_t *pcm)
+int snd_pcm_playback_drain(snd_pcm_t *pcm)
{
- if (!pcm)
- return -EINVAL;
- if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
- return -EINVAL;
- if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_CHANNEL_FLUSH) < 0)
- return -errno;
- return 0;
+ return snd_pcm_channel_drain(pcm, SND_PCM_CHANNEL_PLAYBACK);
}
-int snd_pcm_capture_flush(snd_pcm_t *pcm)
+int snd_pcm_channel_flush(snd_pcm_t *pcm, int channel)
{
+ int err;
if (!pcm)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
return -EINVAL;
- if (pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd < 0)
- return -EINVAL;
- if (ioctl(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, SND_PCM_IOCTL_CHANNEL_FLUSH) < 0)
- return -errno;
+ if (!pcm->chan[channel].open)
+ return -EBADFD;
+ err = pcm->ops->channel_flush(pcm, channel);
+ if (err < 0)
+ return err;
+ snd_pcm_mmap_status_change(pcm, channel, SND_PCM_STATUS_READY);
return 0;
}
-int snd_pcm_channel_flush(snd_pcm_t *pcm, int channel)
+int snd_pcm_playback_flush(snd_pcm_t *pcm)
{
- switch (channel) {
- case SND_PCM_CHANNEL_PLAYBACK:
- return snd_pcm_playback_flush(pcm);
- case SND_PCM_CHANNEL_CAPTURE:
- return snd_pcm_capture_flush(pcm);
- default:
- return -EIO;
- }
+ return snd_pcm_channel_flush(pcm, SND_PCM_CHANNEL_PLAYBACK);
}
-int snd_pcm_playback_pause(snd_pcm_t *pcm, int enable)
+int snd_pcm_capture_flush(snd_pcm_t *pcm)
+{
+ return snd_pcm_channel_flush(pcm, SND_PCM_CHANNEL_CAPTURE);
+}
+
+int snd_pcm_channel_pause(snd_pcm_t *pcm, int channel, int enable)
{
+ int err;
if (!pcm)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
return -EINVAL;
- if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
- return -EINVAL;
- if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_CHANNEL_PAUSE, &enable) < 0)
- return -errno;
+ if (!pcm->chan[channel].open)
+ return -EBADFD;
+ if (channel != SND_PCM_CHANNEL_PLAYBACK)
+ return -EBADFD;
+ err = pcm->ops->channel_pause(pcm, channel, enable);
+ if (err < 0)
+ return err;
+ snd_pcm_mmap_status_change(pcm, channel, SND_PCM_STATUS_PAUSED);
return 0;
}
+int snd_pcm_playback_pause(snd_pcm_t *pcm, int enable)
+{
+ return snd_pcm_channel_pause(pcm, SND_PCM_CHANNEL_PLAYBACK, enable);
+}
+
ssize_t snd_pcm_transfer_size(snd_pcm_t *pcm, int channel)
{
struct snd_pcm_chan *chan;
- if (!pcm || channel < 0 || channel > 1)
+ if (!pcm)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
return -EINVAL;
chan = &pcm->chan[channel];
- if (!chan->setup_is_valid)
+ if (!chan->open)
+ return -EBADFD;
+ if (!chan->valid_setup)
return -EBADFD;
if (chan->setup.mode != SND_PCM_MODE_BLOCK)
return -EBADFD;
- return chan->setup.buf.block.frag_size;
+ return chan->setup.frag_size;
}
ssize_t snd_pcm_write(snd_pcm_t *pcm, const void *buffer, size_t size)
{
- ssize_t result;
-
- if (!pcm || (!buffer && size > 0) || size < 0)
- return -EINVAL;
- if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
- return -EINVAL;
- result = write(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, buffer, size);
- if (result < 0)
- return -errno;
- return result;
+ if (!pcm)
+ return -EFAULT;
+ if (!pcm->chan[SND_PCM_CHANNEL_PLAYBACK].open ||
+ !pcm->chan[SND_PCM_CHANNEL_PLAYBACK].valid_setup)
+ return -EBADFD;
+ if (size > 0 && !buffer)
+ return -EFAULT;
+ return pcm->ops->write(pcm, buffer, size);
}
-ssize_t snd_pcm_writev(snd_pcm_t *pcm, const struct iovec *vector, int count)
+ssize_t snd_pcm_writev(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count)
{
- ssize_t result;
-
- if (!pcm || (!vector && count > 0) || count < 0)
- return -EINVAL;
- if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0)
- return -EINVAL;
-#if 0
- result = writev(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, vector, count);
-#else
- {
- snd_v_args_t args;
- args.vector = vector;
- args.count = count;
- result = ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_IOCTL_WRITEV, &args);
- }
-#endif
- if (result < 0)
- return -errno;
- return result;
+ if (!pcm)
+ return -EFAULT;
+ if (!pcm->chan[SND_PCM_CHANNEL_PLAYBACK].open ||
+ !pcm->chan[SND_PCM_CHANNEL_PLAYBACK].valid_setup)
+ return -EBADFD;
+ if (count > 0 && !vector)
+ return -EFAULT;
+ return pcm->ops->writev(pcm, vector, count);
}
ssize_t snd_pcm_read(snd_pcm_t *pcm, void *buffer, size_t size)
{
- ssize_t result;
-
- if (!pcm || (!buffer && size > 0) || size < 0)
- return -EINVAL;
- if (pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd < 0)
- return -EINVAL;
- result = read(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, buffer, size);
- if (result < 0)
- return -errno;
- return result;
+ if (!pcm)
+ return -EFAULT;
+ if (!pcm->chan[SND_PCM_CHANNEL_CAPTURE].open ||
+ !pcm->chan[SND_PCM_CHANNEL_CAPTURE].valid_setup)
+ return -EBADFD;
+ if (size > 0 && !buffer)
+ return -EFAULT;
+ return pcm->ops->read(pcm, buffer, size);
}
-ssize_t snd_pcm_readv(snd_pcm_t *pcm, const struct iovec *vector, int count)
+ssize_t snd_pcm_readv(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count)
{
- ssize_t result;
+ if (!pcm)
+ return -EFAULT;
+ if (!pcm->chan[SND_PCM_CHANNEL_CAPTURE].open ||
+ !pcm->chan[SND_PCM_CHANNEL_CAPTURE].valid_setup)
+ return -EBADFD;
+ if (count > 0 && !vector)
+ return -EFAULT;
+ return pcm->ops->readv(pcm, vector, count);
+}
- if (!pcm || (!vector && count > 0) || count < 0)
- return -EINVAL;
- if (pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd < 0)
+int snd_pcm_file_descriptor(snd_pcm_t* pcm, int channel)
+{
+ if (!pcm)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
return -EINVAL;
-#if 0
- result = readv(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, vector, count);
-#else
- {
- snd_v_args_t args;
- args.vector = vector;
- args.count = count;
- result = ioctl(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, SND_IOCTL_READV, &args);
- }
-#endif
- if (result < 0)
- return -errno;
- return result;
+ if (!pcm->chan[channel].open)
+ return -EBADFD;
+ return pcm->ops->file_descriptor(pcm, channel);
}
-int snd_pcm_mmap(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t **control, void **buffer)
+int snd_pcm_voices_mask(snd_pcm_t *pcm, int channel, bitset_t *client_vmask)
{
- snd_pcm_channel_info_t info;
- int err, fd, prot;
- void *caddr, *daddr;
- struct snd_pcm_chan *chan;
-
- if (control)
- *control = NULL;
- if (buffer)
- *buffer = NULL;
- if (!pcm || channel < 0 || channel > 1 || !control || !buffer)
- return -EINVAL;
- chan = &pcm->chan[channel];
- fd = chan->fd;
- if (fd < 0)
+ if (!pcm)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
return -EINVAL;
- memset(&info, 0, sizeof(info));
- info.channel = channel;
- if ((err = snd_pcm_channel_info(pcm, &info))<0)
- return err;
- caddr = mmap(NULL, sizeof(snd_pcm_mmap_control_t), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, fd, SND_PCM_MMAP_OFFSET_CONTROL);
- if (caddr == (caddr_t)-1 || caddr == NULL)
- return -errno;
- prot = channel == SND_PCM_CHANNEL_PLAYBACK ? PROT_WRITE : PROT_READ;
- daddr = mmap(NULL, info.mmap_size, prot, MAP_FILE|MAP_SHARED, fd, SND_PCM_MMAP_OFFSET_DATA);
- if (daddr == (caddr_t)-1 || daddr == NULL) {
- err = -errno;
- munmap(caddr, sizeof(snd_pcm_mmap_control_t));
- return err;
- }
- *control = chan->mmap_control = caddr;
- *buffer = chan->mmap_data = daddr;
- chan->mmap_size = info.mmap_size;
- return 0;
+ if (!pcm->chan[channel].open)
+ return -EBADFD;
+ return pcm->ops->voices_mask(pcm, channel, client_vmask);
}
-int snd_pcm_munmap(snd_pcm_t *pcm, int channel)
+int snd_pcm_bytes_per_second(snd_pcm_t *pcm, int channel)
{
struct snd_pcm_chan *chan;
- if (!pcm || channel < 0 || channel > 1)
+ if (!pcm)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
return -EINVAL;
chan = &pcm->chan[channel];
- if (chan->mmap_control) {
- munmap(chan->mmap_control, sizeof(snd_pcm_mmap_control_t));
- chan->mmap_control = NULL;
- }
- if (chan->mmap_data) {
- munmap(chan->mmap_data, chan->mmap_size);
- chan->mmap_data = NULL;
- chan->mmap_size = 0;
- }
- return 0;
+ if (!chan->open || !chan->valid_setup)
+ return -EBADFD;
+ return snd_pcm_format_bytes_per_second(&chan->setup.format);
}
+
--- /dev/null
+/*
+ * PCM - Hardware
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include "pcm_local.h"
+
+#define SND_FILE_PCM_PLAYBACK "/dev/snd/pcmC%iD%ip"
+#define SND_FILE_PCM_CAPTURE "/dev/snd/pcmC%iD%ic"
+#define SND_PCM_VERSION_MAX SND_PROTOCOL_VERSION(2, 0, 0)
+
+static int snd_pcm_hw_channel_close(snd_pcm_t *pcm, int channel)
+{
+ snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private;
+ if (hw->chan[channel].fd >= 0)
+ if (close(hw->chan[channel].fd))
+ return -errno;
+ return 0;
+}
+
+int snd_pcm_hw_channel_fd(snd_pcm_t *pcm, int channel)
+{
+ snd_pcm_hw_t *hw;
+ if (!pcm)
+ return -EINVAL;
+ if (channel < 0 || channel > 1)
+ return -EINVAL;
+ hw = (snd_pcm_hw_t*) &pcm->private;
+ return hw->chan[channel].fd;
+}
+
+static int snd_pcm_hw_channel_nonblock(snd_pcm_t *pcm, int channel, int nonblock)
+{
+ long flags;
+ int fd;
+ snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private;
+ fd = hw->chan[channel].fd;
+
+ if ((flags = fcntl(fd, F_GETFL)) < 0)
+ return -errno;
+ if (nonblock)
+ flags |= O_NONBLOCK;
+ else
+ flags &= ~O_NONBLOCK;
+ if (fcntl(fd, F_SETFL, flags) < 0)
+ return -errno;
+ return 0;
+}
+
+static int snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
+{
+ int fd, channel;
+ snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private;
+ for (channel = 0; channel < 2; ++channel) {
+ fd = hw->chan[channel].fd;
+ if (fd >= 0)
+ break;
+ }
+ if (fd < 0)
+ return -EBADFD;
+ if (ioctl(fd, SND_PCM_IOCTL_INFO, info) < 0)
+ return -errno;
+ return 0;
+}
+
+static int snd_pcm_hw_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
+{
+ int fd;
+ snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private;
+ fd = hw->chan[info->channel].fd;
+ if (fd < 0)
+ return -EINVAL;
+ if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_INFO, info) < 0)
+ return -errno;
+ return 0;
+}
+
+static int snd_pcm_hw_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params)
+{
+ int fd;
+ snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private;
+ fd = hw->chan[params->channel].fd;
+ if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_PARAMS, params) < 0)
+ return -errno;
+ return 0;
+}
+
+static int snd_pcm_hw_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup)
+{
+ int fd;
+ snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private;
+ fd = hw->chan[setup->channel].fd;
+ if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_SETUP, setup) < 0)
+ return -errno;
+ return 0;
+}
+
+static int snd_pcm_hw_voice_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t * setup)
+{
+ int fd;
+ snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private;
+ fd = hw->chan[channel].fd;
+ if (ioctl(fd, SND_PCM_IOCTL_VOICE_SETUP, setup) < 0)
+ return -errno;
+ return 0;
+}
+
+static int snd_pcm_hw_channel_status(snd_pcm_t *pcm, snd_pcm_channel_status_t * status)
+{
+ int fd;
+ snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private;
+ fd = hw->chan[status->channel].fd;
+ if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_STATUS, status) < 0)
+ return -errno;
+ return 0;
+}
+
+static int snd_pcm_hw_channel_prepare(snd_pcm_t *pcm, int channel)
+{
+ int fd;
+ snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private;
+ fd = hw->chan[channel].fd;
+ if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_PREPARE) < 0)
+ return -errno;
+ return 0;
+}
+
+static int snd_pcm_hw_channel_go(snd_pcm_t *pcm, int channel)
+{
+ int fd;
+ snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private;
+ fd = hw->chan[channel].fd;
+ if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_GO) < 0)
+ return -errno;
+ return 0;
+}
+
+static int snd_pcm_hw_sync_go(snd_pcm_t *pcm, snd_pcm_sync_t *sync)
+{
+ int fd;
+ snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private;
+ if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].open)
+ fd = hw->chan[SND_PCM_CHANNEL_PLAYBACK].fd;
+ else
+ fd = hw->chan[SND_PCM_CHANNEL_CAPTURE].fd;
+ if (ioctl(fd, SND_PCM_IOCTL_SYNC_GO, sync) < 0)
+ return -errno;
+ return 0;
+}
+
+static int snd_pcm_hw_channel_drain(snd_pcm_t *pcm, int channel)
+{
+ int fd;
+ snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private;
+ fd = hw->chan[channel].fd;
+ if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_DRAIN) < 0)
+ return -errno;
+ return 0;
+}
+
+static int snd_pcm_hw_channel_flush(snd_pcm_t *pcm, int channel)
+{
+ int fd;
+ snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private;
+ fd = hw->chan[channel].fd;
+ if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_FLUSH) < 0)
+ return -errno;
+ return 0;
+}
+
+static int snd_pcm_hw_channel_pause(snd_pcm_t *pcm, int channel, int enable)
+{
+ int fd;
+ snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private;
+ fd = hw->chan[channel].fd;
+ if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_PAUSE, &enable) < 0)
+ return -errno;
+ return 0;
+}
+
+static ssize_t snd_pcm_hw_write(snd_pcm_t *pcm, const void *buffer, size_t size)
+{
+ ssize_t result;
+ int fd;
+ snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private;
+ fd = hw->chan[SND_PCM_CHANNEL_PLAYBACK].fd;
+ result = write(fd, buffer, size);
+ if (result < 0)
+ return -errno;
+ return result;
+}
+
+static ssize_t snd_pcm_hw_writev(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count)
+{
+ ssize_t result;
+ int fd;
+ snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private;
+ fd = hw->chan[SND_PCM_CHANNEL_PLAYBACK].fd;
+#if 0
+ result = writev(fd, vector, count);
+#else
+ {
+ snd_v_args_t args;
+ args.vector = vector;
+ args.count = count;
+ result = ioctl(fd, SND_IOCTL_WRITEV, &args);
+ }
+#endif
+ if (result < 0)
+ return -errno;
+ return result;
+}
+
+static ssize_t snd_pcm_hw_read(snd_pcm_t *pcm, void *buffer, size_t size)
+{
+ ssize_t result;
+ int fd;
+ snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private;
+ fd = hw->chan[SND_PCM_CHANNEL_CAPTURE].fd;
+ result = read(fd, buffer, size);
+ if (result < 0)
+ return -errno;
+ return result;
+}
+
+ssize_t snd_pcm_hw_readv(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count)
+{
+ ssize_t result;
+ int fd;
+ snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private;
+ fd = hw->chan[SND_PCM_CHANNEL_CAPTURE].fd;
+#if 0
+ result = readv(fd, vector, count);
+#else
+ {
+ snd_v_args_t args;
+ args.vector = vector;
+ args.count = count;
+ result = ioctl(fd, SND_IOCTL_READV, &args);
+ }
+#endif
+ if (result < 0)
+ return -errno;
+ return result;
+}
+
+static int snd_pcm_hw_mmap_control(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t **control, size_t csize)
+{
+ void *caddr;
+ snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private;
+ caddr = mmap(NULL, csize, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED,
+ hw->chan[channel].fd, SND_PCM_MMAP_OFFSET_CONTROL);
+ if (caddr == MAP_FAILED || caddr == NULL)
+ return -errno;
+ *control = caddr;
+ return 0;
+}
+
+static int snd_pcm_hw_mmap_data(snd_pcm_t *pcm, int channel, void **buffer, size_t bsize)
+{
+ int prot;
+ void *daddr;
+ snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private;
+ prot = channel == SND_PCM_CHANNEL_PLAYBACK ? PROT_WRITE : PROT_READ;
+ daddr = mmap(NULL, bsize, prot, MAP_FILE|MAP_SHARED,
+ hw->chan[channel].fd, SND_PCM_MMAP_OFFSET_DATA);
+ if (daddr == MAP_FAILED || daddr == NULL)
+ return -errno;
+ *buffer = daddr;
+ return 0;
+}
+
+static int snd_pcm_hw_munmap_control(snd_pcm_t *pcm UNUSED, int channel UNUSED, snd_pcm_mmap_control_t *control, size_t csize)
+{
+ if (munmap(control, csize) < 0)
+ return -errno;
+ return 0;
+}
+
+static int snd_pcm_hw_munmap_data(snd_pcm_t *pcm UNUSED, int channel UNUSED, void *buffer, size_t bsize)
+{
+ if (munmap(buffer, bsize) < 0)
+ return -errno;
+ return 0;
+}
+
+static int snd_pcm_hw_file_descriptor(snd_pcm_t *pcm, int channel)
+{
+ snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private;
+ return hw->chan[channel].fd;
+}
+
+static int snd_pcm_hw_voices_mask(snd_pcm_t *pcm UNUSED, int channel UNUSED,
+ bitset_t *client_vmask UNUSED)
+{
+ return 0;
+}
+
+struct snd_pcm_ops snd_pcm_hw_ops = {
+ channel_close: snd_pcm_hw_channel_close,
+ channel_nonblock: snd_pcm_hw_channel_nonblock,
+ info: snd_pcm_hw_info,
+ channel_info: snd_pcm_hw_channel_info,
+ channel_params: snd_pcm_hw_channel_params,
+ channel_setup: snd_pcm_hw_channel_setup,
+ voice_setup: snd_pcm_hw_voice_setup,
+ channel_status: snd_pcm_hw_channel_status,
+ channel_prepare: snd_pcm_hw_channel_prepare,
+ channel_go: snd_pcm_hw_channel_go,
+ sync_go: snd_pcm_hw_sync_go,
+ channel_drain: snd_pcm_hw_channel_drain,
+ channel_flush: snd_pcm_hw_channel_flush,
+ channel_pause: snd_pcm_hw_channel_pause,
+ write: snd_pcm_hw_write,
+ writev: snd_pcm_hw_writev,
+ read: snd_pcm_hw_read,
+ readv: snd_pcm_hw_readv,
+ mmap_control: snd_pcm_hw_mmap_control,
+ mmap_data: snd_pcm_hw_mmap_data,
+ munmap_control: snd_pcm_hw_munmap_control,
+ munmap_data: snd_pcm_hw_munmap_data,
+ file_descriptor: snd_pcm_hw_file_descriptor,
+ voices_mask: snd_pcm_hw_voices_mask,
+};
+
+static int snd_pcm_hw_open_channel(int card, int device, int channel, int subdevice, int fmode, snd_ctl_t *ctl, int *ver)
+{
+ char filename[32];
+ char *filefmt;
+ int err, fd;
+ int attempt = 0;
+ snd_pcm_channel_info_t info;
+ switch (channel) {
+ case SND_PCM_CHANNEL_PLAYBACK:
+ filefmt = SND_FILE_PCM_PLAYBACK;
+ break;
+ case SND_PCM_CHANNEL_CAPTURE:
+ filefmt = SND_FILE_PCM_CAPTURE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if ((err = snd_ctl_pcm_channel_prefer_subdevice(ctl, device, channel, subdevice)) < 0)
+ return err;
+ sprintf(filename, filefmt, card, device);
+
+ __again:
+ if (attempt++ > 3) {
+ snd_ctl_close(ctl);
+ return -EBUSY;
+ }
+ if ((fd = open(filename, fmode)) < 0) {
+ err = -errno;
+ return err;
+ }
+ if (ioctl(fd, SND_PCM_IOCTL_PVERSION, ver) < 0) {
+ err = -errno;
+ close(fd);
+ return err;
+ }
+ if (SND_PROTOCOL_INCOMPATIBLE(*ver, SND_PCM_VERSION_MAX)) {
+ close(fd);
+ return -SND_ERROR_INCOMPATIBLE_VERSION;
+ }
+ if (subdevice >= 0) {
+ memset(&info, 0, sizeof(info));
+ if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_INFO, &info) < 0) {
+ err = -errno;
+ close(fd);
+ return err;
+ }
+ if (info.subdevice != subdevice) {
+ close(fd);
+ goto __again;
+ }
+ }
+ return fd;
+}
+
+int snd_pcm_open_subdevice(snd_pcm_t **handle, int card, int device, int subdevice, int mode)
+{
+ int fmode, ver, err;
+ snd_pcm_t *pcm;
+ snd_pcm_hw_t *hw;
+ snd_ctl_t *ctl;
+ int pfd = -1, cfd = -1;
+
+ if (!handle)
+ return -EFAULT;
+ *handle = NULL;
+
+ if (card < 0 || card >= SND_CARDS)
+ return -EINVAL;
+ if ((err = snd_ctl_open(&ctl, card)) < 0)
+ return err;
+ if (mode & SND_PCM_OPEN_PLAYBACK) {
+ fmode = O_RDWR;
+ if (mode & SND_PCM_NONBLOCK_PLAYBACK)
+ fmode |= O_NONBLOCK;
+ pfd = snd_pcm_hw_open_channel(card, device, SND_PCM_CHANNEL_PLAYBACK,
+ subdevice, fmode, ctl, &ver);
+ if (pfd < 0) {
+ snd_ctl_close(ctl);
+ return pfd;
+ }
+ }
+ if (mode & SND_PCM_OPEN_CAPTURE) {
+ fmode = O_RDWR;
+ if (mode & SND_PCM_NONBLOCK_CAPTURE)
+ fmode |= O_NONBLOCK;
+ cfd = snd_pcm_hw_open_channel(card, device, SND_PCM_CHANNEL_CAPTURE,
+ subdevice, fmode, ctl, &ver);
+ if (cfd < 0) {
+ if (pfd >= 0)
+ close(pfd);
+ snd_ctl_close(ctl);
+ return cfd;
+ }
+ }
+ snd_ctl_close(ctl);
+ if (pfd < 0 && cfd < 0)
+ return -EINVAL;
+
+ err = snd_pcm_abstract_open(handle, mode, SND_PCM_TYPE_HW, sizeof(snd_pcm_hw_t));
+ if (err < 0) {
+ if (pfd >= 0)
+ close(pfd);
+ if (cfd >= 0)
+ close(cfd);
+ return err;
+ }
+ pcm = *handle;
+ pcm->ops = &snd_pcm_hw_ops;
+ hw = (snd_pcm_hw_t*) &pcm->private;
+ hw->card = card;
+ hw->device = device;
+ hw->ver = ver;
+ hw->chan[SND_PCM_CHANNEL_PLAYBACK].fd = pfd;
+ hw->chan[SND_PCM_CHANNEL_CAPTURE].fd = cfd;
+ return 0;
+}
+
+int snd_pcm_open(snd_pcm_t **handle, int card, int device, int mode)
+{
+ return snd_pcm_open_subdevice(handle, card, device, -1, mode);
+}
+
#include <pthread.h>
#include "asoundlib.h"
-struct snd_pcm_plug {
+
+struct snd_pcm_ops {
+ int (*channel_close)(snd_pcm_t *pcm, int channel);
+ int (*channel_nonblock)(snd_pcm_t *pcm, int channel, int nonblock);
+ int (*info)(snd_pcm_t *pcm, snd_pcm_info_t *info);
+ 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);
+ int (*voice_setup)(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t *setup);
+ int (*channel_status)(snd_pcm_t *pcm, snd_pcm_channel_status_t *status);
+ int (*channel_prepare)(snd_pcm_t *pcm, int channel);
+ int (*channel_go)(snd_pcm_t *pcm, int channel);
+ int (*sync_go)(snd_pcm_t *pcm, snd_pcm_sync_t *sync);
+ int (*channel_drain)(snd_pcm_t *pcm, int channel);
+ int (*channel_flush)(snd_pcm_t *pcm, int channel);
+ int (*channel_pause)(snd_pcm_t *pcm, int channel, int enable);
+ ssize_t (*write)(snd_pcm_t *pcm, const void *buffer, size_t size);
+ ssize_t (*writev)(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count);
+ ssize_t (*read)(snd_pcm_t *pcm, void *buffer, size_t size);
+ ssize_t (*readv)(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count);
+ int (*mmap_control)(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t **control, size_t csize);
+ int (*mmap_data)(snd_pcm_t *pcm, int channel, void **buffer, size_t bsize);
+ int (*munmap_control)(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t *control, size_t csize);
+ int (*munmap_data)(snd_pcm_t *pcm, int channel, void *buffer, size_t bsize);
+ int (*file_descriptor)(snd_pcm_t* pcm, int channel);
+ int (*voices_mask)(snd_pcm_t *pcm, int channel, bitset_t *client_vmask);
+};
+
+
+struct snd_pcm_plug_chan {
snd_pcm_plugin_t *first;
snd_pcm_plugin_t *last;
void *alloc_ptr[2];
- long alloc_size[2];
+ size_t alloc_size[2];
int alloc_lock[2];
- snd_pcm_mmap_control_t *mmap_control;
- char *mmap_data;
- long mmap_size;
- pthread_t thread;
- int thread_stop;
- int setup_is_valid;
- snd_pcm_channel_setup_t setup;
- int hwstatus;
};
-struct snd_pcm_chan {
+typedef struct snd_pcm_plug {
+ int close_slave;
+ snd_pcm_t *slave;
+ struct snd_pcm_plug_chan chan[2];
+} snd_pcm_plug_t;
+
+struct snd_pcm_hw_chan {
int fd;
- int setup_is_valid;
+};
+
+typedef struct snd_pcm_hw {
+ int card;
+ int device;
+ int ver;
+ struct snd_pcm_hw_chan chan[2];
+} snd_pcm_hw_t;
+
+struct snd_pcm_chan {
+ int open;
+ int mode;
+ int valid_setup;
snd_pcm_channel_setup_t setup;
+ int valid_voices_setup;
+ snd_pcm_voice_setup_t *voices_setup;
snd_pcm_mmap_control_t *mmap_control;
+ size_t mmap_control_size;
+ int mmap_control_emulation;
char *mmap_data;
- long mmap_size;
- struct snd_pcm_plug plug;
+ size_t mmap_data_size;
+ int mmap_data_emulation;
+ pthread_t mmap_thread;
+ int mmap_thread_stop;
+ pthread_mutex_t mutex;
+ pthread_cond_t status_cond;
+ pthread_cond_t ready_cond;
};
struct snd_pcm {
- int card;
- int device;
+ snd_pcm_type_t type;
int mode;
- int ver;
+ struct snd_pcm_ops *ops;
struct snd_pcm_chan chan[2];
+ int private[0];
};
-unsigned int snd_pcm_plugin_formats(unsigned int formats);
-int snd_pcm_plugin_hwparams(snd_pcm_channel_params_t *params,
- snd_pcm_channel_info_t *hwinfo,
- snd_pcm_channel_params_t *hwparams);
-int snd_pcm_plugin_format(snd_pcm_t *pcm,
- snd_pcm_channel_params_t *params,
- snd_pcm_channel_params_t *hwparams,
- snd_pcm_channel_info_t *hwinfo);
+void snd_pcm_mmap_status_change(snd_pcm_t *pcm, int channel, int newstatus);
+
+int snd_pcm_abstract_open(snd_pcm_t **handle, int mode, snd_pcm_type_t type, size_t extra);
+
+
+unsigned int snd_pcm_plug_formats(unsigned int formats);
+int snd_pcm_plug_slave_params(snd_pcm_channel_params_t *params,
+ snd_pcm_channel_info_t *slave_info,
+ snd_pcm_channel_params_t *slave_params);
+int snd_pcm_plug_format(snd_pcm_plugin_handle_t *pcm,
+ snd_pcm_channel_params_t *params,
+ snd_pcm_channel_params_t *slave_params);
+
+ssize_t snd_pcm_plug_write_transfer(snd_pcm_plugin_handle_t *handle, snd_pcm_plugin_voice_t *src_voices, size_t size);
+ssize_t snd_pcm_plug_read_transfer(snd_pcm_plugin_handle_t *handle, snd_pcm_plugin_voice_t *dst_voices_final, size_t size);
+ssize_t snd_pcm_plug_client_voices_iovec(snd_pcm_plugin_handle_t *handle, int channel,
+ const struct iovec *vector, unsigned long count,
+ snd_pcm_plugin_voice_t **voices);
+ssize_t snd_pcm_plug_client_voices_buf(snd_pcm_plugin_handle_t *handle, int channel,
+ char *buf, size_t count,
+ snd_pcm_plugin_voice_t **voices);
+
+int snd_pcm_plug_playback_voices_mask(snd_pcm_plugin_handle_t *handle,
+ bitset_t *client_vmask);
+int snd_pcm_plug_capture_voices_mask(snd_pcm_plugin_handle_t *handle,
+ bitset_t *client_vmask);
+int snd_pcm_plugin_client_voices(snd_pcm_plugin_t *plugin,
+ size_t samples,
+ snd_pcm_plugin_voice_t **voices);
+
+void *snd_pcm_plug_buf_alloc(snd_pcm_t *pcm, int channel, size_t size);
+void snd_pcm_plug_buf_unlock(snd_pcm_t *pcm, int channel, void *ptr);
#define ROUTE_PLUGIN_RESOLUTION 16
int getput_index(int format);
-int copy_index(int src_format, int dst_format);
+int copy_index(int format);
+int conv_index(int src_format, int dst_format);
-void zero_voice(snd_pcm_plugin_t *plugin,
- const snd_pcm_plugin_voice_t *dst_voice,
- size_t samples);
+void snd_pcm_plugin_silence_voice(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_voice_t *dst_voice,
+ size_t samples);
#ifdef PLUGIN_DEBUG
-#define pdprintf( args... ) printf( "plugin: " ##args)
+#define pdprintf( args... ) fprintf(stderr, "plugin: " ##args)
#else
#define pdprintf( args... ) { ; }
#endif
+++ /dev/null
-/*
- * PCM LoopBack Interface - main file
- * 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.
- *
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include "asoundlib.h"
-
-#define SND_FILE_PCM_LB "/proc/asound/%i/pcmloopD%iS%i%s"
-#define SND_PCM_LB_VERSION_MAX SND_PROTOCOL_VERSION(1, 0, 0)
-
-struct snd_pcm_loopback {
- int card;
- int device;
- int fd;
- long mode;
- size_t buffer_size;
- char *buffer;
-};
-
-int snd_pcm_loopback_open(snd_pcm_loopback_t **handle, int card, int device, int subchn, int mode)
-{
- int fd, ver;
- char filename[32];
- snd_pcm_loopback_t *lb;
-
- *handle = NULL;
-
- if (card < 0 || card >= SND_CARDS)
- return -EINVAL;
- sprintf(filename, SND_FILE_PCM_LB, card, device, subchn,
- mode == SND_PCM_LB_OPEN_CAPTURE ? "c" : "p");
- if ((fd = open(filename, mode)) < 0) {
- snd_card_load(card);
- if ((fd = open(filename, mode)) < 0)
- return -errno;
- }
- if (ioctl(fd, SND_PCM_LB_IOCTL_PVERSION, &ver) < 0) {
- close(fd);
- return -errno;
- }
- if (SND_PROTOCOL_INCOMPATIBLE(ver, SND_PCM_LB_VERSION_MAX)) {
- close(fd);
- return -SND_ERROR_INCOMPATIBLE_VERSION;
- }
- lb = (snd_pcm_loopback_t *) calloc(1, sizeof(snd_pcm_loopback_t));
- if (lb == NULL) {
- close(fd);
- return -ENOMEM;
- }
- lb->card = card;
- lb->device = device;
- lb->fd = fd;
- lb->mode = SND_PCM_LB_STREAM_MODE_RAW;
- *handle = lb;
- return 0;
-}
-
-int snd_pcm_loopback_close(snd_pcm_loopback_t *lb)
-{
- int res;
-
- if (!lb)
- return -EINVAL;
- if (lb->buffer)
- free(lb->buffer);
- res = close(lb->fd) < 0 ? -errno : 0;
- free(lb);
- return res;
-}
-
-int snd_pcm_loopback_file_descriptor(snd_pcm_loopback_t *lb)
-{
- if (!lb)
- return -EINVAL;
- return lb->fd;
-}
-
-int snd_pcm_loopback_block_mode(snd_pcm_loopback_t *lb, int enable)
-{
- long flags;
-
- if (!lb)
- return -EINVAL;
- if ((flags = fcntl(lb->fd, F_GETFL)) < 0)
- return -errno;
- if (enable)
- flags &= ~O_NONBLOCK;
- else
- flags |= O_NONBLOCK;
- if (fcntl(lb->fd, F_SETFL, flags) < 0)
- return -errno;
- return 0;
-}
-
-int snd_pcm_loopback_stream_mode(snd_pcm_loopback_t *lb, int mode)
-{
- if (!lb || (mode != SND_PCM_LB_STREAM_MODE_RAW &&
- mode != SND_PCM_LB_STREAM_MODE_PACKET))
- return -EINVAL;
- lb->mode = mode;
- if (ioctl(lb->fd, SND_PCM_LB_IOCTL_STREAM_MODE, &lb->mode) < 0)
- return -errno;
- return 0;
-}
-
-int snd_pcm_loopback_format(snd_pcm_loopback_t *lb, snd_pcm_format_t * format)
-{
- if (!lb)
- return -EINVAL;
- if (ioctl(lb->fd, SND_PCM_LB_IOCTL_FORMAT, format) < 0)
- return -errno;
- return 0;
-}
-
-int snd_pcm_loopback_status(snd_pcm_loopback_t *lb, snd_pcm_loopback_status_t * status)
-{
- if (!lb)
- return -EINVAL;
- if (ioctl(lb->fd, SND_PCM_LB_IOCTL_FORMAT, status) < 0)
- return -errno;
- return 0;
-}
-
-ssize_t snd_pcm_loopback_read(snd_pcm_loopback_t *lb, snd_pcm_loopback_callbacks_t *callbacks)
-{
- ssize_t result = 0, res, count;
- size_t size;
- char *buf;
- snd_pcm_loopback_header_t header;
-
- if (!lb || !callbacks)
- return -EINVAL;
- if (callbacks->max_buffer_size == 0)
- size = 64 * 1024;
- else
- size = callbacks->max_buffer_size < 16 ? 16 : callbacks->max_buffer_size;
- while (1) {
- if (lb->mode == SND_PCM_LB_STREAM_MODE_RAW) {
- header.size = size;
- header.type = SND_PCM_LB_TYPE_DATA;
- } else {
- res = read(lb->fd, &header, sizeof(header));
- if (res < 0)
- return -errno;
- if (res == 0)
- break;
- if (res != sizeof(header))
- return -EBADFD;
- result += res;
- }
- switch (header.type) {
- case SND_PCM_LB_TYPE_DATA:
- if (lb->buffer_size < size) {
- buf = (char *) realloc(lb->buffer, size);
- if (buf == NULL)
- return -ENOMEM;
- lb->buffer = buf;
- lb->buffer_size = size;
- } else {
- buf = lb->buffer;
- }
- while (header.size > 0) {
- count = header.size;
- if (count > size)
- count = size;
- res = read(lb->fd, buf, count);
- if (res < 0)
- return -errno;
- result += res;
- if (lb->mode == SND_PCM_LB_STREAM_MODE_PACKET && res != count)
- return -EBADFD;
- if (res == 0)
- break;
- if (callbacks->data)
- callbacks->data(callbacks->private_data, buf, res);
- if (res < count && lb->mode == SND_PCM_LB_STREAM_MODE_RAW)
- break;
- header.size -= res;
- }
- break;
- case SND_PCM_LB_TYPE_FORMAT:
- {
- snd_pcm_format_t format;
-
- res = read(lb->fd, &format, sizeof(format));
- if (res < 0)
- return -errno;
- result += res;
- if (res != sizeof(format))
- return -EBADFD;
- if (callbacks->format_change)
- callbacks->format_change(callbacks->private_data, &format);
- }
- break;
- case SND_PCM_LB_TYPE_POSITION:
- {
- unsigned int pos;
-
- res = read(lb->fd, &pos, sizeof(pos));
- if (res < 0)
- return -errno;
- result += res;
- if (res != sizeof(pos))
- return -EBADFD;
- if (callbacks->position_change)
- callbacks->position_change(callbacks->private_data, pos);
- }
- break;
- case SND_PCM_LB_TYPE_SILENCE:
- if (callbacks->silence)
- callbacks->silence(callbacks->private_data, header.size);
- break;
- }
- }
- return result;
-}
#ifdef __KERNEL__
#include "../include/driver.h"
+#include "../include/pcm.h"
+#include "../include/pcm_plugin.h"
#else
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
+#include <byteswap.h>
#include <errno.h>
#include <endian.h>
#include <byteswap.h>
ssize_t snd_pcm_format_size(int format, size_t samples)
{
- if (samples < 0)
- return -EINVAL;
- if (samples == 0)
- return samples;
switch (format) {
case SND_PCM_SFMT_S8:
case SND_PCM_SFMT_U8:
}
}
+ssize_t snd_pcm_format_bytes_per_second(snd_pcm_format_t *format)
+{
+ return snd_pcm_format_size(format->format, format->voices * format->rate);
+}
+
const char *snd_pcm_get_format_name(int format)
{
static char *formats[] = {
return formats[format];
}
-unsigned char snd_pcm_format_silence(int format)
+u_int64_t snd_pcm_format_silence_64(int format)
{
switch (format) {
- case SND_PCM_SFMT_IMA_ADPCM: /* special case */
- case SND_PCM_SFMT_MPEG:
- case SND_PCM_SFMT_GSM:
- case SND_PCM_SFMT_MU_LAW:
- case SND_PCM_SFMT_A_LAW:
- return 0;
- 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:
- return 0x80;
case SND_PCM_SFMT_S8:
case SND_PCM_SFMT_S16_LE:
case SND_PCM_SFMT_S16_BE:
case SND_PCM_SFMT_S32_LE:
case SND_PCM_SFMT_S32_BE:
return 0;
- case SND_PCM_SFMT_FLOAT:
- case SND_PCM_SFMT_FLOAT64:
+ case SND_PCM_SFMT_U8:
+ return 0x8080808080808080UL;
+ case SND_PCM_SFMT_U16_LE:
+ case SND_PCM_SFMT_U24_LE:
+ case SND_PCM_SFMT_U32_LE:
+#if defined(LITTLE_ENDIAN)
+ return 0x8000800080008000UL;
+#elif defined(BIG_ENDIAN)
+ return 0x0080008000800080UL;
+#else
+#error "endian"
+#endif
+ case SND_PCM_SFMT_U16_BE:
+ case SND_PCM_SFMT_U24_BE:
+ case SND_PCM_SFMT_U32_BE:
+#if defined(LITTLE_ENDIAN)
+ return 0x0000008000000080UL;
+#elif defined(BIG_ENDIAN)
+ return 0x8000000080000000UL;
+#else
+#error "endian"
+#endif
+ case SND_PCM_SFMT_FLOAT_LE:
+#if defined(LITTLE_ENDIAN)
+ return (float)0.0;
+#elif defined(BIG_ENDIAN)
+ return bswap_32((u_int32_t)((float)0.0));
+#else
+#error "endian"
+#endif
+ case SND_PCM_SFMT_FLOAT64_LE:
+#if defined(LITTLE_ENDIAN)
+ return (double)0.0;
+#elif defined(BIG_ENDIAN)
+ return bswap_64((u_int64_t)((double)0.0));
+#else
+#error "endian"
+#endif
+ case SND_PCM_SFMT_FLOAT_BE:
+#if defined(LITTLE_ENDIAN)
+ return bswap_32((u_int32_t)((float)0.0));
+#elif defined(BIG_ENDIAN)
+ return (float)0.0;
+#else
+#error "endian"
+#endif
+ case SND_PCM_SFMT_FLOAT64_BE:
+#if defined(LITTLE_ENDIAN)
+ return bswap_64((u_int64_t)((double)0.0));
+#elif defined(BIG_ENDIAN)
+ return (double)0.0;
+#else
+#error "endian"
+#endif
case SND_PCM_SFMT_IEC958_SUBFRAME_LE:
case SND_PCM_SFMT_IEC958_SUBFRAME_BE:
return 0;
+ case SND_PCM_SFMT_MU_LAW:
+ return 0x7f7f7f7f7f7f7f7fUL;
+ case SND_PCM_SFMT_A_LAW:
+ return 0x5555555555555555UL;
+ case SND_PCM_SFMT_IMA_ADPCM: /* special case */
+ case SND_PCM_SFMT_MPEG:
+ case SND_PCM_SFMT_GSM:
+ return 0;
}
return 0;
}
+u_int32_t snd_pcm_format_silence_32(int format)
+{
+ return (u_int32_t)snd_pcm_format_silence_64(format);
+}
+
+u_int16_t snd_pcm_format_silence_16(int format)
+{
+ return (u_int16_t)snd_pcm_format_silence_64(format);
+}
+
+u_int8_t snd_pcm_format_silence(int format)
+{
+ return (u_int8_t)snd_pcm_format_silence_64(format);
+}
+
+ssize_t snd_pcm_format_set_silence(int format, void *data, size_t count)
+{
+ size_t count1;
+
+ if (count == 0)
+ return 0;
+ switch (snd_pcm_format_width(format)) {
+ case 4:
+ case 8: {
+ u_int8_t silence = snd_pcm_format_silence_64(format);
+ memset(data, silence, count);
+ break;
+ }
+ case 16: {
+ u_int16_t silence = snd_pcm_format_silence_64(format);
+ if (count % 2)
+ return -EINVAL;
+ count1 = count / 2;
+ while (count1-- > 0)
+ *((u_int16_t *)data)++ = silence;
+ break;
+ }
+ case 32: {
+ u_int32_t silence = snd_pcm_format_silence_64(format);
+ if (count % 4)
+ return -EINVAL;
+ count1 = count / 4;
+ while (count1-- > 0)
+ *((u_int32_t *)data)++ = silence;
+ break;
+ }
+ case 64: {
+ u_int64_t silence = snd_pcm_format_silence_64(format);
+ if (count % 8)
+ return -EINVAL;
+ count1 = count / 8;
+ while (count1-- > 0)
+ *((u_int64_t *)data)++ = silence;
+ }
+ default:
+ return -EINVAL;
+ }
+ return count;
+}
+
static int linear_formats[4*2*2] = {
SND_PCM_SFMT_S8,
SND_PCM_SFMT_U8,
--- /dev/null
+/*
+ * PCM Interface - mmap
+ * 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 <stdio.h>
+#include <malloc.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/poll.h>
+#include <sys/uio.h>
+#include "pcm_local.h"
+
+static void snd_pcm_mmap_clear(snd_pcm_t *pcm, int channel)
+{
+ struct snd_pcm_chan *chan = &pcm->chan[channel];
+ chan->mmap_control->frag_io = 0;
+ chan->mmap_control->frag_data = 0;
+ chan->mmap_control->pos_io = 0;
+ chan->mmap_control->pos_data = 0;
+}
+
+void snd_pcm_mmap_status_change(snd_pcm_t *pcm, int channel, int newstatus)
+{
+ struct snd_pcm_chan *chan = &pcm->chan[channel];
+
+ if (!chan->mmap_control_emulation)
+ return;
+ if (newstatus < 0) {
+ snd_pcm_channel_status_t status;
+ status.channel = channel;
+ if (snd_pcm_channel_status(pcm, &status) < 0)
+ newstatus = SND_PCM_STATUS_NOTREADY;
+ else
+ newstatus = status.status;
+ }
+ if (chan->mmap_control->status != newstatus) {
+ if (newstatus == SND_PCM_STATUS_READY ||
+ (newstatus == SND_PCM_STATUS_PREPARED &&
+ chan->mmap_control->status != SND_PCM_STATUS_READY))
+ snd_pcm_mmap_clear(pcm, channel);
+ chan->mmap_control->status = newstatus;
+ pthread_mutex_lock(&chan->mutex);
+ pthread_cond_signal(&chan->status_cond);
+ pthread_mutex_unlock(&chan->mutex);
+ }
+}
+
+static ssize_t snd_pcm_mmap_playback_frags_used(snd_pcm_t *pcm)
+{
+ struct snd_pcm_chan *chan;
+ ssize_t frags_used;
+ chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK];
+ frags_used = chan->mmap_control->frag_data - chan->mmap_control->frag_io;
+ if (frags_used < (ssize_t)(chan->setup.frags - chan->setup.frag_boundary))
+ frags_used += chan->setup.frag_boundary;
+ return frags_used;
+}
+
+static size_t snd_pcm_mmap_capture_frags_used(snd_pcm_t *pcm)
+{
+ struct snd_pcm_chan *chan;
+ ssize_t frags_used;
+ chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE];
+ frags_used = chan->mmap_control->frag_io - chan->mmap_control->frag_data;
+ if (frags_used < 0)
+ frags_used += chan->setup.frag_boundary;
+ return frags_used;
+}
+
+static size_t snd_pcm_mmap_playback_frags_free(snd_pcm_t *pcm)
+{
+ return pcm->chan[SND_PCM_CHANNEL_PLAYBACK].setup.frags - snd_pcm_mmap_playback_frags_used(pcm);
+}
+
+static ssize_t snd_pcm_mmap_capture_frags_free(snd_pcm_t *pcm)
+{
+ return pcm->chan[SND_PCM_CHANNEL_CAPTURE].setup.frags - snd_pcm_mmap_capture_frags_used(pcm);
+}
+
+int snd_pcm_mmap_frags_used(snd_pcm_t *pcm, int channel, ssize_t *frags)
+{
+ struct snd_pcm_chan *chan;
+ if (!pcm)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
+ return -EINVAL;
+ chan = &pcm->chan[channel];
+ if (!chan->open || !chan->mmap_control)
+ return -EBADFD;
+ if (channel == SND_PCM_CHANNEL_PLAYBACK)
+ *frags = snd_pcm_mmap_playback_frags_used(pcm);
+ else
+ *frags = snd_pcm_mmap_capture_frags_used(pcm);
+ return 0;
+}
+
+int snd_pcm_mmap_frags_free(snd_pcm_t *pcm, int channel, ssize_t *frags)
+{
+ struct snd_pcm_chan *chan;
+ if (!pcm)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
+ return -EINVAL;
+ chan = &pcm->chan[channel];
+ if (!chan->open || !chan->mmap_control)
+ return -EBADFD;
+ if (channel == SND_PCM_CHANNEL_PLAYBACK)
+ *frags = snd_pcm_mmap_playback_frags_free(pcm);
+ else
+ *frags = snd_pcm_mmap_capture_frags_free(pcm);
+ return 0;
+}
+
+static ssize_t snd_pcm_mmap_playback_bytes_used(snd_pcm_t *pcm)
+{
+ struct snd_pcm_chan *chan;
+ ssize_t bytes_used;
+ // snd_pcm_update_pos(pcm, SND_PCM_CHANNEL_PLAYBACK);
+ chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK];
+ bytes_used = chan->mmap_control->pos_data - chan->mmap_control->pos_io;
+ if (bytes_used < (ssize_t)(chan->setup.buffer_size - chan->setup.pos_boundary))
+ bytes_used += chan->setup.pos_boundary;
+ return bytes_used;
+}
+
+static size_t snd_pcm_mmap_capture_bytes_used(snd_pcm_t *pcm)
+{
+ struct snd_pcm_chan *chan;
+ ssize_t bytes_used;
+ // snd_pcm_update_pos(pcm, SND_PCM_CHANNEL_CAPTURE);
+ chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE];
+ bytes_used = chan->mmap_control->pos_io - chan->mmap_control->pos_data;
+ if (bytes_used < 0)
+ bytes_used += chan->setup.pos_boundary;
+ return bytes_used;
+}
+
+int snd_pcm_mmap_bytes_used(snd_pcm_t *pcm, int channel, ssize_t *frags)
+{
+ struct snd_pcm_chan *chan;
+ if (!pcm)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
+ return -EINVAL;
+ chan = &pcm->chan[channel];
+ if (!chan->open || !chan->mmap_control)
+ return -EBADFD;
+ if (channel == SND_PCM_CHANNEL_PLAYBACK)
+ *frags = snd_pcm_mmap_playback_bytes_used(pcm);
+ else
+ *frags = snd_pcm_mmap_capture_bytes_used(pcm);
+ return 0;
+}
+
+static size_t snd_pcm_mmap_playback_bytes_free(snd_pcm_t *pcm)
+{
+ return pcm->chan[SND_PCM_CHANNEL_PLAYBACK].setup.buffer_size - snd_pcm_mmap_playback_bytes_used(pcm);
+}
+
+static ssize_t snd_pcm_mmap_capture_bytes_free(snd_pcm_t *pcm)
+{
+ return pcm->chan[SND_PCM_CHANNEL_CAPTURE].setup.buffer_size - snd_pcm_mmap_capture_bytes_used(pcm);
+}
+
+int snd_pcm_mmap_bytes_free(snd_pcm_t *pcm, int channel, ssize_t *frags)
+{
+ struct snd_pcm_chan *chan;
+ if (!pcm)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
+ return -EINVAL;
+ chan = &pcm->chan[channel];
+ if (!chan->open || !chan->mmap_control)
+ return -EBADFD;
+ if (channel == SND_PCM_CHANNEL_PLAYBACK)
+ *frags = snd_pcm_mmap_playback_bytes_free(pcm);
+ else
+ *frags = snd_pcm_mmap_capture_bytes_free(pcm);
+ return 0;
+}
+
+static int snd_pcm_mmap_playback_ready(snd_pcm_t *pcm)
+{
+ struct snd_pcm_chan *chan;
+ chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK];
+ if (chan->mmap_control->status == SND_PCM_STATUS_XRUN)
+ return -EPIPE;
+ if (chan->setup.mode == SND_PCM_MODE_BLOCK) {
+ return (chan->setup.frags - snd_pcm_mmap_playback_frags_used(pcm)) >= chan->setup.buf.block.frags_min;
+ } else {
+ return (chan->setup.buffer_size - snd_pcm_mmap_playback_bytes_used(pcm)) >= chan->setup.buf.stream.bytes_min;
+ }
+}
+
+static int snd_pcm_mmap_capture_ready(snd_pcm_t *pcm)
+{
+ struct snd_pcm_chan *chan;
+ int ret = 0;
+ chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE];
+ if (chan->mmap_control->status == SND_PCM_STATUS_XRUN) {
+ ret = -EPIPE;
+ if (chan->setup.xrun_mode == SND_PCM_XRUN_DRAIN)
+ return -EPIPE;
+ }
+ if (chan->setup.mode == SND_PCM_MODE_BLOCK) {
+ if (snd_pcm_mmap_capture_frags_used(pcm) >= chan->setup.buf.block.frags_min)
+ return 1;
+ } else {
+ if (snd_pcm_mmap_capture_bytes_used(pcm) >= chan->setup.buf.stream.bytes_min)
+ return 1;
+ }
+ return ret;
+}
+
+int snd_pcm_mmap_ready(snd_pcm_t *pcm, int channel)
+{
+ struct snd_pcm_chan *chan;
+ snd_pcm_mmap_control_t *ctrl;
+ if (!pcm)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
+ return -EINVAL;
+ chan = &pcm->chan[channel];
+ if (!chan->open || !chan->mmap_control)
+ return -EBADFD;
+ ctrl = chan->mmap_control;
+ if (ctrl->status < SND_PCM_STATUS_PREPARED)
+ return -EBADFD;
+ if (channel == SND_PCM_CHANNEL_PLAYBACK) {
+ return snd_pcm_mmap_playback_ready(pcm);
+ } else {
+ return snd_pcm_mmap_capture_ready(pcm);
+ }
+}
+
+/* Bytes transferrable */
+static size_t snd_pcm_mmap_bytes_playback(snd_pcm_t *pcm, size_t size)
+{
+ struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK];
+ snd_pcm_mmap_control_t *ctrl = chan->mmap_control;
+ size_t bytes_cont, bytes_free;
+ unsigned int pos_data = ctrl->pos_data;
+ unsigned int pos_io = ctrl->pos_io;
+ int bytes_used = pos_data - pos_io;
+ if (bytes_used < -(int)(chan->setup.buf.stream.bytes_xrun_max + chan->setup.frag_size))
+ bytes_used += chan->setup.pos_boundary;
+ bytes_cont = chan->setup.buffer_size - (pos_data % chan->setup.buffer_size);
+ if (bytes_cont < size)
+ size = bytes_cont;
+ bytes_free = chan->setup.buffer_size - bytes_used;
+ if (bytes_free < size)
+ size = (bytes_free / chan->setup.buf.stream.bytes_align) * chan->setup.buf.stream.bytes_align;
+ return size;
+}
+
+/* Bytes transferrable */
+static size_t snd_pcm_mmap_bytes_capture(snd_pcm_t *pcm, size_t size)
+{
+ struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE];
+ snd_pcm_mmap_control_t *ctrl = chan->mmap_control;
+ size_t bytes_cont;
+ unsigned int pos_data = ctrl->pos_data;
+ unsigned int pos_io = ctrl->pos_io;
+ int bytes_used = pos_io - pos_data;
+ if (bytes_used < 0)
+ bytes_used += chan->setup.pos_boundary;
+ bytes_cont = chan->setup.buffer_size - (pos_data % chan->setup.buffer_size);
+ if (bytes_cont < size)
+ size = bytes_cont;
+ if ((size_t) bytes_used < size)
+ size = (bytes_used / chan->setup.buf.stream.bytes_align) * chan->setup.buf.stream.bytes_align;
+ return size;
+}
+
+typedef int (*transfer_f)(snd_pcm_t *pcm, size_t hwoff, void *data, size_t off, size_t size);
+
+
+static ssize_t snd_pcm_mmap_write1(snd_pcm_t *pcm, const void *data, size_t count, transfer_f transfer)
+{
+ struct snd_pcm_chan *chan;
+ snd_pcm_mmap_control_t *ctrl;
+ size_t frag_size;
+ size_t offset = 0;
+ size_t result = 0;
+ int err;
+
+ chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK];
+ ctrl = chan->mmap_control;
+ if (ctrl->status < SND_PCM_STATUS_PREPARED)
+ return -EBADFD;
+ frag_size = chan->setup.frag_size;
+ if (chan->setup.mode == SND_PCM_MODE_BLOCK) {
+ if (count % frag_size != 0)
+ return -EINVAL;
+ } else {
+ int tmp = snd_pcm_format_size(chan->setup.format.format, chan->setup.format.voices);
+ if (tmp > 0) {
+ int r = count % tmp;
+ if (r > 0) {
+ count -= r;
+ if (count == 0)
+ return -EINVAL;
+ }
+ }
+ }
+ while (count > 0) {
+ size_t bytes;
+ int ready = snd_pcm_mmap_playback_ready(pcm);
+ if (ready < 0)
+ return ready;
+ if (!ready) {
+ struct pollfd pfd;
+ if (ctrl->status != SND_PCM_STATUS_RUNNING)
+ return result > 0 ? result : -EPIPE;
+ if (chan->mode & SND_PCM_NONBLOCK)
+ return result > 0 ? result : -EAGAIN;
+ pfd.fd = snd_pcm_file_descriptor(pcm, SND_PCM_CHANNEL_PLAYBACK);
+ pfd.events = POLLOUT | POLLERR;
+ ready = poll(&pfd, 1, 10000);
+ if (ready < 0)
+ return result > 0 ? result : ready;
+ if (ready && pfd.revents & POLLERR)
+ return result > 0 ? result : -EPIPE;
+ assert(snd_pcm_mmap_playback_ready(pcm));
+ }
+ if (chan->setup.mode == SND_PCM_MODE_BLOCK) {
+ size_t frag_data, frag;
+ frag_data = ctrl->frag_data;
+ frag = frag_data % chan->setup.frags;
+ err = transfer(pcm, frag_size * frag, (void *) data, offset, frag_size);
+ if (err < 0)
+ return result > 0 ? result : err;
+ if (ctrl->status == SND_PCM_STATUS_XRUN)
+ return result > 0 ? result : -EPIPE;
+ frag_data++;
+ if (frag_data == chan->setup.frag_boundary) {
+ ctrl->frag_data = 0;
+ ctrl->pos_data = 0;
+ } else {
+ ctrl->frag_data = frag_data;
+ ctrl->pos_data += frag_size;
+ }
+ bytes = frag_size;
+ } else {
+ size_t pos_data;
+ bytes = snd_pcm_mmap_bytes_playback(pcm, count);
+ pos_data = ctrl->pos_data;
+ err = transfer(pcm, pos_data % chan->setup.buffer_size, (void *) data, offset, bytes);
+ if (err < 0)
+ return result > 0 ? result : err;
+ if (ctrl->status == SND_PCM_STATUS_XRUN)
+ return result > 0 ? result : -EPIPE;
+ pos_data += bytes;
+ if (pos_data == chan->setup.pos_boundary) {
+ ctrl->pos_data = 0;
+ ctrl->frag_data = 0;
+ } else {
+ ctrl->pos_data = pos_data;
+ ctrl->frag_data = pos_data / chan->setup.frags;
+ }
+ }
+ offset += bytes;
+ count -= bytes;
+ result += bytes;
+ }
+
+ if (ctrl->status == SND_PCM_STATUS_PREPARED &&
+ (chan->setup.start_mode == SND_PCM_START_DATA ||
+ (chan->setup.start_mode == SND_PCM_START_FULL &&
+ !snd_pcm_mmap_playback_ready(pcm)))) {
+ err = snd_pcm_channel_go(pcm, SND_PCM_CHANNEL_PLAYBACK);
+ if (err < 0)
+ return result > 0 ? result : err;
+ }
+ return result;
+}
+
+static int transfer_write(snd_pcm_t *pcm, size_t hwoff, void* data, size_t off, size_t size)
+{
+ struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK];
+ const char *buf = data;
+ unsigned int v, voices;
+#define COPY_LABELS
+#include "plugin/plugin_ops.h"
+#undef COPY_LABELS
+ void *copy;
+ snd_pcm_voice_setup_t *vsetup;
+ int idx;
+ size_t vsize, ssize;
+ idx = copy_index(chan->setup.format.format);
+ if (idx < 0)
+ return idx;
+ copy = copy_labels[idx];
+ voices = chan->setup.format.voices;
+ vsetup = chan->voices_setup;
+ vsize = snd_pcm_format_size(chan->setup.format.format, 1);
+ ssize = vsize * chan->setup.format.voices;
+ hwoff /= ssize;
+ size /= ssize;
+ for (v = 0; v < voices; ++v, ++vsetup) {
+ const char *src;
+ char *dst;
+ size_t dst_step;
+ size_t samples = size;
+ if (vsetup->first % 8 != 0 ||
+ vsetup->step % 8 != 0)
+ return -EINVAL;
+ src = buf + off + v * vsize;
+ dst_step = vsetup->step / 8;
+ dst = chan->mmap_data + vsetup->addr + (vsetup->first + vsetup->step * hwoff) / 8;
+ while (samples-- > 0) {
+ goto *copy;
+#define COPY_END after
+#include "plugin/plugin_ops.h"
+#undef COPY_END
+ after:
+ src += ssize;
+ dst += dst_step;
+ }
+ }
+ return 0;
+}
+
+ssize_t snd_pcm_mmap_write(snd_pcm_t *pcm, const void *buffer, size_t count)
+{
+ struct snd_pcm_chan *chan;
+ if (!pcm)
+ return -EFAULT;
+ chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK];
+ if (!chan->open || !chan->valid_setup || !chan->valid_voices_setup)
+ return -EBADFD;
+ if (!chan->mmap_data || !chan->mmap_control)
+ return -EBADFD;
+ if (count > 0 && !buffer)
+ return -EFAULT;
+ if (!chan->setup.format.interleave && chan->setup.format.voices > 1)
+ return -EINVAL;
+ return snd_pcm_mmap_write1(pcm, buffer, count, transfer_write);
+}
+
+static int transfer_writev(snd_pcm_t *pcm, size_t hwoff, void* data, size_t off, size_t size)
+{
+ struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK];
+ struct iovec *vec = data;
+ unsigned int v, voices;
+#define COPY_LABELS
+#include "plugin/plugin_ops.h"
+#undef COPY_LABELS
+ void *copy;
+ snd_pcm_voice_setup_t *vsetup;
+ int idx;
+ size_t ssize;
+ idx = copy_index(chan->setup.format.format);
+ if (idx < 0)
+ return idx;
+ copy = copy_labels[idx];
+ voices = chan->setup.format.voices;
+ vsetup = chan->voices_setup;
+ ssize = snd_pcm_format_size(chan->setup.format.format, chan->setup.format.voices);
+ hwoff /= ssize;
+ size /= ssize;
+ off /= voices;
+ for (v = 0; v < voices; ++v, ++vsetup, ++vec) {
+ const char *src;
+ char *dst;
+ size_t dst_step;
+ size_t samples = size;
+ if (vsetup->first % 8 != 0 ||
+ vsetup->step % 8 != 0)
+ return -EINVAL;
+ src = vec->iov_base + off;
+ dst_step = vsetup->step / 8;
+ dst = chan->mmap_data + vsetup->addr + (vsetup->first + vsetup->step * hwoff) / 8;
+ while (samples-- > 0) {
+ goto *copy;
+#define COPY_END after
+#include "plugin/plugin_ops.h"
+#undef COPY_END
+ after:
+ src += ssize;
+ dst += dst_step;
+ }
+ }
+ return 0;
+}
+
+ssize_t snd_pcm_mmap_writev(snd_pcm_t *pcm, const struct iovec *vector, unsigned long vcount)
+{
+ struct snd_pcm_chan *chan;
+ size_t result = 0;
+ unsigned int b;
+ if (!pcm)
+ return -EFAULT;
+ chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK];
+ if (!chan->open || !chan->valid_setup || !chan->valid_voices_setup)
+ return -EBADFD;
+ if (!chan->mmap_data || !chan->mmap_control)
+ return -EBADFD;
+ if (vcount > 0 && !vector)
+ return -EFAULT;
+ if (chan->setup.format.interleave) {
+ for (b = 0; b < vcount; b++) {
+ int ret;
+ ret = snd_pcm_mmap_write1(pcm, vector[b].iov_base, vector[b].iov_len, transfer_write);
+ if (ret < 0)
+ return result > 0 ? result : ret;
+ result += ret;
+ }
+ } else {
+ unsigned int voices = chan->setup.format.voices;
+ unsigned long bcount;
+ if (vcount % voices)
+ return -EINVAL;
+ bcount = vcount / voices;
+ for (b = 0; b < bcount; b++) {
+ unsigned int v;
+ int ret;
+ size_t count = 0;
+ count = vector[0].iov_len;
+ for (v = 0; v < voices; ++v) {
+ if (vector[v].iov_len != count)
+ return -EINVAL;
+ }
+ ret = snd_pcm_mmap_write1(pcm, vector, count * voices, transfer_writev);
+ if (ret < 0)
+ return result > 0 ? result : ret;
+ result += ret;
+ if ((size_t)ret != count * voices)
+ break;
+ vector += voices;
+ }
+ }
+ return result;
+}
+
+static ssize_t snd_pcm_mmap_read1(snd_pcm_t *pcm, void *data, size_t count, transfer_f transfer)
+{
+ struct snd_pcm_chan *chan;
+ snd_pcm_mmap_control_t *ctrl;
+ size_t frag_size;
+ size_t offset = 0;
+ size_t result = 0;
+ int err;
+
+ chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE];
+ ctrl = chan->mmap_control;
+ if (ctrl->status < SND_PCM_STATUS_PREPARED)
+ return -EBADFD;
+ frag_size = chan->setup.frag_size;
+ if (chan->setup.mode == SND_PCM_MODE_BLOCK) {
+ if (count % frag_size != 0)
+ return -EINVAL;
+ } else {
+ int tmp = snd_pcm_format_size(chan->setup.format.format, chan->setup.format.voices);
+ if (tmp > 0) {
+ int r = count % tmp;
+ if (r > 0) {
+ count -= r;
+ if (count == 0)
+ return -EINVAL;
+ }
+ }
+ }
+ if (ctrl->status == SND_PCM_STATUS_PREPARED &&
+ chan->setup.start_mode == SND_PCM_START_DATA) {
+ err = snd_pcm_channel_go(pcm, SND_PCM_CHANNEL_CAPTURE);
+ if (err < 0)
+ return err;
+ }
+ while (count > 0) {
+ size_t bytes;
+ int ready = snd_pcm_mmap_capture_ready(pcm);
+ if (ready < 0)
+ return ready;
+ if (!ready) {
+ struct pollfd pfd;
+ if (ctrl->status != SND_PCM_STATUS_RUNNING)
+ return result > 0 ? result : -EPIPE;
+ if (chan->mode & SND_PCM_NONBLOCK)
+ return result > 0 ? result : -EAGAIN;
+ pfd.fd = snd_pcm_file_descriptor(pcm, SND_PCM_CHANNEL_CAPTURE);
+ pfd.events = POLLIN | POLLERR;
+ ready = poll(&pfd, 1, 10000);
+ if (ready < 0)
+ return result > 0 ? result : ready;
+ if (ready && pfd.revents & POLLERR)
+ return result > 0 ? result : -EPIPE;
+ assert(snd_pcm_mmap_capture_ready(pcm));
+ }
+ if (chan->setup.mode == SND_PCM_MODE_BLOCK) {
+ size_t frag_data, frag;
+ frag_data = ctrl->frag_data;
+ frag = frag_data % chan->setup.frags;
+ err = transfer(pcm, frag_size * frag, data, offset, frag_size);
+ if (err < 0)
+ return result > 0 ? result : err;
+ if (ctrl->status == SND_PCM_STATUS_XRUN &&
+ chan->setup.xrun_mode == SND_PCM_XRUN_DRAIN)
+ return result > 0 ? result : -EPIPE;
+ frag_data++;
+ if (frag_data == chan->setup.frag_boundary) {
+ ctrl->frag_data = 0;
+ ctrl->pos_data = 0;
+ } else {
+ ctrl->frag_data = frag_data;
+ ctrl->pos_data += frag_size;
+ }
+ bytes = frag_size;
+ } else {
+ size_t pos_data;
+ bytes = snd_pcm_mmap_bytes_capture(pcm, count);
+ pos_data = ctrl->pos_data;
+ err = transfer(pcm, pos_data % chan->setup.buffer_size, data, offset, bytes);
+ if (err < 0)
+ return result > 0 ? result : err;
+ if (ctrl->status == SND_PCM_STATUS_XRUN &&
+ chan->setup.xrun_mode == SND_PCM_XRUN_DRAIN)
+ return result > 0 ? result : -EPIPE;
+ pos_data += bytes;
+ if (pos_data == chan->setup.pos_boundary) {
+ ctrl->pos_data = 0;
+ ctrl->frag_data = 0;
+ } else {
+ ctrl->pos_data = pos_data;
+ ctrl->frag_data = pos_data / chan->setup.frags;
+ }
+ }
+ offset += bytes;
+ count -= bytes;
+ result += bytes;
+ }
+
+ return result;
+}
+
+static int transfer_read(snd_pcm_t *pcm, size_t hwoff, void* data, size_t off, size_t size)
+{
+ struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE];
+ char *buf = data;
+ unsigned int v, voices;
+#define COPY_LABELS
+#include "plugin/plugin_ops.h"
+#undef COPY_LABELS
+ void *copy;
+ snd_pcm_voice_setup_t *vsetup;
+ int idx;
+ size_t vsize, ssize;
+ idx = copy_index(chan->setup.format.format);
+ if (idx < 0)
+ return idx;
+ copy = copy_labels[idx];
+ voices = chan->setup.format.voices;
+ vsetup = chan->voices_setup;
+ vsize = snd_pcm_format_size(chan->setup.format.format, 1);
+ ssize = vsize * chan->setup.format.voices;
+ hwoff /= ssize;
+ size /= ssize;
+ for (v = 0; v < voices; ++v, ++vsetup) {
+ const char *src;
+ size_t src_step;
+ char *dst;
+ size_t samples = size;
+ if (vsetup->first % 8 != 0 ||
+ vsetup->step % 8 != 0)
+ return -EINVAL;
+ src_step = vsetup->step / 8;
+ src = chan->mmap_data + vsetup->addr + (vsetup->first + vsetup->step * hwoff) / 8;
+ dst = buf + off + v * vsize;
+ while (samples-- > 0) {
+ goto *copy;
+#define COPY_END after
+#include "plugin/plugin_ops.h"
+#undef COPY_END
+ after:
+ src += src_step;
+ dst += ssize;
+ }
+ }
+ return 0;
+}
+
+ssize_t snd_pcm_mmap_read(snd_pcm_t *pcm, void *buffer, size_t count)
+{
+ struct snd_pcm_chan *chan;
+ if (!pcm)
+ return -EFAULT;
+ chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE];
+ if (!chan->open || !chan->valid_setup || !chan->valid_voices_setup)
+ return -EBADFD;
+ if (!chan->mmap_data || !chan->mmap_control)
+ return -EBADFD;
+ if (count > 0 && !buffer)
+ return -EFAULT;
+ if (!chan->setup.format.interleave && chan->setup.format.voices > 1)
+ return -EINVAL;
+ return snd_pcm_mmap_read1(pcm, buffer, count, transfer_read);
+}
+
+static int transfer_readv(snd_pcm_t *pcm, size_t hwoff, void* data, size_t off, size_t size)
+{
+ struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE];
+ struct iovec *vec = data;
+ unsigned int v, voices;
+#define COPY_LABELS
+#include "plugin/plugin_ops.h"
+#undef COPY_LABELS
+ void *copy;
+ snd_pcm_voice_setup_t *vsetup;
+ int idx;
+ size_t ssize;
+ idx = copy_index(chan->setup.format.format);
+ if (idx < 0)
+ return idx;
+ copy = copy_labels[idx];
+ voices = chan->setup.format.voices;
+ vsetup = chan->voices_setup;
+ ssize = snd_pcm_format_size(chan->setup.format.format, chan->setup.format.voices);
+ hwoff /= ssize;
+ size /= ssize;
+ off /= voices;
+ for (v = 0; v < voices; ++v, ++vsetup, ++vec) {
+ const char *src;
+ size_t src_step;
+ char *dst;
+ size_t samples = size;
+ if (vsetup->first % 8 != 0 ||
+ vsetup->step % 8 != 0)
+ return -EINVAL;
+ src_step = vsetup->step / 8;
+ src = chan->mmap_data + vsetup->addr + (vsetup->first + vsetup->step * hwoff) / 8;
+ dst = vec->iov_base + off;
+ while (samples-- > 0) {
+ goto *copy;
+#define COPY_END after
+#include "plugin/plugin_ops.h"
+#undef COPY_END
+ after:
+ src += src_step;
+ dst += ssize;
+ }
+ }
+ return 0;
+}
+
+ssize_t snd_pcm_mmap_readv(snd_pcm_t *pcm, const struct iovec *vector, unsigned long vcount)
+{
+ struct snd_pcm_chan *chan;
+ size_t result = 0;
+ unsigned int b;
+ if (!pcm)
+ return -EFAULT;
+ chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE];
+ if (!chan->open || !chan->valid_setup || !chan->valid_voices_setup)
+ return -EBADFD;
+ if (!chan->mmap_data || !chan->mmap_control)
+ return -EBADFD;
+ if (vcount > 0 && !vector)
+ return -EFAULT;
+ if (chan->setup.format.interleave) {
+ for (b = 0; b < vcount; b++) {
+ int ret;
+ ret = snd_pcm_mmap_read1(pcm, vector[b].iov_base, vector[b].iov_len, transfer_write);
+ if (ret < 0)
+ return result > 0 ? result : ret;
+ result += ret;
+ }
+ } else {
+ unsigned int voices = chan->setup.format.voices;
+ unsigned long bcount;
+ if (vcount % voices)
+ return -EINVAL;
+ bcount = vcount / voices;
+ for (b = 0; b < bcount; b++) {
+ unsigned int v;
+ int ret;
+ size_t count = 0;
+ count = vector[0].iov_len;
+ for (v = 0; v < voices; ++v) {
+ if (vector[v].iov_len != count)
+ return -EINVAL;
+ }
+ ret = snd_pcm_mmap_read1(pcm, (void *) vector, count * voices, transfer_readv);
+ if (ret < 0)
+ return result > 0 ? result : ret;
+ result += ret;
+ if ((size_t)ret != count * voices)
+ break;
+ vector += voices;
+ }
+ }
+ return result;
+}
+
+static void *playback_mmap(void *d)
+{
+ snd_pcm_t *pcm = d;
+ struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK];
+ snd_pcm_mmap_control_t *control;
+ char *data;
+ int frags;
+ int frag_size, voice_size, voice_frag_size;
+ int voices;
+ control = chan->mmap_control;
+ data = chan->mmap_data;
+ frags = chan->setup.frags;
+ frag_size = chan->setup.frag_size;
+ voices = chan->setup.format.voices;
+ voice_size = chan->mmap_data_size / voices;
+ voice_frag_size = voice_size / frags;
+ while (1) {
+ int err;
+ struct pollfd pfd;
+ unsigned int f, frag;
+ if (chan->mmap_thread_stop)
+ break;
+
+ pthread_mutex_lock(&chan->mutex);
+ if (control->status != SND_PCM_STATUS_RUNNING) {
+ pthread_cond_wait(&chan->status_cond, &chan->mutex);
+ pthread_mutex_unlock(&chan->mutex);
+ continue;
+ }
+ pthread_mutex_unlock(&chan->mutex);
+
+ pfd.fd = snd_pcm_file_descriptor(pcm, SND_PCM_CHANNEL_PLAYBACK);
+ pfd.events = POLLOUT | POLLERR;
+ err = poll(&pfd, 1, -1);
+ if (err < 0) {
+ fprintf(stderr, "poll err=%d\n", err);
+ continue;
+ }
+ if (pfd.revents & POLLERR) {
+ snd_pcm_mmap_status_change(pcm, SND_PCM_CHANNEL_PLAYBACK, -1);
+ fprintf(stderr, "pollerr %d\n", control->status);
+ continue;
+ }
+
+ frag = control->frag_io;
+ if (snd_pcm_mmap_playback_frags_used(pcm) <= 0) {
+ fprintf(stderr, "underrun\n");
+ usleep(10000);
+ continue;
+ }
+ f = frag % frags;
+ if (chan->setup.format.interleave) {
+ err = snd_pcm_write(pcm, data + f * frag_size, frag_size);
+ } else {
+ struct iovec vector[voices];
+ struct iovec *v = vector;
+ int voice;
+ for (voice = 0; voice < voices; ++voice) {
+ v->iov_base = data + voice_size * voice + f * voice_frag_size;
+ v->iov_len = voice_frag_size;
+ v++;
+ }
+ err = snd_pcm_writev(pcm, vector, voice_frag_size);
+ }
+ if (err <= 0) {
+ fprintf(stderr, "write err=%d\n", err);
+ snd_pcm_mmap_status_change(pcm, SND_PCM_CHANNEL_PLAYBACK, -1);
+ continue;
+ }
+ pthread_mutex_lock(&chan->mutex);
+ pthread_cond_signal(&chan->ready_cond);
+ pthread_mutex_unlock(&chan->mutex);
+ frag++;
+ if (frag == chan->setup.frag_boundary)
+ frag = 0;
+ control->frag_io = frag;
+ }
+ return 0;
+}
+
+static void *capture_mmap(void *d)
+{
+ snd_pcm_t *pcm = d;
+ struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE];
+ snd_pcm_mmap_control_t *control;
+ char *data;
+ int frags;
+ int frag_size, voice_size, voice_frag_size;
+ int voices;
+ control = chan->mmap_control;
+ data = chan->mmap_data;
+ frags = chan->setup.frags;
+ frag_size = chan->setup.frag_size;
+ voices = chan->setup.format.voices;
+ voice_size = chan->mmap_data_size / voices;
+ voice_frag_size = voice_size / frags;
+ while (1) {
+ int err;
+ struct pollfd pfd;
+ unsigned int f, frag;
+ if (chan->mmap_thread_stop)
+ break;
+
+ pthread_mutex_lock(&chan->mutex);
+ if (control->status != SND_PCM_STATUS_RUNNING) {
+ pthread_cond_wait(&chan->status_cond, &chan->mutex);
+ pthread_mutex_unlock(&chan->mutex);
+ continue;
+ }
+ pthread_mutex_unlock(&chan->mutex);
+
+ pfd.fd = snd_pcm_file_descriptor(pcm, SND_PCM_CHANNEL_CAPTURE);
+ pfd.events = POLLIN | POLLERR;
+ err = poll(&pfd, 1, -1);
+ if (err < 0) {
+ fprintf(stderr, "poll err=%d\n", err);
+ continue;
+ }
+ if (pfd.revents & POLLERR) {
+ snd_pcm_mmap_status_change(pcm, SND_PCM_CHANNEL_CAPTURE, -1);
+ fprintf(stderr, "pollerr %d\n", control->status);
+ continue;
+ }
+
+ frag = control->frag_io;
+ if (snd_pcm_mmap_capture_frags_free(pcm) <= 0) {
+ fprintf(stderr, "overrun\n");
+ usleep(10000);
+ continue;
+ }
+ f = frag % frags;
+ if (chan->setup.format.interleave) {
+ err = snd_pcm_read(pcm, data + f * frag_size, frag_size);
+ } else {
+ struct iovec vector[voices];
+ struct iovec *v = vector;
+ int voice;
+ for (voice = 0; voice < voices; ++voice) {
+ v->iov_base = data + voice_size * voice + f * voice_frag_size;
+ v->iov_len = voice_frag_size;
+ v++;
+ }
+ err = snd_pcm_readv(pcm, vector, voice_frag_size);
+ }
+ if (err < 0) {
+ fprintf(stderr, "read err=%d\n", err);
+ snd_pcm_mmap_status_change(pcm, SND_PCM_CHANNEL_CAPTURE, -1);
+ continue;
+ }
+ frag++;
+ if (frag == chan->setup.frag_boundary)
+ frag = 0;
+ control->frag_io = frag;
+ }
+ return 0;
+}
+
+int snd_pcm_mmap_control(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t **control)
+{
+ struct snd_pcm_chan *chan;
+ snd_pcm_channel_info_t info;
+ size_t csize;
+ int err;
+ if (!pcm || !control)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
+ return -EINVAL;
+ chan = &pcm->chan[channel];
+ if (!chan->open)
+ return -EBADFD;
+ if (chan->mmap_control) {
+ *control = chan->mmap_control;
+ return 0;
+ }
+ if (!chan->valid_setup)
+ return -EBADFD;
+ csize = sizeof(snd_pcm_mmap_control_t);
+
+ info.channel = channel;
+ err = snd_pcm_channel_info(pcm, &info);
+ if (err < 0)
+ return err;
+ if (info.flags & SND_PCM_CHNINFO_MMAP) {
+ if ((err = pcm->ops->mmap_control(pcm, channel, control, csize)) < 0)
+ return err;
+ } else {
+ *control = calloc(1, csize);
+ chan->mmap_control_emulation = 1;
+ }
+ chan->mmap_control = *control;
+ chan->mmap_control_size = csize;
+ return 0;
+}
+
+int snd_pcm_mmap_data(snd_pcm_t *pcm, int channel, void **data)
+{
+ struct snd_pcm_chan *chan;
+ snd_pcm_channel_info_t info;
+ size_t bsize;
+ int err;
+ if (!pcm || !data)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
+ return -EINVAL;
+ chan = &pcm->chan[channel];
+ if (!chan->open)
+ return -EBADFD;
+ if (chan->mmap_data) {
+ *data = chan->mmap_data;
+ return 0;
+ }
+ if (!chan->valid_setup)
+ return -EBADFD;
+
+ info.channel = channel;
+ err = snd_pcm_channel_info(pcm, &info);
+ if (err < 0)
+ return err;
+ bsize = info.mmap_size;
+ if (info.flags & SND_PCM_CHNINFO_MMAP) {
+ if ((err = pcm->ops->mmap_data(pcm, channel, data, bsize)) < 0)
+ return err;
+ } else {
+ *data = calloc(1, bsize);
+
+ pthread_mutex_init(&chan->mutex, NULL);
+ pthread_cond_init(&chan->status_cond, NULL);
+ pthread_cond_init(&chan->ready_cond, NULL);
+ chan->mmap_thread_stop = 0;
+ if (channel == SND_PCM_CHANNEL_PLAYBACK)
+ err = pthread_create(&chan->mmap_thread, NULL, playback_mmap, pcm);
+ else
+ err = pthread_create(&chan->mmap_thread, NULL, capture_mmap, pcm);
+ if (err < 0) {
+ pthread_cond_destroy(&chan->status_cond);
+ pthread_cond_destroy(&chan->ready_cond);
+ pthread_mutex_destroy(&chan->mutex);
+ free(*data);
+ *data = 0;
+ return err;
+ }
+ chan->mmap_data_emulation = 1;
+ }
+ chan->mmap_data = *data;
+ chan->mmap_data_size = bsize;
+ snd_pcm_all_voices_setup(pcm, channel, NULL);
+ return 0;
+}
+
+int snd_pcm_mmap(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t **control, void **data)
+{
+ int err;
+ err = snd_pcm_mmap_control(pcm, channel, control);
+ if (err < 0)
+ return err;
+ err = snd_pcm_mmap_data(pcm, channel, data);
+ if (err < 0) {
+ snd_pcm_munmap_control(pcm, channel);
+ return err;
+ }
+ return 0;
+}
+
+int snd_pcm_munmap_control(snd_pcm_t *pcm, int channel)
+{
+ int err;
+ struct snd_pcm_chan *chan;
+ if (!pcm)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
+ return -EINVAL;
+ chan = &pcm->chan[channel];
+ if (!chan->open)
+ return -EBADFD;
+ if (!chan->mmap_control)
+ return -EINVAL;
+ if (chan->mmap_control_emulation) {
+ free(chan->mmap_control);
+ chan->mmap_control_emulation = 0;
+ } else {
+ if ((err = pcm->ops->munmap_control(pcm, channel, chan->mmap_control, chan->mmap_control_size)) < 0)
+ return err;
+ }
+ chan->mmap_control = 0;
+ chan->mmap_control_size = 0;
+ return 0;
+}
+
+int snd_pcm_munmap_data(snd_pcm_t *pcm, int channel)
+{
+ int err;
+ struct snd_pcm_chan *chan;
+ if (!pcm)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
+ return -EINVAL;
+ chan = &pcm->chan[channel];
+ if (!chan->open)
+ return -EBADFD;
+ if (!chan->mmap_data)
+ return -EINVAL;
+ if (chan->mmap_data_emulation) {
+ chan->mmap_thread_stop = 1;
+ pthread_mutex_lock(&chan->mutex);
+ pthread_cond_signal(&chan->status_cond);
+ pthread_mutex_unlock(&chan->mutex);
+ pthread_join(chan->mmap_thread, NULL);
+ pthread_cond_destroy(&chan->status_cond);
+ pthread_cond_destroy(&chan->ready_cond);
+ pthread_mutex_destroy(&chan->mutex);
+ free(chan->mmap_data);
+ chan->mmap_data_emulation = 0;
+ } else {
+ if ((err = pcm->ops->munmap_data(pcm, channel, chan->mmap_data, chan->mmap_data_size)) < 0)
+ return err;
+ }
+ chan->mmap_data = 0;
+ chan->mmap_data_size = 0;
+ return 0;
+}
+
+int snd_pcm_munmap(snd_pcm_t *pcm, int channel)
+{
+ int err;
+ err = snd_pcm_munmap_control(pcm, channel);
+ if (err < 0)
+ return err;
+ return snd_pcm_munmap_data(pcm, channel);
+}
+
--- /dev/null
+/*
+ * PCM - Plug
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+#include <sys/uio.h>
+#include "pcm_local.h"
+
+/* snd_pcm_plug helpers */
+
+void *snd_pcm_plug_buf_alloc(snd_pcm_t *pcm, int channel, size_t size)
+{
+ int idx;
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ struct snd_pcm_plug_chan *plugchan = &plug->chan[channel];
+
+ for (idx = 0; idx < 2; idx++) {
+ if (plugchan->alloc_lock[idx])
+ continue;
+ if (plugchan->alloc_size[idx] >= size) {
+ plugchan->alloc_lock[idx] = 1;
+ return plugchan->alloc_ptr[idx];
+ }
+ }
+ for (idx = 0; idx < 2; idx++) {
+ if (plugchan->alloc_lock[idx])
+ continue;
+ if (plugchan->alloc_ptr[idx] != NULL)
+ free(plugchan->alloc_ptr[idx]);
+ plugchan->alloc_ptr[idx] = malloc(size);
+ if (plugchan->alloc_ptr[idx] == NULL)
+ return NULL;
+ plugchan->alloc_size[idx] = size;
+ plugchan->alloc_lock[idx] = 1;
+ return plugchan->alloc_ptr[idx];
+ }
+ return NULL;
+}
+
+void snd_pcm_plug_buf_unlock(snd_pcm_t *pcm, int channel, void *ptr)
+{
+ int idx;
+
+ snd_pcm_plug_t *plug;
+ struct snd_pcm_plug_chan *plugchan;
+
+ if (!ptr)
+ return;
+ plug = (snd_pcm_plug_t*) &pcm->private;
+ plugchan = &plug->chan[channel];
+
+ for (idx = 0; idx < 2; idx++) {
+ if (plugchan->alloc_ptr[idx] == ptr) {
+ plugchan->alloc_lock[idx] = 0;
+ return;
+ }
+ }
+}
+
+/* snd_pcm_plugin externs */
+
+int snd_pcm_plugin_insert(snd_pcm_plugin_t *plugin)
+{
+ snd_pcm_plug_t *plug;
+ struct snd_pcm_plug_chan *plugchan;
+ snd_pcm_t *pcm;
+ if (!plugin)
+ return -EFAULT;
+ pcm = plugin->handle;
+ plug = (snd_pcm_plug_t*) &pcm->private;
+ plugchan = &plug->chan[plugin->channel];
+ plugin->next = plugchan->first;
+ plugin->prev = NULL;
+ if (plugchan->first) {
+ plugchan->first->prev = plugin;
+ plugchan->first = plugin;
+ } else {
+ plugchan->last =
+ plugchan->first = plugin;
+ }
+ return 0;
+}
+
+int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin)
+{
+ snd_pcm_plug_t *plug;
+ struct snd_pcm_plug_chan *plugchan;
+ snd_pcm_t *pcm;
+ if (!plugin)
+ return -EFAULT;
+ pcm = plugin->handle;
+ plug = (snd_pcm_plug_t*) &pcm->private;
+ plugchan = &plug->chan[plugin->channel];
+
+ plugin->next = NULL;
+ plugin->prev = plugchan->last;
+ if (plugchan->last) {
+ plugchan->last->next = plugin;
+ plugchan->last = plugin;
+ } else {
+ plugchan->last =
+ plugchan->first = plugin;
+ }
+ return 0;
+}
+
+#if 0
+int snd_pcm_plugin_remove_to(snd_pcm_plugin_t *plugin)
+{
+ snd_pcm_plugin_t *plugin1, *plugin1_prev;
+ snd_pcm_plug_t *plug;
+ snd_pcm_t *pcm;
+ struct snd_pcm_plug_chan *plugchan;
+ if (!plugin)
+ return -EFAULT;
+ pcm = plugin->handle;
+
+ plug = (snd_pcm_plug_t*) &pcm->private;
+ plugchan = &plug->chan[plugin->channel];
+
+ plugin1 = plugin;
+ while (plugin1->prev)
+ plugin1 = plugin1->prev;
+ if (plugchan->first != plugin1)
+ return -EINVAL;
+ plugchan->first = plugin;
+ plugin1 = plugin->prev;
+ plugin->prev = NULL;
+ while (plugin1) {
+ plugin1_prev = plugin1->prev;
+ snd_pcm_plugin_free(plugin1);
+ plugin1 = plugin1_prev;
+ }
+ return 0;
+}
+
+int snd_pcm_plug_remove_first(snd_pcm_t *pcm, int channel)
+{
+ snd_pcm_plugin_t *plugin;
+ snd_pcm_plug_t *plug;
+ struct snd_pcm_plug_chan *plugchan;
+ if (!pcm)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
+ return -EINVAL;
+ if (!pcm->chan[channel].open)
+ return -EBADFD;
+
+ plug = (snd_pcm_plug_t*) &pcm->private;
+ plugchan = &plug->chan[channel];
+
+ plugin = plugchan->first;
+ if (plugin->next) {
+ plugin = plugin->next;
+ } else {
+ return snd_pcm_plug_clear(pcm, channel);
+ }
+ return snd_pcm_plugin_remove_to(plugin);
+}
+#endif
+
+/* snd_pcm_plug externs */
+
+int snd_pcm_plug_clear(snd_pcm_t *pcm, int channel)
+{
+ snd_pcm_plugin_t *plugin, *plugin_next;
+ snd_pcm_plug_t *plug;
+ struct snd_pcm_plug_chan *plugchan;
+ int idx;
+
+ if (!pcm)
+ return -EINVAL;
+ if (channel < 0 || channel > 1)
+ return -EINVAL;
+ if (!pcm->chan[channel].open)
+ return -EBADFD;
+
+ plug = (snd_pcm_plug_t*) &pcm->private;
+ plugchan = &plug->chan[channel];
+ plugin = plugchan->first;
+ plugchan->first = NULL;
+ plugchan->last = NULL;
+ while (plugin) {
+ plugin_next = plugin->next;
+ snd_pcm_plugin_free(plugin);
+ plugin = plugin_next;
+ }
+ for (idx = 0; idx < 2; idx++) {
+ if (plugchan->alloc_ptr[idx]) {
+ free(plugchan->alloc_ptr[idx]);
+ plugchan->alloc_ptr[idx] = 0;
+ }
+ plugchan->alloc_size[idx] = 0;
+ plugchan->alloc_lock[idx] = 0;
+ }
+ return 0;
+}
+
+snd_pcm_plugin_t *snd_pcm_plug_first(snd_pcm_t *pcm, int channel)
+{
+ snd_pcm_plug_t *plug;
+ struct snd_pcm_plug_chan *plugchan;
+ if (!pcm)
+ return NULL;
+ if (channel < 0 || channel > 1)
+ return NULL;
+ if (!pcm->chan[channel].open)
+ return NULL;
+
+ plug = (snd_pcm_plug_t*) &pcm->private;
+ plugchan = &plug->chan[channel];
+
+ return plugchan->first;
+}
+
+snd_pcm_plugin_t *snd_pcm_plug_last(snd_pcm_t *pcm, int channel)
+{
+ snd_pcm_plug_t *plug;
+ struct snd_pcm_plug_chan *plugchan;
+ if (!pcm)
+ return NULL;
+ if (channel < 0 || channel > 1)
+ return NULL;
+ if (!pcm->chan[channel].open)
+ return NULL;
+
+ plug = (snd_pcm_plug_t*) &pcm->private;
+ plugchan = &plug->chan[channel];
+
+ return plugchan->last;
+}
+
+int snd_pcm_plug_direct(snd_pcm_t *pcm, int channel)
+{
+ return snd_pcm_plug_first(pcm, channel) == NULL;
+}
+
+#if 0
+double snd_pcm_plug_client_ratio(snd_pcm_t *pcm, int channel)
+{
+ ssize_t client;
+ if (!pcm)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
+ return -EINVAL;
+ if (!pcm->chan[channel].open)
+ return -EBADFD;
+
+ client = snd_pcm_plug_client_size(pcm, channel, 1000000);
+ if (client < 0)
+ return 0;
+ return (double)client / (double)1000000;
+}
+
+double snd_pcm_plug_slave_ratio(snd_pcm_t *pcm, int channel)
+{
+ ssize_t slave;
+ if (!pcm)
+ return -EFAULT;
+ if (channel < 0 || channel > 1)
+ return -EINVAL;
+ if (!pcm->chan[channel].open)
+ return -EBADFD;
+
+ slave = snd_pcm_plug_slave_size(pcm, channel, 1000000);
+ if (slave < 0)
+ return 0;
+ return (double)slave / (double)1000000;
+}
+#endif
+
+/*
+ *
+ */
+
+static int snd_pcm_plug_channel_close(snd_pcm_t *pcm, int channel)
+{
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ snd_pcm_plug_clear(pcm, channel);
+ if (plug->close_slave)
+ return snd_pcm_channel_close(plug->slave, channel);
+ return 0;
+}
+
+static int snd_pcm_plug_channel_nonblock(snd_pcm_t *pcm, int channel, int nonblock)
+{
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ return snd_pcm_channel_nonblock(plug->slave, channel, nonblock);
+}
+
+static int snd_pcm_plug_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
+{
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ return snd_pcm_info(plug->slave, info);
+}
+
+static int snd_pcm_plug_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
+{
+ int err;
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ struct snd_pcm_chan *chan;
+
+ if ((err = snd_pcm_channel_info(plug->slave, info)) < 0)
+ return err;
+ info->formats = snd_pcm_plug_formats(info->formats);
+ info->min_rate = 4000;
+ info->max_rate = 192000;
+ info->min_voices = 1;
+ info->max_voices = 32;
+ info->rates = SND_PCM_RATE_CONTINUOUS | SND_PCM_RATE_8000_192000;
+ info->flags |= SND_PCM_CHNINFO_INTERLEAVE | SND_PCM_CHNINFO_NONINTERLEAVE;
+
+ chan = &pcm->chan[info->channel];
+ if (pcm->chan[info->channel].valid_setup) {
+ info->buffer_size = snd_pcm_plug_client_size(pcm, info->channel, info->buffer_size);
+ info->min_fragment_size = snd_pcm_plug_client_size(pcm, info->channel, info->min_fragment_size);
+ info->max_fragment_size = snd_pcm_plug_client_size(pcm, info->channel, info->max_fragment_size);
+ info->fragment_align = snd_pcm_plug_client_size(pcm, info->channel, info->fragment_align);
+ info->fifo_size = snd_pcm_plug_client_size(pcm, info->channel, info->fifo_size);
+ info->transfer_block_size = snd_pcm_plug_client_size(pcm, info->channel, info->transfer_block_size);
+ if (chan->setup.mode == SND_PCM_MODE_BLOCK)
+ info->mmap_size = chan->setup.buffer_size;
+ else
+ info->mmap_size = snd_pcm_plug_client_size(pcm, info->channel, info->mmap_size);
+ }
+ if (!snd_pcm_plug_direct(pcm, info->channel))
+ info->flags &= ~(SND_PCM_CHNINFO_MMAP | SND_PCM_CHNINFO_MMAP_VALID);
+ return 0;
+}
+
+static int snd_pcm_plug_action(snd_pcm_t *pcm, int channel, int action,
+ unsigned long data)
+{
+ snd_pcm_plugin_t *plugin;
+ int err;
+ snd_pcm_plug_t *plug;
+ struct snd_pcm_plug_chan *plugchan;
+ plug = (snd_pcm_plug_t*) &pcm->private;
+ plugchan = &plug->chan[channel];
+
+ plugin = plugchan->first;
+ while (plugin) {
+ if (plugin->action) {
+ if ((err = plugin->action(plugin, action, data))<0)
+ return err;
+ }
+ plugin = plugin->next;
+ }
+ return 0;
+}
+
+static int snd_pcm_plug_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
+{
+ snd_pcm_channel_params_t slave_params, params1;
+ snd_pcm_channel_info_t slave_info;
+ snd_pcm_plugin_t *plugin;
+ snd_pcm_plug_t *plug;
+ int err;
+ int channel = params->channel;
+
+ plug = (snd_pcm_plug_t*) &pcm->private;
+
+ /*
+ * try to decide, if a conversion is required
+ */
+
+ memset(&slave_info, 0, sizeof(slave_info));
+ slave_info.channel = channel;
+ if ((err = snd_pcm_channel_info(plug->slave, &slave_info)) < 0) {
+ snd_pcm_plug_clear(pcm, channel);
+ return err;
+ }
+
+ if ((err = snd_pcm_plug_slave_params(params, &slave_info, &slave_params)) < 0)
+ return err;
+
+
+ snd_pcm_plug_clear(pcm, channel);
+
+ /* add necessary plugins */
+ memcpy(¶ms1, params, sizeof(*params));
+ if ((err = snd_pcm_plug_format(pcm, ¶ms1, &slave_params)) < 0)
+ return err;
+
+ if (snd_pcm_plug_direct(pcm, channel))
+ return snd_pcm_channel_params(plug->slave, params);
+
+ /*
+ * I/O plugins
+ */
+
+ if (params->mode == SND_PCM_MODE_STREAM) {
+ pdprintf("params stream plugin\n");
+ err = snd_pcm_plugin_build_stream(pcm, channel, plug->slave, &slave_params.format, &plugin);
+ } else if (params->mode == SND_PCM_MODE_BLOCK) {
+ if (slave_info.flags & SND_PCM_CHNINFO_MMAP) {
+ pdprintf("params mmap plugin\n");
+ err = snd_pcm_plugin_build_mmap(pcm, channel, plug->slave, &slave_params.format, &plugin);
+ } else {
+ pdprintf("params block plugin\n");
+ err = snd_pcm_plugin_build_block(pcm, channel, plug->slave, &slave_params.format, &plugin);
+ }
+ } else {
+ return -EINVAL;
+ }
+ if (err < 0)
+ return err;
+ if (channel == SND_PCM_CHANNEL_PLAYBACK) {
+ err = snd_pcm_plugin_append(plugin);
+ } else {
+ err = snd_pcm_plugin_insert(plugin);
+ }
+ if (err < 0) {
+ snd_pcm_plugin_free(plugin);
+ return err;
+ }
+
+ /* compute right sizes */
+ slave_params.frag_size = snd_pcm_plug_slave_size(pcm, channel, slave_params.frag_size);
+ if (params->mode == SND_PCM_MODE_STREAM) {
+ slave_params.buf.stream.bytes_fill_max = snd_pcm_plug_slave_size(pcm, channel, slave_params.buf.stream.bytes_fill_max);
+ slave_params.buf.stream.bytes_min = snd_pcm_plug_slave_size(pcm, channel, slave_params.buf.stream.bytes_min);
+ slave_params.buf.stream.bytes_xrun_max = snd_pcm_plug_slave_size(pcm, channel, slave_params.buf.stream.bytes_xrun_max);
+ slave_params.buf.stream.bytes_align = snd_pcm_plug_slave_size(pcm, channel, slave_params.buf.stream.bytes_align);
+ } else if (params->mode != SND_PCM_MODE_BLOCK)
+ return -EINVAL;
+ pdprintf("params requested params: format = %i, rate = %i, voices = %i\n", slave_params.format.format, slave_params.format.rate, slave_params.format.voices);
+ err = snd_pcm_channel_params(plug->slave, &slave_params);
+ if (err < 0)
+ return err;
+
+ err = snd_pcm_plug_action(pcm, channel, INIT, 0);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static int snd_pcm_plug_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup)
+{
+ int err;
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ struct snd_pcm_plug_chan *plugchan;
+
+ err = snd_pcm_channel_setup(plug->slave, setup);
+ if (err < 0)
+ return err;
+ if (snd_pcm_plug_direct(pcm, setup->channel))
+ return 0;
+ setup->buffer_size = snd_pcm_plug_client_size(pcm, setup->channel, setup->buffer_size);
+ setup->frag_size = snd_pcm_plug_client_size(pcm, setup->channel, setup->frag_size);
+ /* FIXME: it may overflow */
+ setup->pos_boundary = snd_pcm_plug_client_size(pcm, setup->channel, setup->pos_boundary);
+ if (setup->mode == SND_PCM_MODE_STREAM) {
+ setup->buf.stream.bytes_min = snd_pcm_plug_client_size(pcm, setup->channel, setup->buf.stream.bytes_min);
+ setup->buf.stream.bytes_align = snd_pcm_plug_client_size(pcm, setup->channel, setup->buf.stream.bytes_align);
+ setup->buf.stream.bytes_xrun_max = snd_pcm_plug_client_size(pcm, setup->channel, setup->buf.stream.bytes_xrun_max);
+ } else if (setup->mode != SND_PCM_MODE_BLOCK) {
+ return -EINVAL;
+ }
+
+ plugchan = &plug->chan[setup->channel];
+ if (setup->channel == SND_PCM_CHANNEL_PLAYBACK)
+ setup->format = plugchan->first->src_format;
+ else
+ setup->format = plugchan->last->dst_format;
+ return 0;
+}
+
+static int snd_pcm_plug_channel_status(snd_pcm_t *pcm, snd_pcm_channel_status_t *status)
+{
+ int err;
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+
+ err = snd_pcm_channel_status(plug->slave, status);
+ if (err < 0)
+ return err;
+ if (snd_pcm_plug_direct(pcm, status->channel))
+ return 0;
+
+ /* FIXME: may overflow */
+ status->pos_io = snd_pcm_plug_client_size(pcm, status->channel, status->pos_io);
+ status->pos_data = snd_pcm_plug_client_size(pcm, status->channel, status->pos_data);
+ status->bytes_used = snd_pcm_plug_client_size(pcm, status->channel, status->bytes_used);
+ status->bytes_free = snd_pcm_plug_client_size(pcm, status->channel, status->bytes_free);
+ return 0;
+}
+
+static int snd_pcm_plug_channel_prepare(snd_pcm_t *pcm, int channel)
+{
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ int err;
+ err = snd_pcm_channel_prepare(plug->slave, channel);
+ if (err < 0)
+ return err;
+ if (snd_pcm_plug_direct(pcm, channel))
+ return 0;
+ if ((err = snd_pcm_plug_action(pcm, channel, PREPARE, 0))<0)
+ return err;
+ return 0;
+}
+
+static int snd_pcm_plug_channel_go(snd_pcm_t *pcm, int channel)
+{
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ return snd_pcm_channel_go(plug->slave, channel);
+}
+
+static int snd_pcm_plug_sync_go(snd_pcm_t *pcm, snd_pcm_sync_t *sync)
+{
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ return snd_pcm_sync_go(plug->slave, sync);
+}
+
+static int snd_pcm_plug_channel_drain(snd_pcm_t *pcm, int channel)
+{
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ int err;
+
+ if ((err = snd_pcm_channel_drain(plug->slave, channel)) < 0)
+ return err;
+ if (snd_pcm_plug_direct(pcm, channel))
+ return 0;
+ if ((err = snd_pcm_plug_action(pcm, channel, DRAIN, 0))<0)
+ return err;
+ return 0;
+}
+
+static int snd_pcm_plug_channel_flush(snd_pcm_t *pcm, int channel)
+{
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ int err;
+
+ if ((err = snd_pcm_channel_flush(plug->slave, channel)) < 0)
+ return err;
+ if (snd_pcm_plug_direct(pcm, channel))
+ return 0;
+ if ((err = snd_pcm_plug_action(pcm, channel, FLUSH, 0))<0)
+ return err;
+ return 0;
+}
+
+static int snd_pcm_plug_channel_pause(snd_pcm_t *pcm, int channel, int enable)
+{
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ int err;
+
+ if ((err = snd_pcm_channel_pause(plug->slave, channel, enable)) < 0)
+ return err;
+ if ((err = snd_pcm_plug_action(pcm, channel, PAUSE, 0))<0)
+ return err;
+ return 0;
+}
+
+static int snd_pcm_plug_voice_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t *setup)
+{
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ struct snd_pcm_chan *chan;
+ unsigned int voice;
+ int width;
+ size_t size;
+
+ if (snd_pcm_plug_direct(pcm, channel))
+ return snd_pcm_voice_setup(plug->slave, channel, setup);
+
+ voice = setup->voice;
+ memset(setup, 0, sizeof(*setup));
+ setup->voice = voice;
+ chan = &pcm->chan[channel];
+ if (!chan->mmap_control) {
+ setup->addr = -1;
+ return 0;
+ }
+ if (voice >= chan->setup.format.voices)
+ return -EINVAL;
+
+ width = snd_pcm_format_physical_width(chan->setup.format.format);
+ if (width < 0)
+ return width;
+ size = chan->mmap_data_size;
+ if (chan->setup.format.interleave) {
+ setup->addr = 0;
+ setup->first = voice * width;
+ setup->step = chan->setup.format.voices * width;
+ } else {
+ size /= chan->setup.format.voices;
+ setup->addr = setup->voice * size;
+ setup->first = 0;
+ setup->step = width;
+ }
+ return 0;
+}
+
+ssize_t snd_pcm_plug_writev(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count)
+{
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK];
+ unsigned int k, step, voices;
+ int size = 0;
+ if (snd_pcm_plug_direct(pcm, SND_PCM_CHANNEL_PLAYBACK))
+ return snd_pcm_writev(plug->slave, vector, count);
+ voices = chan->setup.format.voices;
+ if (chan->setup.format.interleave)
+ step = 1;
+ else {
+ step = voices;
+ if (count % voices != 0)
+ return -EINVAL;
+ }
+ for (k = 0; k < count; k += step, vector += step) {
+ snd_pcm_plugin_voice_t *voices;
+ int expected, ret;
+ expected = snd_pcm_plug_client_voices_iovec(pcm, SND_PCM_CHANNEL_PLAYBACK, vector, count, &voices);
+ if (expected < 0)
+ return expected;
+ ret = snd_pcm_plug_write_transfer(pcm, voices, expected);
+ if (ret < 0) {
+ if (size > 0)
+ return size;
+ return ret;
+ }
+ size += ret;
+ if (ret != expected)
+ return size;
+ }
+ return size;
+}
+
+ssize_t snd_pcm_plug_readv(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count)
+{
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE];
+ unsigned int k, step, voices;
+ int size = 0;
+ if (snd_pcm_plug_direct(pcm, SND_PCM_CHANNEL_CAPTURE))
+ return snd_pcm_readv(plug->slave, vector, count);
+ voices = chan->setup.format.voices;
+ if (chan->setup.format.interleave)
+ step = 1;
+ else {
+ step = voices;
+ if (count % voices != 0)
+ return -EINVAL;
+ }
+ for (k = 0; k < count; k += step) {
+ snd_pcm_plugin_voice_t *voices;
+ int expected, ret;
+ expected = snd_pcm_plug_client_voices_iovec(pcm, SND_PCM_CHANNEL_CAPTURE, vector, count, &voices);
+ if (expected < 0)
+ return expected;
+ ret = snd_pcm_plug_read_transfer(pcm, voices, expected);
+ if (ret < 0) {
+ if (size > 0)
+ return size;
+ return ret;
+ }
+ size += ret;
+ if (ret != expected)
+ return size;
+ }
+ return size;
+}
+
+ssize_t snd_pcm_plug_write(snd_pcm_t *pcm, const void *buf, size_t count)
+{
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ int expected;
+ snd_pcm_plugin_voice_t *voices;
+
+ if (snd_pcm_plug_direct(pcm, SND_PCM_CHANNEL_PLAYBACK))
+ return snd_pcm_write(plug->slave, buf, count);
+ expected = snd_pcm_plug_client_voices_buf(pcm, SND_PCM_CHANNEL_PLAYBACK, (char *)buf, count, &voices);
+ if (expected < 0)
+ return expected;
+ return snd_pcm_plug_write_transfer(pcm, voices, expected);
+}
+
+ssize_t snd_pcm_plug_read(snd_pcm_t *pcm, void *buf, size_t count)
+{
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ int expected;
+ snd_pcm_plugin_voice_t *voices;
+
+ if (snd_pcm_plug_direct(pcm, SND_PCM_CHANNEL_CAPTURE))
+ return snd_pcm_read(plug->slave, buf, count);
+ expected = snd_pcm_plug_client_voices_buf(pcm, SND_PCM_CHANNEL_CAPTURE, buf, count, &voices);
+ if (expected < 0)
+ return expected;
+ return snd_pcm_plug_read_transfer(pcm, voices, expected);
+}
+
+static int snd_pcm_plug_mmap_control(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t **control, size_t csize UNUSED)
+{
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ if (snd_pcm_plug_direct(pcm, channel))
+ return snd_pcm_mmap_control(plug->slave, channel, control);
+ return -EBADFD;
+}
+
+static int snd_pcm_plug_mmap_data(snd_pcm_t *pcm, int channel, void **buffer, size_t bsize UNUSED)
+{
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ if (snd_pcm_plug_direct(pcm, channel))
+ return snd_pcm_mmap_data(plug->slave, channel, buffer);
+ return -EBADFD;
+}
+
+static int snd_pcm_plug_munmap_control(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t *control UNUSED, size_t csize UNUSED)
+{
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ if (snd_pcm_plug_direct(pcm, channel))
+ return snd_pcm_munmap_control(plug->slave, channel);
+ return -EBADFD;
+}
+
+static int snd_pcm_plug_munmap_data(snd_pcm_t *pcm, int channel, void *buffer UNUSED, size_t size UNUSED)
+{
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ if (snd_pcm_plug_direct(pcm, channel))
+ return snd_pcm_munmap_data(plug->slave, channel);
+ return -EBADFD;
+}
+
+static int snd_pcm_plug_voices_mask(snd_pcm_t *pcm, int channel,
+ bitset_t *client_vmask)
+{
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ if (snd_pcm_plug_direct(pcm, channel))
+ return snd_pcm_voices_mask(plug->slave, channel, client_vmask);
+ if (channel == SND_PCM_CHANNEL_PLAYBACK)
+ return snd_pcm_plug_playback_voices_mask(pcm, client_vmask);
+ else
+ return snd_pcm_plug_capture_voices_mask(pcm, client_vmask);
+}
+
+int snd_pcm_plug_file_descriptor(snd_pcm_t* pcm, int channel)
+{
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private;
+ return snd_pcm_file_descriptor(plug->slave, channel);
+}
+
+struct snd_pcm_ops snd_pcm_plug_ops = {
+ channel_close: snd_pcm_plug_channel_close,
+ channel_nonblock: snd_pcm_plug_channel_nonblock,
+ info: snd_pcm_plug_info,
+ channel_info: snd_pcm_plug_channel_info,
+ channel_params: snd_pcm_plug_channel_params,
+ channel_setup: snd_pcm_plug_channel_setup,
+ voice_setup: snd_pcm_plug_voice_setup,
+ channel_status: snd_pcm_plug_channel_status,
+ channel_prepare: snd_pcm_plug_channel_prepare,
+ channel_go: snd_pcm_plug_channel_go,
+ sync_go: snd_pcm_plug_sync_go,
+ channel_drain: snd_pcm_plug_channel_drain,
+ channel_flush: snd_pcm_plug_channel_flush,
+ channel_pause: snd_pcm_plug_channel_pause,
+ write: snd_pcm_plug_write,
+ writev: snd_pcm_plug_writev,
+ read: snd_pcm_plug_read,
+ readv: snd_pcm_plug_readv,
+ mmap_control: snd_pcm_plug_mmap_control,
+ mmap_data: snd_pcm_plug_mmap_data,
+ munmap_control: snd_pcm_plug_munmap_control,
+ munmap_data: snd_pcm_plug_munmap_data,
+ file_descriptor: snd_pcm_plug_file_descriptor,
+ voices_mask: snd_pcm_plug_voices_mask,
+};
+
+int snd_pcm_plug_connect(snd_pcm_t **handle, snd_pcm_t *slave, int mode, int close_slave)
+{
+ snd_pcm_t *pcm;
+ snd_pcm_plug_t *plug;
+ int err;
+ err = snd_pcm_abstract_open(handle, mode, SND_PCM_TYPE_PLUG, sizeof(snd_pcm_plug_t));
+ if (err < 0) {
+ if (close_slave)
+ snd_pcm_close(slave);
+ return err;
+ }
+ pcm = *handle;
+ pcm->ops = &snd_pcm_plug_ops;
+ plug = (snd_pcm_plug_t*) &pcm->private;
+ plug->slave = slave;
+ plug->close_slave = close_slave;
+ return 0;
+}
+
+int snd_pcm_plug_open_subdevice(snd_pcm_t **handle, int card, int device, int subdevice, int mode)
+{
+ snd_pcm_t *slave;
+ int err;
+ err = snd_pcm_open_subdevice(&slave, card, device, subdevice, mode);
+ if (err < 0)
+ return err;
+ return snd_pcm_plug_connect(handle, slave, mode, 1);
+}
+
+int snd_pcm_plug_open(snd_pcm_t **handle, int card, int device, int mode)
+{
+ return snd_pcm_plug_open_subdevice(handle, card, device, -1, mode);
+}
+
+
+++ /dev/null
-/*
- * PCM Plug-In Interface
- * Copyright (c) 1999 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.
- *
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <math.h>
-#include <sys/uio.h>
-#include <sys/poll.h>
-#include "pcm_local.h"
-
-static void *snd_pcm_plugin_buf_alloc(snd_pcm_t *pcm, int channel, size_t size);
-static void snd_pcm_plugin_buf_free(snd_pcm_t *pcm, int channel, void *ptr);
-
-snd_pcm_plugin_t *snd_pcm_plugin_build(snd_pcm_plugin_handle_t *handle,
- const char *name,
- snd_pcm_format_t *src_format,
- snd_pcm_format_t *dst_format,
- int extra)
-{
- snd_pcm_plugin_t *plugin;
- int voices = 0;
-
- if (extra < 0)
- return NULL;
- if (src_format)
- voices = src_format->voices;
- if (dst_format && dst_format->voices > voices)
- voices = dst_format->voices;
- plugin = (snd_pcm_plugin_t *)calloc(1, sizeof(*plugin) + voices * sizeof(snd_pcm_plugin_voice_t) + extra);
- if (plugin == NULL)
- return NULL;
- plugin->name = name ? strdup(name) : NULL;
- if (src_format) {
- memcpy(&plugin->src_format, src_format, sizeof(snd_pcm_format_t));
- if ((plugin->src_width = snd_pcm_format_physical_width(src_format->format)) < 0)
- return NULL;
- }
- if (dst_format) {
- memcpy(&plugin->dst_format, dst_format, sizeof(snd_pcm_format_t));
- if ((plugin->dst_width = snd_pcm_format_physical_width(dst_format->format)) < 0)
- return NULL;
- }
- plugin->handle = handle;
- plugin->voices = (snd_pcm_plugin_voice_t *)((char *)plugin + sizeof(*plugin));
- plugin->extra_data = (char *)plugin->voices + voices * sizeof(snd_pcm_plugin_voice_t);
- plugin->src_voices = snd_pcm_plugin_src_voices;
- plugin->dst_voices = snd_pcm_plugin_dst_voices;
- return plugin;
-}
-
-int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin)
-{
- if (plugin) {
- if (plugin->private_free)
- plugin->private_free(plugin, plugin->private_data);
- if (plugin->name)
- free(plugin->name);
- free(plugin);
- }
- return 0;
-}
-
-int snd_pcm_plugin_clear(snd_pcm_t *pcm, int channel)
-{
- snd_pcm_plugin_t *plugin, *plugin_next;
- struct snd_pcm_plug *plug;
- int idx;
-
- if (!pcm)
- return -EINVAL;
- plugin = pcm->chan[channel].plug.first;
- pcm->chan[channel].plug.first = NULL;
- pcm->chan[channel].plug.last = NULL;
- while (plugin) {
- plugin_next = plugin->next;
- snd_pcm_plugin_free(plugin);
- plugin = plugin_next;
- }
- plug = &pcm->chan[channel].plug;
- for (idx = 0; idx < 2; idx++) {
- if (plug->alloc_ptr[idx])
- free(plug->alloc_ptr[idx]);
- plug->alloc_ptr[idx] = 0;
- plug->alloc_size[idx] = 0;
- plug->alloc_lock[idx] = 0;
- }
- return 0;
-}
-
-int snd_pcm_plugin_insert(snd_pcm_t *pcm, int channel, snd_pcm_plugin_t *plugin)
-{
- if (!pcm || channel < 0 || channel > 1 || !plugin)
- return -EINVAL;
- plugin->next = pcm->chan[channel].plug.first;
- plugin->prev = NULL;
- if (pcm->chan[channel].plug.first) {
- pcm->chan[channel].plug.first->prev = plugin;
- pcm->chan[channel].plug.first = plugin;
- } else {
- pcm->chan[channel].plug.last =
- pcm->chan[channel].plug.first = plugin;
- }
- return 0;
-}
-
-int snd_pcm_plugin_append(snd_pcm_t *pcm, int channel, snd_pcm_plugin_t *plugin)
-{
- if (!pcm || channel < 0 || channel > 1 || !plugin)
- return -EINVAL;
- plugin->next = NULL;
- plugin->prev = pcm->chan[channel].plug.last;
- if (pcm->chan[channel].plug.last) {
- pcm->chan[channel].plug.last->next = plugin;
- pcm->chan[channel].plug.last = plugin;
- } else {
- pcm->chan[channel].plug.last =
- pcm->chan[channel].plug.first = plugin;
- }
- return 0;
-}
-
-int snd_pcm_plugin_remove_to(snd_pcm_t *pcm, int channel, snd_pcm_plugin_t *plugin)
-{
- snd_pcm_plugin_t *plugin1, *plugin1_prev;
-
- if (!pcm || channel < 0 || channel > 1 || !plugin || !plugin->prev)
- return -EINVAL;
- plugin1 = plugin;
- while (plugin1->prev)
- plugin1 = plugin1->prev;
- if (pcm->chan[channel].plug.first != plugin1)
- return -EINVAL;
- pcm->chan[channel].plug.first = plugin;
- plugin1 = plugin->prev;
- plugin->prev = NULL;
- while (plugin1) {
- plugin1_prev = plugin1->prev;
- snd_pcm_plugin_free(plugin1);
- plugin1 = plugin1_prev;
- }
- return 0;
-}
-
-int snd_pcm_plugin_remove_first(snd_pcm_t *pcm, int channel)
-{
- snd_pcm_plugin_t *plugin;
-
- plugin = pcm->chan[channel].plug.first;
- if (plugin->next) {
- plugin = plugin->next;
- } else {
- return snd_pcm_plugin_clear(pcm, channel);
- }
- return snd_pcm_plugin_remove_to(pcm, channel, plugin);
-}
-
-snd_pcm_plugin_t *snd_pcm_plugin_first(snd_pcm_t *pcm, int channel)
-{
- if (!pcm || channel < 0 || channel > 1)
- return NULL;
- return pcm->chan[channel].plug.first;
-}
-
-snd_pcm_plugin_t *snd_pcm_plugin_last(snd_pcm_t *pcm, int channel)
-{
- if (!pcm || channel < 0 || channel > 1)
- return NULL;
- return pcm->chan[channel].plug.last;
-}
-
-
-double snd_pcm_plugin_transfer_ratio(snd_pcm_t *pcm, int channel)
-{
- ssize_t transfer;
-
- transfer = snd_pcm_plugin_client_size(pcm, channel, 1000000);
- if (transfer < 0)
- return 0;
- return (double)transfer / (double)1000000;
-}
-
-double snd_pcm_plugin_hardware_ratio(snd_pcm_t *pcm, int channel)
-{
- ssize_t hardware;
-
- hardware = snd_pcm_plugin_hardware_size(pcm, channel, 1000000);
- if (hardware < 0)
- return 0;
- return (double)hardware / (double)1000000;
-}
-
-/*
- *
- */
-
-int snd_pcm_plugin_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
-{
- int err;
-
- if ((err = snd_pcm_channel_info(pcm, info)) < 0)
- return err;
- info->formats = snd_pcm_plugin_formats(info->formats);
- info->min_rate = 4000;
- info->max_rate = 192000;
- info->min_voices = 1;
- info->max_voices = 32;
- info->rates = SND_PCM_RATE_CONTINUOUS | SND_PCM_RATE_8000_192000;
- info->buffer_size = snd_pcm_plugin_client_size(pcm, info->channel, info->buffer_size);
- info->min_fragment_size = snd_pcm_plugin_client_size(pcm, info->channel, info->min_fragment_size);
- info->max_fragment_size = snd_pcm_plugin_client_size(pcm, info->channel, info->max_fragment_size);
- info->fragment_align = snd_pcm_plugin_client_size(pcm, info->channel, info->fragment_align);
- info->fifo_size = snd_pcm_plugin_client_size(pcm, info->channel, info->fifo_size);
- info->transfer_block_size = snd_pcm_plugin_client_size(pcm, info->channel, info->transfer_block_size);
- info->mmap_size = snd_pcm_plugin_client_size(pcm, info->channel, info->mmap_size);
- return 0;
-}
-
-static int snd_pcm_plugin_action(snd_pcm_t *pcm, int channel, int action,
- unsigned long data)
-{
- snd_pcm_plugin_t *plugin;
- int err;
-
- plugin = pcm->chan[channel].plug.first;
- while (plugin) {
- if (plugin->action) {
- if ((err = plugin->action(plugin, action, data))<0)
- return err;
- }
- plugin = plugin->next;
- }
- return 0;
-}
-
-int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
-{
- snd_pcm_channel_params_t hwparams, params1;
- snd_pcm_channel_info_t hwinfo;
- snd_pcm_plugin_t *plugin;
- struct snd_pcm_plug *plug;
- int err;
-
- if (!pcm || !params || params->channel < 0 || params->channel > 1)
- return -EINVAL;
-
- plug = &pcm->chan[params->channel].plug;
- if (plug->mmap_data)
- return -EBADFD;
-
- /*
- * try to decide, if a conversion is required
- */
-
- memset(&hwinfo, 0, sizeof(hwinfo));
- hwinfo.channel = params->channel;
- if ((err = snd_pcm_channel_info(pcm, &hwinfo)) < 0) {
- snd_pcm_plugin_clear(pcm, params->channel);
- return err;
- }
-
- if ((err = snd_pcm_plugin_hwparams(params, &hwinfo, &hwparams)) < 0)
- return err;
-
-
- snd_pcm_plugin_clear(pcm, params->channel);
-
- /* add necessary plugins */
- memcpy(¶ms1, params, sizeof(*params));
- if ((err = snd_pcm_plugin_format(pcm, ¶ms1, &hwparams, &hwinfo)) < 0)
- return err;
-
- if (snd_pcm_plugin_first(pcm, params->channel) == NULL)
- return snd_pcm_channel_params(pcm, params);
-
- /*
- * I/O plugins
- */
-
- if (params->mode == SND_PCM_MODE_STREAM) {
- pdprintf("params stream plugin\n");
- err = snd_pcm_plugin_build_stream(pcm, params->channel, &hwparams.format, &plugin);
- } else if (params->mode == SND_PCM_MODE_BLOCK) {
- if (hwinfo.flags & SND_PCM_CHNINFO_MMAP) {
- pdprintf("params mmap plugin\n");
- err = snd_pcm_plugin_build_mmap(pcm, params->channel, &hwparams.format, &plugin);
- } else {
- pdprintf("params block plugin\n");
- err = snd_pcm_plugin_build_block(pcm, params->channel, &hwparams.format, &plugin);
- }
- } else {
- return -EINVAL;
- }
- if (err < 0)
- return err;
- if (params->channel == SND_PCM_CHANNEL_PLAYBACK) {
- err = snd_pcm_plugin_append(pcm, params->channel, plugin);
- } else {
- err = snd_pcm_plugin_insert(pcm, params->channel, plugin);
- }
- if (err < 0) {
- snd_pcm_plugin_free(plugin);
- return err;
- }
-
- /* compute right sizes */
- if (params->mode == SND_PCM_MODE_STREAM) {
- pdprintf("params queue_size = %i\n", hwparams.buf.stream.queue_size);
- hwparams.buf.stream.queue_size = snd_pcm_plugin_hardware_size(pcm, hwparams.channel, hwparams.buf.stream.queue_size);
- hwparams.buf.stream.max_fill = snd_pcm_plugin_hardware_size(pcm, hwparams.channel, hwparams.buf.stream.max_fill);
- pdprintf("params queue_size = %i\n", hwparams.buf.stream.queue_size);
- } else if (params->mode == SND_PCM_MODE_BLOCK) {
- pdprintf("params frag_size = %i\n", hwparams.buf.block.frag_size);
- hwparams.buf.block.frag_size = snd_pcm_plugin_hardware_size(pcm, hwparams.channel, hwparams.buf.block.frag_size);
- pdprintf("params frag_size = %i\n", hwparams.buf.block.frag_size);
- } else {
- return -EINVAL;
- }
- pdprintf("params requested params: format = %i, rate = %i, voices = %i\n", hwparams.format.format, hwparams.format.rate, hwparams.format.voices);
- err = snd_pcm_channel_params(pcm, &hwparams);
- if (err < 0)
- return err;
-
- plug->setup_is_valid = 0;
- memset(&plug->setup, 0, sizeof(snd_pcm_channel_setup_t));
- plug->setup.channel = params->channel;
- if ((err = snd_pcm_plugin_setup(pcm, &plug->setup))<0)
- return err;
- err = snd_pcm_plugin_action(pcm, hwparams.channel, INIT, (long)&hwparams);
- if (err < 0)
- return err;
- return 0;
-}
-
-int snd_pcm_plugin_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup)
-{
- int err;
- struct snd_pcm_plug *plug;
-
- if (!pcm || !setup || setup->channel < 0 || setup->channel > 1)
- return -EINVAL;
- plug = &pcm->chan[setup->channel].plug;
- if (plug->first == NULL)
- return snd_pcm_channel_setup(pcm, setup);
- if (plug->setup_is_valid) {
- memcpy(setup, &plug->setup, sizeof(*setup));
- return 0;
- }
- err = snd_pcm_channel_setup(pcm, setup);
- if (err < 0)
- return err;
- if (setup->mode == SND_PCM_MODE_STREAM) {
- pdprintf("params setup: queue_size = %i\n", setup->buf.stream.queue_size);
- setup->buf.stream.queue_size = snd_pcm_plugin_client_size(pcm, setup->channel, setup->buf.stream.queue_size);
- pdprintf("params setup: queue_size = %i\n", setup->buf.stream.queue_size);
- } else if (setup->mode == SND_PCM_MODE_BLOCK) {
- pdprintf("params setup: frag_size = %i\n", setup->buf.block.frag_size);
- setup->buf.block.frag_size = snd_pcm_plugin_client_size(pcm, setup->channel, setup->buf.block.frag_size);
- pdprintf("params setup: frag_size = %i\n", setup->buf.block.frag_size);
- } else {
- return -EINVAL;
- }
- if (setup->channel == SND_PCM_CHANNEL_PLAYBACK)
- setup->format = plug->first->src_format;
- else
- setup->format = plug->last->dst_format;
- memcpy(&plug->setup, setup, sizeof(*setup));
- plug->setup_is_valid = 1;
- return 0;
-}
-
-int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_channel_status_t *status)
-{
- int err;
-
- if (!pcm || !status || status->channel < 0 || status->channel > 1)
- return -EINVAL;
- err = snd_pcm_channel_status(pcm, status);
- if (err < 0)
- return err;
- /* FIXME: scount may overflow */
- status->scount = snd_pcm_plugin_client_size(pcm, status->channel, status->scount);
- status->count = snd_pcm_plugin_client_size(pcm, status->channel, status->count);
- status->free = snd_pcm_plugin_client_size(pcm, status->channel, status->free);
- return 0;
-}
-
-static void mmap_clear(struct snd_pcm_plug *plug)
-{
- int idx;
- snd_pcm_mmap_fragment_t *f;
-
- f = plug->mmap_control->fragments;
- for (idx = 0; idx < plug->setup.buf.block.frags; idx++) {
- f->data = 0;
- f->io = 0;
- f->res[0] = 0;
- f->res[1] = 0;
- f++;
- }
- plug->mmap_control->status.frag_io = 0;
- plug->mmap_control->status.block = 0;
- plug->mmap_control->status.expblock = 0;
-}
-
-static void snd_pcm_plugin_status_change(snd_pcm_t *pcm, int channel)
-{
- struct snd_pcm_chan *chan;
- struct snd_pcm_plug *plug;
- snd_pcm_channel_status_t status;
- int newstatus;
-
- chan = &pcm->chan[channel];
- plug = &chan->plug;
- if (!plug->mmap_data)
- return;
- if (chan->mmap_control) {
- newstatus = chan->mmap_control->status.status;
- } else {
- status.channel = channel;
- if (snd_pcm_channel_status(pcm, &status) < 0)
- newstatus = SND_PCM_STATUS_NOTREADY;
- else
- newstatus = status.status;
- }
- if (plug->mmap_control->status.status == SND_PCM_STATUS_RUNNING &&
- newstatus != SND_PCM_STATUS_RUNNING) {
- mmap_clear(plug);
- }
- plug->mmap_control->status.status = newstatus;
-}
-
-int snd_pcm_plugin_prepare(snd_pcm_t *pcm, int channel)
-{
- int err;
-
- if ((err = snd_pcm_plugin_action(pcm, channel, PREPARE, 0))<0)
- return err;
- if ((err = snd_pcm_channel_prepare(pcm, channel)) < 0)
- return err;
- snd_pcm_plugin_status_change(pcm, channel);
- return 0;
-}
-
-int snd_pcm_plugin_go(snd_pcm_t *pcm, int channel)
-{
- struct snd_pcm_chan *chan;
- struct snd_pcm_plug *plug;
- if (!pcm || channel < 0 || channel > 1)
- return -EINVAL;
- chan = &pcm->chan[channel];
- plug = &chan->plug;
- if (plug->first == NULL)
- return snd_pcm_channel_go(pcm, channel);
- if (plug->mmap_control) {
- if (plug->mmap_control->status.status != SND_PCM_STATUS_PREPARED)
- return -EBADFD;
- if (channel == SND_PCM_CHANNEL_PLAYBACK) {
- if (!plug->mmap_control->fragments[0].data)
- return -EIO;
- } else {
- if (plug->mmap_control->fragments[0].data)
- return -EIO;
- }
- plug->mmap_control->status.status = SND_PCM_STATUS_RUNNING;
- }
- return 0;
-}
-
-int snd_pcm_plugin_sync_go(snd_pcm_t *pcm, snd_pcm_sync_t *sync)
-{
- int err;
- if (!pcm || !sync)
- return -EINVAL;
- if (snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_PLAYBACK) ||
- snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_CAPTURE)) {
- if ((err = snd_pcm_plugin_go(pcm, SND_PCM_CHANNEL_PLAYBACK)) < 0)
- return err;
- return snd_pcm_plugin_go(pcm, SND_PCM_CHANNEL_CAPTURE);
- }
- return snd_pcm_sync_go(pcm, sync);
-}
-
-int snd_pcm_plugin_playback_drain(snd_pcm_t *pcm)
-{
- int err;
-
- if ((err = snd_pcm_plugin_action(pcm, SND_PCM_CHANNEL_PLAYBACK, DRAIN, 0))<0)
- return err;
- if ((err = snd_pcm_playback_drain(pcm)) < 0)
- return err;
- snd_pcm_plugin_status_change(pcm, SND_PCM_CHANNEL_PLAYBACK);
- return 0;
-}
-
-int snd_pcm_plugin_flush(snd_pcm_t *pcm, int channel)
-{
- int err;
-
- pdprintf("flush\n");
- if ((err = snd_pcm_plugin_action(pcm, channel, FLUSH, 0))<0)
- return err;
- if ((err = snd_pcm_channel_flush(pcm, channel)) < 0)
- return err;
- snd_pcm_plugin_status_change(pcm, channel);
- return 0;
-}
-
-int snd_pcm_plugin_pause(snd_pcm_t *pcm, int enable)
-{
- int err;
-
- if ((err = snd_pcm_playback_pause(pcm, enable)) < 0)
- return err;
- snd_pcm_plugin_status_change(pcm, SND_PCM_CHANNEL_PLAYBACK);
- return 0;
-}
-
-ssize_t snd_pcm_plugin_transfer_size(snd_pcm_t *pcm, int channel)
-{
- struct snd_pcm_plug *plug;
- if (!pcm || channel < 0 || channel > 1)
- return -EINVAL;
- plug = &pcm->chan[channel].plug;
- if (plug->first == NULL)
- return snd_pcm_plugin_transfer_size(pcm, channel);
- if (!plug->setup_is_valid)
- return -EBADFD;
- if (plug->setup.mode != SND_PCM_MODE_BLOCK)
- return -EBADFD;
- return plug->setup.buf.block.frag_size;
-}
-
-int snd_pcm_plugin_voice_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t *setup)
-{
- int voice, width, size;
- struct snd_pcm_plug* plug;
-
- if (!pcm || !setup)
- return -EINVAL;
- if (channel < 0 || channel > 1)
- return -EINVAL;
- voice = setup->voice;
- memset(setup, 0, sizeof(*setup));
- setup->voice = voice;
- if (voice < 0)
- return -EINVAL;
- plug = &pcm->chan[channel].plug;
- if (plug->first == NULL)
- return snd_pcm_voice_setup(pcm, channel, setup);
- if (!plug->mmap_control) {
- setup->addr = -1;
- return 0;
- }
- if (voice >= plug->setup.format.voices)
- return -EINVAL;
-
- width = snd_pcm_format_physical_width(plug->setup.format.format);
- if (width < 0)
- return width;
- size = plug->mmap_size;
- if (plug->setup.format.interleave) {
- setup->addr = 0;
- setup->first = voice * width;
- setup->step = plug->setup.format.voices * width;
- } else {
- size /= plug->setup.format.voices;
- setup->addr = setup->voice * size;
- setup->first = 0;
- setup->step = width;
- }
- return 0;
-}
-
-static int snd_pcm_plugin_load_vector(snd_pcm_plugin_t *plugin,
- snd_pcm_plugin_voice_t **voices,
- const struct iovec *vector,
- int count,
- snd_pcm_format_t *format)
-{
- snd_pcm_plugin_voice_t *v = plugin->voices;
- int width, cvoices, voice;
-
- *voices = NULL;
- if ((width = snd_pcm_format_width(format->format)) < 0)
- return width;
- cvoices = format->voices;
- if (format->interleave) {
- if (count != 1)
- return -EINVAL;
- for (voice = 0; voice < cvoices; voice++, v++) {
- v->aptr = NULL;
- if ((v->addr = vector->iov_base) == NULL)
- return -EINVAL;
- v->first = voice * width;
- v->step = cvoices * width;
- }
- } else {
- if (count != cvoices)
- return -EINVAL;
- for (voice = 0; voice < cvoices; voice++, v++) {
- v->aptr = NULL;
- v->addr = vector[voice].iov_base;
- v->first = 0;
- v->step = width;
- }
- }
- *voices = plugin->voices;
- return 0;
-}
-
-static inline int snd_pcm_plugin_load_src_vector(snd_pcm_plugin_t *plugin,
- snd_pcm_plugin_voice_t **voices,
- const struct iovec *vector,
- int count)
-{
- return snd_pcm_plugin_load_vector(plugin, voices, vector, count, &plugin->src_format);
-}
-
-static inline int snd_pcm_plugin_load_dst_vector(snd_pcm_plugin_t *plugin,
- snd_pcm_plugin_voice_t **voices,
- const struct iovec *vector,
- int count)
-{
- return snd_pcm_plugin_load_vector(plugin, voices, vector, count, &plugin->dst_format);
-}
-
-static ssize_t snd_pcm_plugin_writev1(snd_pcm_t *pcm, const struct iovec *vector, int count)
-{
- snd_pcm_plugin_t *plugin, *next;
- snd_pcm_plugin_voice_t *src_voices, *dst_voices;
- ssize_t samples;
- ssize_t size;
- int idx, err;
-
- plugin = snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_PLAYBACK);
- if ((err = snd_pcm_plugin_load_src_vector(plugin, &src_voices, vector, count)) < 0)
- return err;
- size = 0;
- for (idx = 0; idx < count; idx++)
- size += vector[idx].iov_len;
- samples = snd_pcm_plugin_src_size_to_samples(plugin, size);
- if (samples < 0)
- return samples;
- while (plugin) {
- if ((next = plugin->next) != NULL) {
- ssize_t samples1 = samples;
- if (plugin->dst_samples)
- samples1 = plugin->dst_samples(plugin, samples);
- if ((err = next->src_voices(next, &dst_voices, samples1)) < 0) {
- snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_PLAYBACK, src_voices->aptr);
- return err;
- }
- } else {
- dst_voices = NULL;
- }
- pdprintf("write plugin: %s, %i\n", plugin->name, samples);
- if ((samples = plugin->transfer(plugin, src_voices, dst_voices, samples)) < 0) {
- snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_PLAYBACK, src_voices->aptr);
- if (dst_voices)
- snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_PLAYBACK, dst_voices->aptr);
- return samples;
- }
- snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_PLAYBACK, src_voices->aptr);
- plugin = plugin->next;
- src_voices = dst_voices;
- }
- samples = snd_pcm_plugin_client_samples(pcm, SND_PCM_CHANNEL_PLAYBACK, samples);
- size = snd_pcm_plugin_src_samples_to_size(snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_PLAYBACK), samples);
- if (size < 0)
- return size;
- pdprintf("writev result = %i\n", size);
- return size;
-}
-
-ssize_t snd_pcm_plugin_writev(snd_pcm_t *pcm, const struct iovec *vector, int count)
-{
- snd_pcm_plugin_t *plugin;
- int k, step, voices;
- int size = 0;
- if (vector == NULL)
- return -EFAULT;
- plugin = snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_PLAYBACK);
- if (plugin == NULL)
- return snd_pcm_readv(pcm, vector, count);
- voices = plugin->src_format.voices;
- if (plugin->src_format.interleave)
- step = 1;
- else {
- step = voices;
- if (count % voices != 0)
- return -EINVAL;
- }
- for (k = 0; k < count; k += step) {
- int expected = 0;
- int j;
- int ret = snd_pcm_plugin_writev1(pcm, vector, step);
- if (ret < 0) {
- if (size > 0)
- return size;
- return ret;
- }
- size += ret;
- for (j = 0; j < step; ++j) {
- expected += vector->iov_len;
- ++vector;
- }
- if (ret != expected)
- return size;
- }
- return size;
-}
-
-static ssize_t snd_pcm_plugin_readv1(snd_pcm_t *pcm, const struct iovec *vector, int count)
-{
- snd_pcm_plugin_t *plugin, *next;
- snd_pcm_plugin_voice_t *src_voices = NULL, *dst_voices;
- ssize_t samples;
- ssize_t size;
- int idx, err;
-
- plugin = snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_CAPTURE);
- size = 0;
- for (idx = 0; idx < count; idx++)
- size += vector[idx].iov_len;
- if (size < 0)
- return size;
- samples = snd_pcm_plugin_dst_size_to_samples(snd_pcm_plugin_last(pcm, SND_PCM_CHANNEL_CAPTURE), size);
- samples = snd_pcm_plugin_hardware_samples(pcm, SND_PCM_CHANNEL_CAPTURE, samples);
- while (plugin && samples > 0) {
- if ((next = plugin->next) != NULL) {
- if ((err = plugin->dst_voices(plugin, &dst_voices, samples)) < 0) {
- if (src_voices)
- snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_CAPTURE, src_voices->aptr);
- return err;
- }
- } else {
- if ((err = snd_pcm_plugin_load_dst_vector(plugin, &dst_voices, vector, count)) < 0) {
- if (src_voices)
- snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_CAPTURE, src_voices->aptr);
- return err;
- }
- }
- pdprintf("read plugin: %s, %i\n", plugin->name, samples);
- if ((samples = plugin->transfer(plugin, src_voices, dst_voices, samples)) < 0) {
- if (src_voices)
- snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_CAPTURE, src_voices->aptr);
- snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_CAPTURE, dst_voices->aptr);
- return samples;
- }
- if (src_voices)
- snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_CAPTURE, src_voices->aptr);
- plugin = plugin->next;
- src_voices = dst_voices;
- }
- snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_CAPTURE, dst_voices->aptr);
- size = snd_pcm_plugin_dst_samples_to_size(snd_pcm_plugin_last(pcm, SND_PCM_CHANNEL_CAPTURE), samples);
- pdprintf("readv result = %i\n", size);
- return size;
-}
-
-ssize_t snd_pcm_plugin_readv(snd_pcm_t *pcm, const struct iovec *vector, int count)
-{
- snd_pcm_plugin_t *plugin;
- int k, step, voices;
- int size = 0;
- if (vector == NULL)
- return -EFAULT;
- plugin = snd_pcm_plugin_last(pcm, SND_PCM_CHANNEL_CAPTURE);
- if (plugin == NULL)
- return snd_pcm_readv(pcm, vector, count);
- voices = plugin->dst_format.voices;
- if (plugin->dst_format.interleave)
- step = 1;
- else {
- step = voices;
- if (count % voices != 0)
- return -EINVAL;
- }
- for (k = 0; k < count; k += step) {
- int expected = 0;
- int j;
- int ret = snd_pcm_plugin_readv1(pcm, vector, step);
- if (ret < 0) {
- if (size > 0)
- return size;
- return ret;
- }
- size += ret;
- for (j = 0; j < step; ++j) {
- expected += vector->iov_len;
- ++vector;
- }
- if (ret != expected)
- return size;
- }
- return size;
-}
-
-ssize_t snd_pcm_plugin_write(snd_pcm_t *pcm, const void *buffer, size_t count)
-{
- snd_pcm_plugin_t *plugin;
- int voices;
-
- if ((plugin = snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_PLAYBACK)) == NULL)
- return snd_pcm_write(pcm, buffer, count);
- voices = plugin->src_format.voices;
- if (count % voices != 0)
- return -EINVAL;
- if (plugin->src_format.interleave) {
- struct iovec vec;
- vec.iov_base = (void *)buffer;
- vec.iov_len = count;
- return snd_pcm_plugin_writev1(pcm, &vec, 1);
- } else {
- int idx;
- int size = count / voices;
- struct iovec vec[voices];
- for (idx = 0; idx < voices; idx++) {
- vec[idx].iov_base = (char *)buffer + (size * idx);
- vec[idx].iov_len = size;
- }
- return snd_pcm_plugin_writev1(pcm, vec, voices);
- }
-}
-
-ssize_t snd_pcm_plugin_read(snd_pcm_t *pcm, void *buffer, size_t count)
-{
- snd_pcm_plugin_t *plugin;
- int voices;
-
- if ((plugin = snd_pcm_plugin_last(pcm, SND_PCM_CHANNEL_CAPTURE)) == NULL)
- return snd_pcm_read(pcm, buffer, count);
- voices = plugin->dst_format.voices;
- if (count % voices != 0)
- return -EINVAL;
- if (plugin->dst_format.interleave) {
- struct iovec vec;
- vec.iov_base = buffer;
- vec.iov_len = count;
- return snd_pcm_plugin_readv1(pcm, &vec, 1);
- } else {
- int idx;
- int size = count / voices;
- struct iovec vec[voices];
- for (idx = 0; idx < voices; idx++) {
- vec[idx].iov_base = (char *)buffer + (size * idx);
- vec[idx].iov_len = size;
- }
- return snd_pcm_plugin_readv1(pcm, vec, voices);
- }
-}
-
-/*
- * Plugin helpers
- */
-
-static void *snd_pcm_plugin_buf_alloc(snd_pcm_t *pcm, int channel, size_t size)
-{
- int idx;
- void *ptr;
- struct snd_pcm_plug *plug;
-
- if (pcm == NULL || size <= 0)
- return NULL;
- plug = &pcm->chan[channel].plug;
- for (idx = 0; idx < 2; idx++) {
- if (plug->alloc_lock[idx])
- continue;
- if (plug->alloc_ptr[idx] == NULL)
- continue;
- if (plug->alloc_size[idx] >= size) {
- plug->alloc_lock[idx] = 1;
- return plug->alloc_ptr[idx];
- }
- }
- for (idx = 0; idx < 2; idx++) {
- if (plug->alloc_lock[idx])
- continue;
- if (plug->alloc_ptr[idx] == NULL)
- continue;
- ptr = realloc(plug->alloc_ptr[idx], size);
- if (ptr == NULL)
- continue;
- plug->alloc_size[idx] = size;
- plug->alloc_lock[idx] = 1;
- return plug->alloc_ptr[idx] = ptr;
- }
- for (idx = 0; idx < 2; idx++) {
- if (plug->alloc_ptr[idx] != NULL)
- continue;
- ptr = malloc(size);
- if (ptr == NULL)
- continue;
- plug->alloc_size[idx] = size;
- plug->alloc_lock[idx] = 1;
- return plug->alloc_ptr[idx] = ptr;
- }
- return NULL;
-}
-
-static void snd_pcm_plugin_buf_free(snd_pcm_t *pcm, int channel, void *ptr)
-{
- int idx;
-
- struct snd_pcm_plug *plug;
-
- if (pcm == NULL || ptr == NULL)
- return;
- plug = &pcm->chan[channel].plug;
- for (idx = 0; idx < 2; idx++) {
- if (plug->alloc_ptr[idx] == ptr) {
- plug->alloc_lock[idx] = 0;
- return;
- }
- }
-}
-
-static int snd_pcm_plugin_xvoices(snd_pcm_plugin_t *plugin,
- int channel,
- snd_pcm_plugin_voice_t **voices,
- size_t samples,
- snd_pcm_format_t *format)
-{
- char *ptr;
- int width, voice;
- long size;
- snd_pcm_plugin_voice_t *v;
-
- *voices = NULL;
- if ((width = snd_pcm_format_physical_width(format->format)) < 0)
- return width;
- size = format->voices * samples * width;
- if ((size % 8) != 0)
- return -EINVAL;
- size /= 8;
- ptr = (char *)snd_pcm_plugin_buf_alloc(plugin->handle, channel, size);
- if (ptr == NULL)
- return -ENOMEM;
- if ((size % format->voices) != 0)
- return -EINVAL;
- size /= format->voices;
- v = plugin->voices;
- for (voice = 0; voice < format->voices; voice++, v++) {
- v->aptr = ptr;
- if (format->interleave) {
- v->addr = ptr;
- v->first = voice * width;
- v->step = format->voices * width;
- } else {
- v->addr = ptr + (voice * size);
- v->first = 0;
- v->step = width;
- }
- }
- *voices = plugin->voices;
- return 0;
-}
-
-int snd_pcm_plugin_src_voices(snd_pcm_plugin_t *plugin,
- snd_pcm_plugin_voice_t **voices,
- size_t samples)
-{
- return snd_pcm_plugin_xvoices(plugin, SND_PCM_CHANNEL_PLAYBACK, voices, samples, &plugin->src_format);
-}
-
-int snd_pcm_plugin_dst_voices(snd_pcm_plugin_t *plugin,
- snd_pcm_plugin_voice_t **voices,
- size_t samples)
-{
- return snd_pcm_plugin_xvoices(plugin, SND_PCM_CHANNEL_CAPTURE, voices, samples, &plugin->dst_format);
-}
-
-static void *playback_mmap(void *data)
-{
- snd_pcm_t *pcm = data;
- struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK];
- struct snd_pcm_plug *plug = &chan->plug;
- snd_pcm_mmap_control_t *control;
- void *buffer;
- int frags;
- int frag_size, voice_size, voice_frag_size;
- int voices;
- control = plug->mmap_control;
- buffer = plug->mmap_data;
- frags = plug->setup.buf.block.frags;
- frag_size = plug->setup.buf.block.frag_size;
- voices = plug->setup.format.voices;
- voice_size = plug->mmap_size / voices;
- voice_frag_size = voice_size / frags;
- while (1) {
- int err;
- struct pollfd pfd;
- int frag = control->status.frag_io;
- if (plug->thread_stop)
- break;
- if (control->status.status != SND_PCM_STATUS_RUNNING &&
- control->status.status != SND_PCM_STATUS_PREPARED) {
- usleep(100000);
- continue;
- }
- pfd.fd = chan->fd;
- pfd.events = POLLOUT;
- pfd.revents = 0;
- err = poll(&pfd, 1, -1);
- if (err < 0) {
- fprintf(stderr, "error on poll\n");
- continue;
- }
- if (!control->fragments[frag].data) {
- usleep(10000);
- continue;
- }
- /* NYI: status.block */
- control->fragments[frag].io = 1;
- if (plug->setup.format.interleave) {
- err = snd_pcm_plugin_write(pcm, buffer + frag * frag_size, frag_size);
- } else {
- struct iovec vector[voices];
- struct iovec *v = vector;
- int voice;
- for (voice = 0; voice < voices; ++voice) {
- v->iov_base = buffer + voice_size * voice + frag * voice_frag_size;
- v->iov_len = voice_frag_size;
- v++;
- }
- err = snd_pcm_plugin_writev(pcm, vector, voice_frag_size);
- }
- if (err <= 0) {
- control->fragments[frag].io = 0;
- snd_pcm_plugin_status_change(pcm, SND_PCM_CHANNEL_PLAYBACK);
- continue;
- }
- control->fragments[frag].io = 0;
- control->fragments[frag].data = 0;
- frag++;
- if (frag == frags)
- frag = 0;
- control->status.frag_io = frag;
- }
- return 0;
-}
-
-static void *capture_mmap(void *data)
-{
- snd_pcm_t *pcm = data;
- struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE];
- struct snd_pcm_plug *plug = &chan->plug;
- snd_pcm_mmap_control_t *control;
- void *buffer;
- int frag, frags;
- int frag_size, voice_size, voice_frag_size;
- int voices;
- control = plug->mmap_control;
- buffer = plug->mmap_data;
- frag = 0;
- frags = plug->setup.buf.block.frags;
- frag_size = plug->setup.buf.block.frag_size;
- voices = plug->setup.format.voices;
- voice_size = plug->mmap_size / voices;
- voice_frag_size = voice_size / frags;
- while (1) {
- int err;
- struct pollfd pfd;
- if (plug->thread_stop)
- break;
- if (control->status.status != SND_PCM_STATUS_RUNNING) {
- usleep(100000);
- continue;
- }
- pfd.fd = chan->fd;
- pfd.events = POLLIN;
- pfd.revents = 0;
- err = poll(&pfd, 1, -1);
- if (err < 0) {
- printf("error on poll\n");
- continue;
- }
- if (control->fragments[frag].data) {
- usleep(10000);
- continue;
- }
- /* NYI: status.block */
- control->status.frag_io = frag;
- control->fragments[frag].io = 1;
- if (plug->setup.format.interleave) {
- err = snd_pcm_plugin_read(pcm, buffer + frag * frag_size, frag_size);
- } else {
- struct iovec vector[voices];
- struct iovec *v = vector;
- int voice;
- for (voice = 0; voice < voices; ++voice) {
- v->iov_base = buffer + voice_size * voice + frag * voice_frag_size;
- v->iov_len = voice_frag_size;
- v++;
- }
- err = snd_pcm_plugin_readv(pcm, vector, voice_frag_size);
- }
- if (err < 0) {
- snd_pcm_plugin_status_change(pcm, SND_PCM_CHANNEL_CAPTURE);
- continue;
- }
- control->fragments[frag].io = 0;
- control->fragments[frag].data = 1;
- frag++;
- if (frag == frags)
- frag = 0;
- }
- return 0;
-}
-
-int snd_pcm_plugin_mmap(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t **control, void **data)
-{
- int err;
-
- struct snd_pcm_plug *plug;
- if (!pcm || channel < 0 || channel > 1 || !control || !data)
- return -EINVAL;
-
- plug = &pcm->chan[channel].plug;
- if (plug->first == NULL)
- return snd_pcm_mmap(pcm, channel, control, data);
- if (!plug->setup_is_valid)
- return -EBADFD;
- if (plug->setup.mode != SND_PCM_MODE_BLOCK)
- return -EINVAL;
-
- if (plug->mmap_data) {
- *control = plug->mmap_control;
- *data = plug->mmap_data;
- return 0;
- }
-
- plug->mmap_control = malloc(sizeof(snd_pcm_mmap_control_t));
- plug->mmap_size = plug->setup.buf.block.frag_size * plug->setup.buf.block.frags;
- plug->mmap_data = malloc(plug->mmap_size);
-
- mmap_clear(plug);
-
- *control = plug->mmap_control;
- *data = plug->mmap_data;
-
- plug->thread_stop = 0;
- if (channel == SND_PCM_CHANNEL_PLAYBACK)
- err = pthread_create(&plug->thread, NULL, playback_mmap, pcm);
- else
- err = pthread_create(&plug->thread, NULL, capture_mmap, pcm);
- if (err < 0) {
- snd_pcm_plugin_munmap(pcm, channel);
- return err;
- }
- return 0;
-}
-
-int snd_pcm_plugin_munmap(snd_pcm_t *pcm, int channel)
-{
- struct snd_pcm_plug *plug;
- if (!pcm || channel < 0 || channel > 1)
- return -EINVAL;
- plug = &pcm->chan[channel].plug;
- if (plug->first == NULL)
- return snd_pcm_munmap(pcm, channel);
- if (plug->mmap_data) {
- plug->thread_stop = 1;
- pthread_join(plug->thread, NULL);
- free(plug->mmap_control);
- plug->mmap_control = 0;
- free(plug->mmap_data);
- plug->mmap_data = 0;
- }
- return 0;
-}
/*
* PCM Plug-In shared (kernel/library) code
* Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
*
*
* This library is free software; you can redistribute it and/or modify
#define PLUGIN_DEBUG
#endif
#ifdef __KERNEL__
-#include "../include/driver.h"
-#include "../include/pcm.h"
-#define snd_pcm_plugin_first(pb, channel) ((pb)->oss.plugin_first)
-#define snd_pcm_plugin_last(pb, channel) ((pb)->oss.plugin_last)
-#define snd_pcm_plugin_append(pb, channel, plugin) snd_pcm_oss_plugin_append(pb, plugin)
-#define my_calloc(size) snd_kcalloc(size, GFP_KERNEL)
-#define my_free(ptr) snd_kfree(ptr)
-#define my_strdup(str) snd_kmalloc_strdup(str, GFP_KERNEL)
+#include "../../include/driver.h"
+#include "../../include/pcm.h"
+#define snd_pcm_plug_first(handle, channel) ((handle)->runtime->oss.plugin_first)
+#define snd_pcm_plug_last(handle, channel) ((handle)->runtime->oss.plugin_last)
#else
#include <malloc.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
+#include <sys/uio.h>
#include "pcm_local.h"
-#define my_calloc(size) calloc(1, size)
-#define my_free(ptr) free(ptr)
-#define my_strdup(str) strdup(str)
#endif
+static int snd_pcm_plugin_src_voices_mask(snd_pcm_plugin_t *plugin,
+ bitset_t *dst_vmask,
+ bitset_t **src_vmask)
+{
+ bitset_t *vmask = plugin->src_vmask;
+ bitset_copy(vmask, dst_vmask, plugin->src_format.voices);
+ *src_vmask = vmask;
+ return 0;
+}
+
+static int snd_pcm_plugin_dst_voices_mask(snd_pcm_plugin_t *plugin,
+ bitset_t *src_vmask,
+ bitset_t **dst_vmask)
+{
+ bitset_t *vmask = plugin->dst_vmask;
+ bitset_copy(vmask, src_vmask, plugin->dst_format.voices);
+ *dst_vmask = vmask;
+ return 0;
+}
+
+static int snd_pcm_plugin_side_voices(snd_pcm_plugin_t *plugin,
+ int client_side,
+ size_t samples,
+ snd_pcm_plugin_voice_t **voices)
+{
+ char *ptr;
+ int width;
+ unsigned int voice;
+ long size;
+ snd_pcm_plugin_voice_t *v;
+ snd_pcm_format_t *format;
+ if ((plugin->channel == SND_PCM_CHANNEL_PLAYBACK && client_side) ||
+ (plugin->channel == SND_PCM_CHANNEL_CAPTURE && !client_side)) {
+ format = &plugin->src_format;
+ v = plugin->src_voices;
+ } else {
+ format = &plugin->dst_format;
+ v = plugin->dst_voices;
+ }
+
+ *voices = v;
+ if ((width = snd_pcm_format_physical_width(format->format)) < 0)
+ return width;
+ size = format->voices * samples * width;
+ if ((size % 8) != 0)
+ return -EINVAL;
+ size /= 8;
+ ptr = (char *)snd_pcm_plug_buf_alloc(plugin->handle, plugin->channel, size);
+ if (ptr == NULL)
+ return -ENOMEM;
+ if ((size % format->voices) != 0)
+ return -EINVAL;
+ size /= format->voices;
+ for (voice = 0; voice < format->voices; voice++, v++) {
+ v->enabled = 1;
+ v->wanted = 0;
+ v->aptr = ptr;
+ if (format->interleave) {
+ v->addr = ptr;
+ v->first = voice * width;
+ v->step = format->voices * width;
+ } else {
+ v->addr = ptr + (voice * size);
+ v->first = 0;
+ v->step = width;
+ }
+ }
+ return 0;
+}
+
+int snd_pcm_plugin_client_voices(snd_pcm_plugin_t *plugin,
+ size_t samples,
+ snd_pcm_plugin_voice_t **voices)
+{
+ return snd_pcm_plugin_side_voices(plugin, 1, samples, voices);
+}
+
+int snd_pcm_plugin_slave_voices(snd_pcm_plugin_t *plugin,
+ size_t samples,
+ snd_pcm_plugin_voice_t **voices)
+{
+ return snd_pcm_plugin_side_voices(plugin, 0, samples, voices);
+}
+
+
+int snd_pcm_plugin_build(snd_pcm_plugin_handle_t *handle,
+ int channel,
+ const char *name,
+ snd_pcm_format_t *src_format,
+ snd_pcm_format_t *dst_format,
+ int extra,
+ snd_pcm_plugin_t **ret)
+{
+ snd_pcm_plugin_t *plugin;
+
+ if (!handle)
+ return -EFAULT;
+ if (extra < 0)
+ return -EINVAL;
+ if (channel < 0 || channel > 1)
+ return -EINVAL;
+ if (!src_format || !dst_format)
+ return -EFAULT;
+ plugin = (snd_pcm_plugin_t *)calloc(1, sizeof(*plugin) + extra);
+ if (plugin == NULL)
+ return -ENOMEM;
+ plugin->name = name ? strdup(name) : NULL;
+ plugin->handle = handle;
+ plugin->channel = channel;
+ memcpy(&plugin->src_format, src_format, sizeof(snd_pcm_format_t));
+ if ((plugin->src_width = snd_pcm_format_physical_width(src_format->format)) < 0)
+ return -EINVAL;
+ memcpy(&plugin->dst_format, dst_format, sizeof(snd_pcm_format_t));
+ if ((plugin->dst_width = snd_pcm_format_physical_width(dst_format->format)) < 0)
+ return -EINVAL;
+ plugin->src_voices = calloc(src_format->voices, sizeof(snd_pcm_plugin_voice_t));
+ if (plugin->src_voices == NULL) {
+ free(plugin);
+ return -ENOMEM;
+ }
+ plugin->dst_voices = calloc(dst_format->voices, sizeof(snd_pcm_plugin_voice_t));
+ if (plugin->dst_voices == NULL) {
+ free(plugin->src_voices);
+ free(plugin);
+ return -ENOMEM;
+ }
+ plugin->src_vmask = bitset_alloc(src_format->voices);
+ if (plugin->src_vmask == NULL) {
+ free(plugin->src_voices);
+ free(plugin->dst_voices);
+ free(plugin);
+ return -ENOMEM;
+ }
+ plugin->dst_vmask = bitset_alloc(dst_format->voices);
+ if (plugin->dst_vmask == NULL) {
+ free(plugin->src_voices);
+ free(plugin->dst_voices);
+ free(plugin->src_vmask);
+ free(plugin);
+ return -ENOMEM;
+ }
+ plugin->client_voices = snd_pcm_plugin_client_voices;
+ plugin->src_voices_mask = snd_pcm_plugin_src_voices_mask;
+ plugin->dst_voices_mask = snd_pcm_plugin_dst_voices_mask;
+ *ret = plugin;
+ return 0;
+}
+
+int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin)
+{
+ if (plugin) {
+ if (plugin->private_free)
+ plugin->private_free(plugin, plugin->private_data);
+ if (plugin->name)
+ free(plugin->name);
+ free(plugin->src_voices);
+ free(plugin->dst_voices);
+ free(plugin->src_vmask);
+ free(plugin->dst_vmask);
+ free(plugin);
+ }
+ return 0;
+}
+
ssize_t snd_pcm_plugin_src_samples_to_size(snd_pcm_plugin_t *plugin, size_t samples)
{
ssize_t result;
if (plugin == NULL)
- return -EINVAL;
+ return -EFAULT;
result = samples * plugin->src_format.voices * plugin->src_width;
#if 0
if ((result % 8) != 0)
ssize_t result;
if (plugin == NULL)
- return -EINVAL;
+ return -EFAULT;
result = samples * plugin->dst_format.voices * plugin->dst_width;
#if 0
if ((result % 8) != 0)
long tmp;
if (plugin == NULL)
- return -EINVAL;
+ return -EFAULT;
result = size * 8;
tmp = plugin->src_format.voices * plugin->src_width;
#if 0
long tmp;
if (plugin == NULL)
- return -EINVAL;
+ return -EFAULT;
result = size * 8;
tmp = plugin->dst_format.voices * plugin->dst_width;
#if 0
return result / tmp;
}
-ssize_t snd_pcm_plugin_client_samples(snd_pcm_plugin_handle_t *pb, int channel, size_t drv_samples)
+ssize_t snd_pcm_plug_client_samples(snd_pcm_plugin_handle_t *handle, int channel, size_t drv_samples)
{
snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next;
- if (pb == NULL || (channel != SND_PCM_CHANNEL_PLAYBACK &&
- channel != SND_PCM_CHANNEL_CAPTURE))
+ if (handle == NULL)
+ return -EFAULT;
+ if (channel != SND_PCM_CHANNEL_PLAYBACK &&
+ channel != SND_PCM_CHANNEL_CAPTURE)
return -EINVAL;
if (drv_samples == 0)
return 0;
- if (drv_samples < 0)
- return -EINVAL;
if (channel == SND_PCM_CHANNEL_PLAYBACK) {
- plugin = snd_pcm_plugin_last(pb, SND_PCM_CHANNEL_PLAYBACK);
+ plugin = snd_pcm_plug_last(handle, SND_PCM_CHANNEL_PLAYBACK);
while (plugin && drv_samples > 0) {
plugin_prev = plugin->prev;
if (plugin->src_samples)
plugin = plugin_prev;
}
} else if (channel == SND_PCM_CHANNEL_CAPTURE) {
- plugin = snd_pcm_plugin_first(pb, SND_PCM_CHANNEL_CAPTURE);
+ plugin = snd_pcm_plug_first(handle, SND_PCM_CHANNEL_CAPTURE);
while (plugin && drv_samples > 0) {
plugin_next = plugin->next;
if (plugin->dst_samples)
return drv_samples;
}
-ssize_t snd_pcm_plugin_hardware_samples(snd_pcm_plugin_handle_t *pb, int channel, size_t clt_samples)
+ssize_t snd_pcm_plug_slave_samples(snd_pcm_plugin_handle_t *handle, int channel, size_t clt_samples)
{
snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next;
+ ssize_t samples;
- if (pb == NULL || (channel != SND_PCM_CHANNEL_PLAYBACK &&
- channel != SND_PCM_CHANNEL_CAPTURE))
+ if (handle == NULL)
+ return -EFAULT;
+ if (channel != SND_PCM_CHANNEL_PLAYBACK &&
+ channel != SND_PCM_CHANNEL_CAPTURE)
return -EINVAL;
if (clt_samples == 0)
return 0;
- if (clt_samples < 0)
- return -EINVAL;
+ samples = clt_samples;
if (channel == SND_PCM_CHANNEL_PLAYBACK) {
- plugin = snd_pcm_plugin_first(pb, SND_PCM_CHANNEL_PLAYBACK);
- while (plugin && clt_samples > 0) {
+ plugin = snd_pcm_plug_first(handle, SND_PCM_CHANNEL_PLAYBACK);
+ while (plugin && samples > 0) {
plugin_next = plugin->next;
- if (plugin->dst_samples)
- clt_samples = plugin->dst_samples(plugin, clt_samples);
+ if (plugin->dst_samples) {
+ samples = plugin->dst_samples(plugin, samples);
+ if (samples < 0)
+ return samples;
+ }
plugin = plugin_next;
}
- if (clt_samples < 0)
- return clt_samples;
} else if (channel == SND_PCM_CHANNEL_CAPTURE) {
- plugin = snd_pcm_plugin_last(pb, SND_PCM_CHANNEL_CAPTURE);
+ plugin = snd_pcm_plug_last(handle, SND_PCM_CHANNEL_CAPTURE);
while (plugin) {
plugin_prev = plugin->prev;
- if (plugin->src_samples)
- clt_samples = plugin->src_samples(plugin, clt_samples);
+ if (plugin->src_samples) {
+ samples = plugin->src_samples(plugin, samples);
+ if (samples < 0)
+ return samples;
+ }
plugin = plugin_prev;
}
}
- return clt_samples;
+ return samples;
}
-ssize_t snd_pcm_plugin_client_size(snd_pcm_plugin_handle_t *pb, int channel, size_t drv_size)
+ssize_t snd_pcm_plug_client_size(snd_pcm_plugin_handle_t *handle, int channel, size_t drv_size)
{
snd_pcm_plugin_t *plugin;
ssize_t result = 0;
- if (pb == NULL || (channel != SND_PCM_CHANNEL_PLAYBACK &&
- channel != SND_PCM_CHANNEL_CAPTURE))
+ if (handle == NULL)
+ return -EFAULT;
+ if (channel != SND_PCM_CHANNEL_PLAYBACK &&
+ channel != SND_PCM_CHANNEL_CAPTURE)
return -EINVAL;
if (drv_size == 0)
return 0;
- if (drv_size < 0)
- return -EINVAL;
if (channel == SND_PCM_CHANNEL_PLAYBACK) {
- plugin = snd_pcm_plugin_last(pb, SND_PCM_CHANNEL_PLAYBACK);
+ plugin = snd_pcm_plug_last(handle, SND_PCM_CHANNEL_PLAYBACK);
if (plugin == NULL)
return drv_size;
result = snd_pcm_plugin_dst_size_to_samples(plugin, drv_size);
- result = snd_pcm_plugin_client_samples(pb, SND_PCM_CHANNEL_PLAYBACK, result);
if (result < 0)
return result;
- plugin = snd_pcm_plugin_first(pb, SND_PCM_CHANNEL_PLAYBACK);
+ result = snd_pcm_plug_client_samples(handle, SND_PCM_CHANNEL_PLAYBACK, result);
+ if (result < 0)
+ return result;
+ plugin = snd_pcm_plug_first(handle, SND_PCM_CHANNEL_PLAYBACK);
result = snd_pcm_plugin_src_samples_to_size(plugin, result);
} else if (channel == SND_PCM_CHANNEL_CAPTURE) {
- plugin = snd_pcm_plugin_first(pb, SND_PCM_CHANNEL_CAPTURE);
+ plugin = snd_pcm_plug_first(handle, SND_PCM_CHANNEL_CAPTURE);
if (plugin == NULL)
return drv_size;
result = snd_pcm_plugin_src_size_to_samples(plugin, drv_size);
- result = snd_pcm_plugin_client_samples(pb, SND_PCM_CHANNEL_PLAYBACK, result);
- plugin = snd_pcm_plugin_last(pb, SND_PCM_CHANNEL_CAPTURE);
+ if (result < 0)
+ return result;
+ result = snd_pcm_plug_client_samples(handle, SND_PCM_CHANNEL_PLAYBACK, result);
+ if (result < 0)
+ return result;
+ plugin = snd_pcm_plug_last(handle, SND_PCM_CHANNEL_CAPTURE);
result = snd_pcm_plugin_dst_samples_to_size(plugin, result);
}
return result;
}
-ssize_t snd_pcm_plugin_hardware_size(snd_pcm_plugin_handle_t *pb, int channel, size_t clt_size)
+ssize_t snd_pcm_plug_slave_size(snd_pcm_plugin_handle_t *handle, int channel, size_t clt_size)
{
snd_pcm_plugin_t *plugin;
ssize_t result = 0;
- if (pb == NULL || (channel != SND_PCM_CHANNEL_PLAYBACK &&
- channel != SND_PCM_CHANNEL_CAPTURE))
+ if (handle == NULL)
+ return -EFAULT;
+ if (channel != SND_PCM_CHANNEL_PLAYBACK &&
+ channel != SND_PCM_CHANNEL_CAPTURE)
return -EINVAL;
if (clt_size == 0)
return 0;
- if (clt_size < 0)
- return -EINVAL;
if (channel == SND_PCM_CHANNEL_PLAYBACK) {
- plugin = snd_pcm_plugin_first(pb, SND_PCM_CHANNEL_PLAYBACK);
+ plugin = snd_pcm_plug_first(handle, SND_PCM_CHANNEL_PLAYBACK);
if (plugin == NULL)
return clt_size;
result = snd_pcm_plugin_src_size_to_samples(plugin, clt_size);
- result = snd_pcm_plugin_hardware_samples(pb, SND_PCM_CHANNEL_PLAYBACK, result);
if (result < 0)
return result;
- plugin = snd_pcm_plugin_last(pb, SND_PCM_CHANNEL_PLAYBACK);
+ result = snd_pcm_plug_slave_samples(handle, SND_PCM_CHANNEL_PLAYBACK, result);
+ if (result < 0)
+ return result;
+ plugin = snd_pcm_plug_last(handle, SND_PCM_CHANNEL_PLAYBACK);
result = snd_pcm_plugin_dst_samples_to_size(plugin, result);
} else if (channel == SND_PCM_CHANNEL_CAPTURE) {
- plugin = snd_pcm_plugin_last(pb, SND_PCM_CHANNEL_CAPTURE);
+ plugin = snd_pcm_plug_last(handle, SND_PCM_CHANNEL_CAPTURE);
if (plugin == NULL)
return clt_size;
result = snd_pcm_plugin_dst_size_to_samples(plugin, clt_size);
- result = snd_pcm_plugin_hardware_samples(pb, SND_PCM_CHANNEL_PLAYBACK, result);
if (result < 0)
return result;
- plugin = snd_pcm_plugin_first(pb, SND_PCM_CHANNEL_CAPTURE);
+ result = snd_pcm_plug_slave_samples(handle, SND_PCM_CHANNEL_PLAYBACK, result);
+ if (result < 0)
+ return result;
+ plugin = snd_pcm_plug_first(handle, SND_PCM_CHANNEL_CAPTURE);
result = snd_pcm_plugin_src_samples_to_size(plugin, result);
}
return result;
}
-unsigned int snd_pcm_plugin_formats(unsigned int formats)
+unsigned int snd_pcm_plug_formats(unsigned int formats)
{
int linfmts = (SND_PCM_FMT_U8 | SND_PCM_FMT_S8 |
SND_PCM_FMT_U16_LE | SND_PCM_FMT_S16_LE |
SND_PCM_SFMT_U8
};
-int snd_pcm_plugin_hwparams(snd_pcm_channel_params_t *params,
- snd_pcm_channel_info_t *hwinfo,
- snd_pcm_channel_params_t *hwparams)
+int snd_pcm_plug_slave_params(snd_pcm_channel_params_t *params,
+ snd_pcm_channel_info_t *slave_info,
+ snd_pcm_channel_params_t *slave_params)
{
- memcpy(hwparams, params, sizeof(*hwparams));
- if ((hwinfo->formats & (1 << params->format.format)) == 0) {
+ memcpy(slave_params, params, sizeof(*slave_params));
+ if ((slave_info->formats & (1 << params->format.format)) == 0) {
int format = params->format.format;
- if ((snd_pcm_plugin_formats(hwinfo->formats) & (1 << format)) == 0)
+ 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);
for (sgn = 0; sgn < 2; ++sgn) {
format1 = snd_pcm_build_linear_format(width1, unsignd1, big1);
if (format1 >= 0 &&
- hwinfo->formats & (1 << format1))
+ slave_info->formats & (1 << format1))
goto _found;
unsignd1 = !unsignd1;
}
}
return -EINVAL;
_found:
- hwparams->format.format = format1;
+ slave_params->format.format = format1;
} else {
- int i;
+ unsigned int i;
switch (format) {
case SND_PCM_SFMT_MU_LAW:
#ifndef __KERNEL__
#endif
for (i = 0; i < sizeof(preferred_formats) / sizeof(preferred_formats[0]); ++i) {
int format1 = preferred_formats[i];
- if (hwinfo->formats & (1 << format1)) {
- hwparams->format.format = format1;
+ if (slave_info->formats & (1 << format1)) {
+ slave_params->format.format = format1;
break;
}
}
}
/* voices */
- if (params->format.voices < hwinfo->min_voices ||
- params->format.voices > hwinfo->max_voices) {
- int dst_voices = params->format.voices < hwinfo->min_voices ?
- hwinfo->min_voices : hwinfo->max_voices;
- if ((params->format.rate < hwinfo->min_rate ||
- params->format.rate > hwinfo->max_rate) &&
- dst_voices > 2)
- dst_voices = 2;
- hwparams->format.voices = dst_voices;
+ if (params->format.voices < slave_info->min_voices ||
+ params->format.voices > slave_info->max_voices) {
+ unsigned int dst_voices = params->format.voices < slave_info->min_voices ?
+ slave_info->min_voices : slave_info->max_voices;
+ slave_params->format.voices = dst_voices;
}
/* rate */
- if (params->format.rate < hwinfo->min_rate ||
- params->format.rate > hwinfo->max_rate) {
- int dst_rate = params->format.rate < hwinfo->min_rate ?
- hwinfo->min_rate : hwinfo->max_rate;
- hwparams->format.rate = dst_rate;
+ if (params->format.rate < slave_info->min_rate ||
+ params->format.rate > slave_info->max_rate) {
+ unsigned int dst_rate = params->format.rate < slave_info->min_rate ?
+ slave_info->min_rate : slave_info->max_rate;
+ slave_params->format.rate = dst_rate;
}
/* interleave */
- if (!(hwinfo->flags & SND_PCM_CHNINFO_INTERLEAVE))
- hwparams->format.interleave = 0;
- if (!(hwinfo->flags & SND_PCM_CHNINFO_NONINTERLEAVE))
- hwparams->format.interleave = 1;
+ if (!(slave_info->flags & SND_PCM_CHNINFO_INTERLEAVE))
+ slave_params->format.interleave = 0;
+ if (!(slave_info->flags & SND_PCM_CHNINFO_NONINTERLEAVE))
+ slave_params->format.interleave = 1;
return 0;
}
typedef float ttable_entry_t;
#endif
-int snd_pcm_plugin_format(snd_pcm_plugin_handle_t *pb,
- snd_pcm_channel_params_t *params,
- snd_pcm_channel_params_t *hwparams,
- snd_pcm_channel_info_t *hwinfo)
+int snd_pcm_plug_format(snd_pcm_plugin_handle_t *handle,
+ snd_pcm_channel_params_t *params,
+ snd_pcm_channel_params_t *slave_params)
{
snd_pcm_channel_params_t tmpparams;
snd_pcm_channel_params_t dstparams;
switch (params->channel) {
case SND_PCM_CHANNEL_PLAYBACK:
- memcpy(&dstparams, hwparams, sizeof(*hwparams));
- srcparams = hwparams;
+ memcpy(&dstparams, slave_params, sizeof(*slave_params));
+ srcparams = slave_params;
memcpy(srcparams, params, sizeof(*params));
break;
case SND_PCM_CHANNEL_CAPTURE:
memcpy(&dstparams, params, sizeof(*params));
srcparams = params;
- memcpy(srcparams, hwparams, sizeof(*hwparams));
+ memcpy(srcparams, slave_params, sizeof(*slave_params));
break;
default:
return -EINVAL;
dstparams.format.rate,
dstparams.format.voices);
+ if (srcparams->format.voices == 1)
+ srcparams->format.interleave = dstparams.format.interleave;
+
/* Format change (linearization) */
if ((srcparams->format.format != dstparams.format.format ||
srcparams->format.rate != dstparams.format.rate ||
tmpparams.format.format = dstparams.format.format;
else
tmpparams.format.format = SND_PCM_SFMT_S16;
+ tmpparams.format.interleave = dstparams.format.interleave;
switch (srcparams->format.format) {
case SND_PCM_SFMT_MU_LAW:
- err = snd_pcm_plugin_build_mulaw(pb,
+ err = snd_pcm_plugin_build_mulaw(handle,
+ params->channel,
&srcparams->format,
&tmpparams.format,
&plugin);
break;
#ifndef __KERNEL__
case SND_PCM_SFMT_A_LAW:
- err = snd_pcm_plugin_build_alaw(pb,
+ err = snd_pcm_plugin_build_alaw(handle,
+ params->channel,
&srcparams->format,
&tmpparams.format,
&plugin);
break;
case SND_PCM_SFMT_IMA_ADPCM:
- err = snd_pcm_plugin_build_adpcm(pb,
+ err = snd_pcm_plugin_build_adpcm(handle,
+ params->channel,
&srcparams->format,
&tmpparams.format,
&plugin);
pdprintf("params format change: src=%i, dst=%i returns %i\n", srcparams->format.format, tmpparams.format.format, err);
if (err < 0)
return err;
- err = snd_pcm_plugin_append(pb, params->channel, plugin);
+ err = snd_pcm_plugin_append(plugin);
if (err < 0) {
snd_pcm_plugin_free(plugin);
return err;
if (srcparams->format.voices > dstparams.format.voices) {
int sv = srcparams->format.voices;
int dv = dstparams.format.voices;
- ttable_entry_t *ttable = my_calloc(dv*sv*sizeof(*ttable));
+ ttable_entry_t *ttable = calloc(1, dv*sv*sizeof(*ttable));
#if 1
if (sv == 2 && dv == 1) {
ttable[0] = HALF;
ttable[v * sv + v] = FULL;
}
tmpparams.format.voices = dstparams.format.voices;
+ tmpparams.format.interleave = dstparams.format.interleave;
if (srcparams->format.rate == dstparams.format.rate &&
snd_pcm_format_linear(dstparams.format.format))
tmpparams.format.format = dstparams.format.format;
- err = snd_pcm_plugin_build_route(pb, &srcparams->format,
+ err = snd_pcm_plugin_build_route(handle,
+ params->channel,
+ &srcparams->format,
&tmpparams.format,
ttable,
&plugin);
- my_free(ttable);
+ free(ttable);
pdprintf("params voices reduction: src=%i, dst=%i returns %i\n", srcparams->format.voices, tmpparams.format.voices, err);
if (err < 0) {
snd_pcm_plugin_free(plugin);
return err;
}
- err = snd_pcm_plugin_append(pb, params->channel, plugin);
+ err = snd_pcm_plugin_append(plugin);
if (err < 0) {
snd_pcm_plugin_free(plugin);
return err;
/* rate resampling */
if (srcparams->format.rate != dstparams.format.rate) {
tmpparams.format.rate = dstparams.format.rate;
+ tmpparams.format.interleave = dstparams.format.interleave;
if (srcparams->format.voices == dstparams.format.voices &&
snd_pcm_format_linear(dstparams.format.format))
tmpparams.format.format = dstparams.format.format;
- err = snd_pcm_plugin_build_rate(pb,
+ err = snd_pcm_plugin_build_rate(handle,
+ params->channel,
&srcparams->format,
&tmpparams.format,
&plugin);
snd_pcm_plugin_free(plugin);
return err;
}
- err = snd_pcm_plugin_append(pb, params->channel, plugin);
+ err = snd_pcm_plugin_append(plugin);
if (err < 0) {
snd_pcm_plugin_free(plugin);
return err;
if (srcparams->format.voices < dstparams.format.voices) {
int sv = srcparams->format.voices;
int dv = dstparams.format.voices;
- ttable_entry_t *ttable = my_calloc(dv * sv * sizeof(*ttable));
-#if 1
- if (sv == 1 && dv == 2) {
- ttable[0] = FULL;
- ttable[1] = FULL;
- } else
-#endif
+ ttable_entry_t *ttable = calloc(1, dv * sv * sizeof(*ttable));
+#if 0
{
int v;
for (v = 0; v < sv; ++v)
ttable[v * sv + v] = FULL;
}
+#else
+ {
+ /* Playback is spreaded on all voices */
+ int vd, vs;
+ for (vd = 0, vs = 0; vd < dv; ++vd) {
+ ttable[vd * sv + vs] = FULL;
+ vs++;
+ if (vs == sv)
+ vs = 0;
+ }
+ }
+#endif
tmpparams.format.voices = dstparams.format.voices;
+ tmpparams.format.interleave = dstparams.format.interleave;
if (snd_pcm_format_linear(dstparams.format.format))
tmpparams.format.format = dstparams.format.format;
- err = snd_pcm_plugin_build_route(pb,
+ err = snd_pcm_plugin_build_route(handle,
+ params->channel,
&srcparams->format,
&tmpparams.format,
ttable,
&plugin);
- my_free(ttable);
+ free(ttable);
pdprintf("params voices extension: src=%i, dst=%i returns %i\n", srcparams->format.voices, tmpparams.format.voices, err);
if (err < 0) {
snd_pcm_plugin_free(plugin);
return err;
}
- err = snd_pcm_plugin_append(pb, params->channel, plugin);
+ err = snd_pcm_plugin_append(plugin);
if (err < 0) {
snd_pcm_plugin_free(plugin);
return err;
/* format change */
if (srcparams->format.format != dstparams.format.format) {
tmpparams.format.format = dstparams.format.format;
+ tmpparams.format.interleave = dstparams.format.interleave;
if (tmpparams.format.format == SND_PCM_SFMT_MU_LAW) {
- err = snd_pcm_plugin_build_mulaw(pb,
+ err = snd_pcm_plugin_build_mulaw(handle,
+ params->channel,
&srcparams->format,
&tmpparams.format,
&plugin);
}
#ifndef __KERNEL__
else if (tmpparams.format.format == SND_PCM_SFMT_A_LAW) {
- err = snd_pcm_plugin_build_alaw(pb,
+ err = snd_pcm_plugin_build_alaw(handle,
+ params->channel,
&srcparams->format,
&tmpparams.format,
&plugin);
}
else if (tmpparams.format.format == SND_PCM_SFMT_IMA_ADPCM) {
- err = snd_pcm_plugin_build_adpcm(pb,
+ err = snd_pcm_plugin_build_adpcm(handle,
+ params->channel,
&srcparams->format,
&tmpparams.format,
&plugin);
#endif
else if (snd_pcm_format_linear(srcparams->format.format) &&
snd_pcm_format_linear(tmpparams.format.format)) {
- err = snd_pcm_plugin_build_linear(pb,
+ err = snd_pcm_plugin_build_linear(handle,
+ params->channel,
&srcparams->format,
&tmpparams.format,
&plugin);
pdprintf("params format change: src=%i, dst=%i returns %i\n", srcparams->format.format, tmpparams.format.format, err);
if (err < 0)
return err;
- err = snd_pcm_plugin_append(pb, params->channel, plugin);
+ err = snd_pcm_plugin_append(plugin);
+ if (err < 0) {
+ snd_pcm_plugin_free(plugin);
+ return err;
+ }
+ srcparams->format = tmpparams.format;
+ }
+
+ /* interleave */
+ if (srcparams->format.interleave != dstparams.format.interleave) {
+ tmpparams.format.interleave = dstparams.format.interleave;
+ err = snd_pcm_plugin_build_copy(handle,
+ params->channel,
+ &tmpparams.format,
+ &plugin);
+ pdprintf("interleave change: src=%i, dst=%i returns %i\n", srcparams->format.interleave, tmpparams.format.interleave, err);
+ if (err < 0)
+ return err;
+ err = snd_pcm_plugin_append(plugin);
if (err < 0) {
snd_pcm_plugin_free(plugin);
return err;
return 0;
}
+
+ssize_t snd_pcm_plug_client_voices_buf(snd_pcm_plugin_handle_t *handle,
+ int channel,
+ char *buf,
+ size_t count,
+ snd_pcm_plugin_voice_t **voices)
+{
+ snd_pcm_plugin_t *plugin;
+ snd_pcm_plugin_voice_t *v;
+ snd_pcm_format_t *format;
+ int width, nvoices, voice;
+
+ if (buf == NULL)
+ return -EINVAL;
+ if (channel == SND_PCM_CHANNEL_PLAYBACK) {
+ plugin = snd_pcm_plug_first(handle, channel);
+ format = &plugin->src_format;
+ v = plugin->src_voices;
+ }
+ else {
+ plugin = snd_pcm_plug_last(handle, channel);
+ format = &plugin->dst_format;
+ v = plugin->dst_voices;
+ }
+ *voices = v;
+ if ((width = snd_pcm_format_physical_width(format->format)) < 0)
+ return width;
+ if ((count * 8) % width != 0)
+ return -EINVAL;
+ nvoices = format->voices;
+ if (format->interleave ||
+ format->voices == 1) {
+ for (voice = 0; voice < nvoices; voice++, v++) {
+ v->enabled = 1;
+ v->wanted = (channel == SND_PCM_CHANNEL_CAPTURE);
+ v->aptr = NULL;
+ v->addr = buf;
+ v->first = voice * width;
+ v->step = nvoices * width;
+ }
+ return count;
+ } else
+ return -EINVAL;
+}
+
+int snd_pcm_plug_client_voices_iovec(snd_pcm_plugin_handle_t *handle,
+ int channel,
+ const struct iovec *vector,
+ unsigned long count,
+ snd_pcm_plugin_voice_t **voices)
+{
+ snd_pcm_plugin_t *plugin;
+ snd_pcm_plugin_voice_t *v;
+ snd_pcm_format_t *format;
+ int width;
+ unsigned int nvoices, voice;
+
+ if (channel == SND_PCM_CHANNEL_PLAYBACK) {
+ plugin = snd_pcm_plug_first(handle, channel);
+ format = &plugin->src_format;
+ v = plugin->src_voices;
+ }
+ else {
+ plugin = snd_pcm_plug_last(handle, channel);
+ format = &plugin->dst_format;
+ v = plugin->dst_voices;
+ }
+ *voices = v;
+ if ((width = snd_pcm_format_physical_width(format->format)) < 0)
+ return width;
+ nvoices = format->voices;
+ if (format->interleave) {
+ if (count != 1 || vector->iov_base == NULL ||
+ (vector->iov_len * 8) % width != 0)
+ return -EINVAL;
+
+ for (voice = 0; voice < nvoices; voice++, v++) {
+ v->enabled = 1;
+ v->wanted = (channel == SND_PCM_CHANNEL_CAPTURE);
+ v->aptr = NULL;
+ v->addr = vector->iov_base;
+ v->first = voice * width;
+ v->step = nvoices * width;
+ }
+ return vector->iov_len;
+ } else {
+ size_t len;
+ if (count != nvoices)
+ return -EINVAL;
+ len = vector->iov_len;
+ if ((len * 8) % width != 0)
+ return -EINVAL;
+ for (voice = 0; voice < nvoices; voice++, v++, vector++) {
+ if (vector->iov_len != len)
+ return -EINVAL;
+ v->enabled = (vector->iov_base != NULL);
+ v->wanted = (v->enabled && (channel == SND_PCM_CHANNEL_CAPTURE));
+ v->aptr = NULL;
+ v->addr = vector->iov_base;
+ v->first = 0;
+ v->step = width;
+ }
+ return len * nvoices;
+ }
+}
+
+int snd_pcm_plug_playback_voices_mask(snd_pcm_plugin_handle_t *handle,
+ bitset_t *client_vmask)
+{
+#ifndef __KERNEL__
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &handle->private;
+#endif
+ snd_pcm_plugin_t *plugin = snd_pcm_plug_last(handle, SND_PCM_CHANNEL_PLAYBACK);
+ if (plugin == NULL) {
+#ifndef __KERNEL__
+ return snd_pcm_voices_mask(plug->slave, SND_PCM_CHANNEL_PLAYBACK, client_vmask);
+#else
+ return 0;
+#endif
+ } else {
+ int svoices = plugin->dst_format.voices;
+ bitset_t bs[bitset_size(svoices)];
+ bitset_t *srcmask;
+ bitset_t *dstmask = bs;
+ int err;
+ bitset_one(dstmask, svoices);
+#ifndef __KERNEL__
+ err = snd_pcm_voices_mask(plug->slave, SND_PCM_CHANNEL_PLAYBACK, dstmask);
+ if (err < 0)
+ return err;
+#endif
+ if (plugin == NULL) {
+ bitset_and(client_vmask, dstmask, svoices);
+ return 0;
+ }
+ while (1) {
+ err = plugin->src_voices_mask(plugin, dstmask, &srcmask);
+ if (err < 0)
+ return err;
+ dstmask = srcmask;
+ if (plugin->prev == NULL)
+ break;
+ plugin = plugin->prev;
+ }
+ bitset_and(client_vmask, dstmask, plugin->src_format.voices);
+ return 0;
+ }
+}
+
+int snd_pcm_plug_capture_voices_mask(snd_pcm_plugin_handle_t *handle,
+ bitset_t *client_vmask)
+{
+#ifndef __KERNEL__
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &handle->private;
+#endif
+ snd_pcm_plugin_t *plugin = snd_pcm_plug_first(handle, SND_PCM_CHANNEL_CAPTURE);
+ if (plugin == NULL) {
+#ifndef __KERNEL__
+ return snd_pcm_voices_mask(plug->slave, SND_PCM_CHANNEL_CAPTURE, client_vmask);
+#else
+ return 0;
+#endif
+ } else {
+ int svoices = plugin->src_format.voices;
+ bitset_t bs[bitset_size(svoices)];
+ bitset_t *srcmask = bs;
+ bitset_t *dstmask;
+ int err;
+ bitset_one(srcmask, svoices);
+#ifndef __KERNEL__
+ err = snd_pcm_voices_mask(plug->slave, SND_PCM_CHANNEL_CAPTURE, srcmask);
+ if (err < 0)
+ return err;
+#endif
+ while (1) {
+ err = plugin->dst_voices_mask(plugin, srcmask, &dstmask);
+ if (err < 0)
+ return err;
+ srcmask = dstmask;
+ if (plugin->next == NULL)
+ break;
+ plugin = plugin->next;
+ }
+ bitset_and(client_vmask, srcmask, plugin->dst_format.voices);
+ return 0;
+ }
+}
+
+static int snd_pcm_plug_playback_disable_useless_voices(snd_pcm_plugin_handle_t *handle,
+ snd_pcm_plugin_voice_t *src_voices)
+{
+ snd_pcm_plugin_t *plugin = snd_pcm_plug_first(handle, SND_PCM_CHANNEL_PLAYBACK);
+ unsigned int nvoices = plugin->src_format.voices;
+ bitset_t bs[bitset_size(nvoices)];
+ bitset_t *srcmask = bs;
+ int err;
+ unsigned int voice;
+ for (voice = 0; voice < nvoices; voice++) {
+ if (src_voices[voice].enabled)
+ bitset_set(srcmask, voice);
+ else
+ bitset_reset(srcmask, voice);
+ }
+ err = snd_pcm_plug_playback_voices_mask(handle, srcmask);
+ if (err < 0)
+ return err;
+ for (voice = 0; voice < nvoices; voice++) {
+ if (!bitset_get(srcmask, voice))
+ src_voices[voice].enabled = 0;
+ }
+ return 0;
+}
+
+static int snd_pcm_plug_capture_disable_useless_voices(snd_pcm_plugin_handle_t *handle,
+ snd_pcm_plugin_voice_t *src_voices,
+ snd_pcm_plugin_voice_t *client_voices)
+{
+#ifndef __KERNEL__
+ snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &handle->private;
+#endif
+ snd_pcm_plugin_t *plugin = snd_pcm_plug_last(handle, SND_PCM_CHANNEL_CAPTURE);
+ unsigned int nvoices = plugin->dst_format.voices;
+ bitset_t bs[bitset_size(nvoices)];
+ bitset_t *dstmask = bs;
+ bitset_t *srcmask;
+ int err;
+ unsigned int voice;
+ for (voice = 0; voice < nvoices; voice++) {
+ if (client_voices[voice].enabled)
+ bitset_set(dstmask, voice);
+ else
+ bitset_reset(dstmask, voice);
+ }
+ while (plugin) {
+ err = plugin->src_voices_mask(plugin, dstmask, &srcmask);
+ if (err < 0)
+ return err;
+ dstmask = srcmask;
+ plugin = plugin->prev;
+ }
+#ifndef __KERNEL__
+ err = snd_pcm_voices_mask(plug->slave, SND_PCM_CHANNEL_CAPTURE, dstmask);
+ if (err < 0)
+ return err;
+#endif
+ plugin = snd_pcm_plug_first(handle, SND_PCM_CHANNEL_CAPTURE);
+ nvoices = plugin->src_format.voices;
+ for (voice = 0; voice < nvoices; voice++) {
+ if (!bitset_get(dstmask, voice))
+ src_voices[voice].enabled = 0;
+ }
+ return 0;
+}
+
+void snd_pcm_plugin_silence_voice(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_voice_t *dst_voice,
+ size_t samples)
+{
+ char *dst = dst_voice->addr + dst_voice->first / 8;
+ int dst_step = dst_voice->step / 8;
+ switch (plugin->dst_width) {
+ case 4: {
+ u_int8_t silence = snd_pcm_format_silence_64(plugin->dst_format.format);
+ u_int8_t s0 = silence & 0x0f;
+ u_int8_t s1 = silence & 0xf0;
+ int dstbit = dst_voice->first % 8;
+ int dstbit_step = dst_voice->step % 8;
+ while (samples-- > 0) {
+ if (dstbit) {
+ *dst &= 0x0f;
+ *dst |= s1;
+ } else {
+ *dst &= 0xf0;
+ *dst |= s0;
+ }
+ dst += dst_step;
+ dstbit += dstbit_step;
+ if (dstbit == 8) {
+ dst++;
+ dstbit = 0;
+ }
+ }
+ break;
+ }
+ case 8: {
+ u_int8_t silence = snd_pcm_format_silence_64(plugin->dst_format.format);
+ while (samples-- > 0) {
+ *dst = silence;
+ dst += dst_step;
+ }
+ break;
+ }
+ case 16: {
+ u_int16_t silence = snd_pcm_format_silence_64(plugin->dst_format.format);
+ while (samples-- > 0) {
+ *(u_int16_t*)dst = silence;
+ dst += dst_step;
+ }
+ break;
+ }
+ case 32: {
+ u_int32_t silence = snd_pcm_format_silence_64(plugin->dst_format.format);
+ while (samples-- > 0) {
+ *(u_int32_t*)dst = silence;
+ dst += dst_step;
+ }
+ break;
+ }
+ case 64: {
+ u_int64_t silence = snd_pcm_format_silence_64(plugin->dst_format.format);
+ while (samples-- > 0) {
+ *(u_int64_t*)dst = silence;
+ dst += dst_step;
+ }
+ break;
+ }
+ }
+}
+
+
+ssize_t snd_pcm_plug_write_transfer(snd_pcm_plugin_handle_t *handle, snd_pcm_plugin_voice_t *src_voices, size_t size)
+{
+ snd_pcm_plugin_t *plugin, *next;
+ snd_pcm_plugin_voice_t *dst_voices;
+ ssize_t samples;
+ int err;
+
+ if ((err = snd_pcm_plug_playback_disable_useless_voices(handle, src_voices)) < 0)
+ return err;
+
+ plugin = snd_pcm_plug_first(handle, SND_PCM_CHANNEL_PLAYBACK);
+ samples = snd_pcm_plugin_src_size_to_samples(plugin, size);
+ if (samples < 0)
+ return samples;
+ while (plugin && samples > 0) {
+ if ((next = plugin->next) != NULL) {
+ ssize_t samples1 = samples;
+ if (plugin->dst_samples)
+ samples1 = plugin->dst_samples(plugin, samples);
+ if ((err = next->client_voices(next, samples1, &dst_voices)) < 0) {
+ snd_pcm_plug_buf_unlock(handle, SND_PCM_CHANNEL_PLAYBACK, src_voices->aptr);
+ return err;
+ }
+ } else {
+ if ((err = snd_pcm_plugin_slave_voices(plugin, samples, &dst_voices)) < 0)
+ return err;
+ }
+ pdprintf("write plugin: %s, %i\n", plugin->name, samples);
+ if ((samples = plugin->transfer(plugin, src_voices, dst_voices, samples)) < 0) {
+ snd_pcm_plug_buf_unlock(handle, SND_PCM_CHANNEL_PLAYBACK, src_voices->aptr);
+ snd_pcm_plug_buf_unlock(handle, SND_PCM_CHANNEL_PLAYBACK, dst_voices->aptr);
+ return samples;
+ }
+ snd_pcm_plug_buf_unlock(handle, SND_PCM_CHANNEL_PLAYBACK, src_voices->aptr);
+ src_voices = dst_voices;
+ plugin = next;
+ }
+ snd_pcm_plug_buf_unlock(handle, SND_PCM_CHANNEL_PLAYBACK, src_voices->aptr);
+ samples = snd_pcm_plug_client_samples(handle, SND_PCM_CHANNEL_PLAYBACK, samples);
+ if (samples < 0)
+ return samples;
+ return snd_pcm_plugin_src_samples_to_size(snd_pcm_plug_first(handle, SND_PCM_CHANNEL_PLAYBACK), samples);
+}
+
+ssize_t snd_pcm_plug_read_transfer(snd_pcm_plugin_handle_t *handle, snd_pcm_plugin_voice_t *dst_voices_final, size_t size)
+{
+ snd_pcm_plugin_t *plugin, *next;
+ snd_pcm_plugin_voice_t *src_voices, *dst_voices;
+ ssize_t samples;
+ int err;
+
+ plugin = snd_pcm_plug_last(handle, SND_PCM_CHANNEL_CAPTURE);
+ samples = snd_pcm_plugin_dst_size_to_samples(plugin, size);
+ if (samples < 0)
+ return samples;
+ samples = snd_pcm_plug_slave_samples(handle, SND_PCM_CHANNEL_CAPTURE, samples);
+ if (samples < 0)
+ return samples;
+
+ plugin = snd_pcm_plug_first(handle, SND_PCM_CHANNEL_CAPTURE);
+ if ((err = snd_pcm_plugin_slave_voices(plugin, samples, &src_voices)) < 0)
+ return err;
+ if ((err = snd_pcm_plug_capture_disable_useless_voices(handle, src_voices, dst_voices_final) < 0))
+ return err;
+
+ while (plugin && samples > 0) {
+ if ((next = plugin->next) != NULL) {
+ if ((err = plugin->client_voices(plugin, samples, &dst_voices)) < 0) {
+ snd_pcm_plug_buf_unlock(handle, SND_PCM_CHANNEL_CAPTURE, src_voices->aptr);
+ return err;
+ }
+ } else {
+ dst_voices = dst_voices_final;
+ }
+ pdprintf("read plugin: %s, %i\n", plugin->name, samples);
+ if ((samples = plugin->transfer(plugin, src_voices, dst_voices, samples)) < 0) {
+ snd_pcm_plug_buf_unlock(handle, SND_PCM_CHANNEL_CAPTURE, src_voices->aptr);
+ snd_pcm_plug_buf_unlock(handle, SND_PCM_CHANNEL_CAPTURE, dst_voices->aptr);
+ return samples;
+ }
+#if 0
+ {
+ unsigned int voice;
+ for (voice = 0; voice < plugin->src_format.voices; ++voice) {
+ fprintf(stderr, "%d%d ", src_voices[voice].enabled, src_voices[voice].wanted);
+ }
+ fprintf(stderr, " -> ");
+ for (voice = 0; voice < plugin->dst_format.voices; ++voice) {
+ fprintf(stderr, "%d%d ", dst_voices[voice].enabled, dst_voices[voice].wanted);
+ }
+ fprintf(stderr, "\n");
+ }
+#endif
+ snd_pcm_plug_buf_unlock(handle, SND_PCM_CHANNEL_CAPTURE, src_voices->aptr);
+ plugin = next;
+ src_voices = dst_voices;
+ }
+ snd_pcm_plug_buf_unlock(handle, SND_PCM_CHANNEL_CAPTURE, src_voices->aptr);
+ return snd_pcm_plugin_dst_samples_to_size(snd_pcm_plug_last(handle, SND_PCM_CHANNEL_CAPTURE), samples);
+}
EXTRA_LTLIBRARIES = libpcmplugin.la
-libpcmplugin_la_SOURCES = block.c mmap.c stream.c linear.c \
+libpcmplugin_la_SOURCES = block.c mmap.c stream.c copy.c linear.c \
mulaw.c alaw.c adpcm.c rate.c route.c
all: libpcmplugin.la
typedef void (*adpcm_f)(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
size_t samples);
typedef struct adpcm_private_data {
static void adpcm_init(snd_pcm_plugin_t *plugin)
{
- int voice;
+ unsigned int voice;
adpcm_t *data = (adpcm_t *)plugin->extra_data;
for (voice = 0; voice < plugin->src_format.voices; voice++) {
adpcm_voice_t *v = &data->voices[voice];
static void adpcm_decode(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
size_t samples)
{
#define PUT16_LABELS
int src_step, srcbit_step, dst_step;
size_t samples1;
adpcm_voice_t *state;
- if (src_voices[voice].addr == NULL) {
- if (dst_voices[voice].addr != NULL) {
-// null_voice(&dst_voices[voice]);
- zero_voice(plugin, &dst_voices[voice], samples);
- }
+ if (!src_voices[voice].enabled) {
+ if (dst_voices[voice].wanted)
+ snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples);
+ dst_voices[voice].enabled = 0;
continue;
}
+ dst_voices[voice].enabled = 1;
src = src_voices[voice].addr + src_voices[voice].first / 8;
srcbit = src_voices[voice].first % 8;
dst = dst_voices[voice].addr + dst_voices[voice].first / 8;
static void adpcm_encode(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
size_t samples)
{
#define GET16_LABELS
int src_step, dst_step, dstbit_step;
size_t samples1;
adpcm_voice_t *state;
- if (src_voices[voice].addr == NULL) {
- if (dst_voices[voice].addr != NULL) {
-// null_voice(&dst_voices[voice]);
- zero_voice(plugin, &dst_voices[voice], samples);
- }
+ if (!src_voices[voice].enabled) {
+ if (dst_voices[voice].wanted)
+ snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples);
+ dst_voices[voice].enabled = 0;
continue;
}
+ dst_voices[voice].enabled = 1;
src = src_voices[voice].addr + src_voices[voice].first / 8;
dst = dst_voices[voice].addr + dst_voices[voice].first / 8;
dstbit = dst_voices[voice].first % 8;
static ssize_t adpcm_transfer(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
size_t samples)
{
adpcm_t *data;
- int voice;
+ unsigned int voice;
if (plugin == NULL || src_voices == NULL || dst_voices == NULL)
return -EFAULT;
- if (samples < 0)
- return -EINVAL;
if (samples == 0)
return 0;
for (voice = 0; voice < plugin->src_format.voices; voice++) {
- if (src_voices[voice].addr != NULL &&
- dst_voices[voice].addr == NULL)
- return -EFAULT;
if (plugin->src_format.format == SND_PCM_SFMT_IMA_ADPCM) {
if (src_voices[voice].first % 4 != 0 ||
src_voices[voice].step % 4 != 0 ||
static int adpcm_action(snd_pcm_plugin_t * plugin,
snd_pcm_plugin_action_t action,
- unsigned long udata)
+ unsigned long udata UNUSED)
{
if (plugin == NULL)
return -EINVAL;
case FLUSH:
adpcm_init(plugin);
break;
+ default:
+ break;
}
return 0; /* silenty ignore other actions */
}
int snd_pcm_plugin_build_adpcm(snd_pcm_plugin_handle_t *handle,
+ int channel,
snd_pcm_format_t *src_format,
snd_pcm_format_t *dst_format,
snd_pcm_plugin_t **r_plugin)
{
+ int err;
struct adpcm_private_data *data;
snd_pcm_plugin_t *plugin;
snd_pcm_format_t *format;
if (!snd_pcm_format_linear(format->format))
return -EINVAL;
- plugin = snd_pcm_plugin_build(handle,
- "Ima-ADPCM<->linear conversion",
- src_format,
- dst_format,
- sizeof(adpcm_t) + src_format->voices * sizeof(adpcm_voice_t));
- if (plugin == NULL)
- return -ENOMEM;
+ err = snd_pcm_plugin_build(handle, channel,
+ "Ima-ADPCM<->linear conversion",
+ src_format,
+ dst_format,
+ sizeof(adpcm_t) + src_format->voices * sizeof(adpcm_voice_t),
+ &plugin);
+ if (err < 0)
+ return err;
data = (adpcm_t *)plugin->extra_data;
data->func = func;
data->conv = getput_index(format->format);
typedef void (*alaw_f)(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
size_t samples);
typedef struct alaw_private_data {
static void alaw_decode(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
size_t samples)
{
#define PUT16_LABELS
char *dst;
int src_step, dst_step;
size_t samples1;
- if (src_voices[voice].addr == NULL) {
- if (dst_voices[voice].addr != NULL) {
-// null_voice(&dst_voices[voice]);
- zero_voice(plugin, &dst_voices[voice], samples);
- }
+ if (!src_voices[voice].enabled) {
+ if (dst_voices[voice].wanted)
+ snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples);
+ dst_voices[voice].enabled = 0;
continue;
}
+ dst_voices[voice].enabled = 1;
src = src_voices[voice].addr + src_voices[voice].first / 8;
dst = dst_voices[voice].addr + dst_voices[voice].first / 8;
src_step = src_voices[voice].step / 8;
static void alaw_encode(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
size_t samples)
{
#define GET16_LABELS
char *dst;
int src_step, dst_step;
size_t samples1;
- if (src_voices[voice].addr == NULL) {
- if (dst_voices[voice].addr != NULL) {
-// null_voice(&dst_voices[voice]);
- zero_voice(plugin, &dst_voices[voice], samples);
- }
+ if (!src_voices[voice].enabled) {
+ if (dst_voices[voice].wanted)
+ snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples);
+ dst_voices[voice].enabled = 0;
continue;
}
+ dst_voices[voice].enabled = 1;
src = src_voices[voice].addr + src_voices[voice].first / 8;
dst = dst_voices[voice].addr + dst_voices[voice].first / 8;
src_step = src_voices[voice].step / 8;
static ssize_t alaw_transfer(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
size_t samples)
{
alaw_t *data;
- int voice;
+ unsigned int voice;
if (plugin == NULL || src_voices == NULL || dst_voices == NULL)
return -EFAULT;
- if (samples < 0)
- return -EINVAL;
if (samples == 0)
return 0;
for (voice = 0; voice < plugin->src_format.voices; voice++) {
- if (src_voices[voice].addr != NULL &&
- dst_voices[voice].addr == NULL)
- return -EFAULT;
if (src_voices[voice].first % 8 != 0 ||
src_voices[voice].step % 8 != 0)
return -EINVAL;
}
int snd_pcm_plugin_build_alaw(snd_pcm_plugin_handle_t *handle,
+ int channel,
snd_pcm_format_t *src_format,
snd_pcm_format_t *dst_format,
snd_pcm_plugin_t **r_plugin)
{
+ int err;
alaw_t *data;
snd_pcm_plugin_t *plugin;
snd_pcm_format_t *format;
if (!snd_pcm_format_linear(format->format))
return -EINVAL;
- plugin = snd_pcm_plugin_build(handle,
- "A-Law<->linear conversion",
- src_format,
- dst_format,
- sizeof(alaw_t));
- if (plugin == NULL)
- return -ENOMEM;
+ err = snd_pcm_plugin_build(handle, channel,
+ "A-Law<->linear conversion",
+ src_format,
+ dst_format,
+ sizeof(alaw_t),
+ &plugin);
+ if (err < 0)
+ return err;
data = (alaw_t*)plugin->extra_data;
data->func = func;
data->conv = getput_index(format->format);
*
*/
+#ifdef __KERNEL__
+#include "../../include/driver.h"
+#include "../../include/pcm.h"
+#include "../../include/pcm_plugin.h"
+#define snd_pcm_write(handle,buf,count) snd_pcm_oss_write3(handle,buf,count,1)
+#define snd_pcm_writev(handle,vec,count) snd_pcm_oss_writev3(handle,vec,count,1)
+#define snd_pcm_read(handle,buf,count) snd_pcm_oss_read3(handle,buf,count,1)
+#define snd_pcm_readv(handle,vec,count) snd_pcm_oss_readv3(handle,vec,count,1)
+#else
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/uio.h>
#include "../pcm_local.h"
+#endif
/*
* Basic block plugin
*/
typedef struct block_private_data {
- int channel;
+ snd_pcm_plugin_handle_t *slave;
} block_t;
static ssize_t block_transfer(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
size_t samples)
{
block_t *data;
if (data == NULL)
return -EINVAL;
vec = (struct iovec *)((char *)data + sizeof(*data));
- if (data->channel == SND_PCM_CHANNEL_PLAYBACK) {
+ if (plugin->channel == SND_PCM_CHANNEL_PLAYBACK) {
if (src_voices == NULL)
return -EINVAL;
if ((result = snd_pcm_plugin_src_samples_to_size(plugin, samples)) < 0)
return result;
+ count = plugin->src_format.voices;
if (plugin->src_format.interleave) {
- result = snd_pcm_write(plugin->handle, src_voices->addr, result);
+ result = snd_pcm_write(data->slave, src_voices->addr, result);
} else {
- count = plugin->src_format.voices;
result /= count;
for (voice = 0; voice < count; voice++) {
- vec[voice].iov_base = src_voices[voice].addr;
+ if (src_voices[voice].enabled)
+ vec[voice].iov_base = src_voices[voice].addr;
+ else
+ vec[voice].iov_base = 0;
vec[voice].iov_len = result;
}
- result = snd_pcm_writev(plugin->handle, vec, count);
+ result = snd_pcm_writev(data->slave, vec, count);
}
if (result < 0)
return result;
return snd_pcm_plugin_src_size_to_samples(plugin, result);
- } else if (data->channel == SND_PCM_CHANNEL_CAPTURE) {
+ } else if (plugin->channel == SND_PCM_CHANNEL_CAPTURE) {
if (dst_voices == NULL)
return -EINVAL;
if ((result = snd_pcm_plugin_dst_samples_to_size(plugin, samples)) < 0)
return result;
+ count = plugin->dst_format.voices;
if (plugin->dst_format.interleave) {
- result = snd_pcm_read(plugin->handle, dst_voices->addr, result);
+ result = snd_pcm_read(data->slave, dst_voices->addr, result);
+ for (voice = 0; voice < count; voice++) {
+ dst_voices[voice].enabled = src_voices[voice].enabled;
+ }
} else {
- count = plugin->dst_format.voices;
result /= count;
for (voice = 0; voice < count; voice++) {
- vec[voice].iov_base = dst_voices[voice].addr;
+ dst_voices[voice].enabled = src_voices[voice].enabled;
+ if (dst_voices[voice].enabled)
+ vec[voice].iov_base = dst_voices[voice].addr;
+ else
+ vec[voice].iov_base = 0;
vec[voice].iov_len = result;
}
- result = snd_pcm_readv(plugin->handle, vec, count);
+ result = snd_pcm_readv(data->slave, vec, count);
}
if (result < 0)
return result;
}
}
-int snd_pcm_plugin_build_block(snd_pcm_t *pcm, int channel,
+static int block_src_voices(snd_pcm_plugin_t *plugin,
+ size_t samples,
+ snd_pcm_plugin_voice_t **voices)
+{
+ int err;
+ unsigned int voice;
+ snd_pcm_plugin_voice_t *v;
+ err = snd_pcm_plugin_client_voices(plugin, samples, &v);
+ if (err < 0)
+ return err;
+ *voices = v;
+ for (voice = 0; voice < plugin->src_format.voices; ++voice, ++v)
+ v->wanted = 1;
+ return 0;
+}
+
+int snd_pcm_plugin_build_block(snd_pcm_plugin_handle_t *pcm,
+ int channel,
+ snd_pcm_plugin_handle_t *slave,
snd_pcm_format_t *format,
snd_pcm_plugin_t **r_plugin)
{
+ int err;
block_t *data;
snd_pcm_plugin_t *plugin;
if (r_plugin == NULL)
return -EINVAL;
*r_plugin = NULL;
- if (pcm == NULL || channel < 0 || channel > 1 || format == NULL)
+ if (pcm == NULL || format == NULL)
return -EINVAL;
- plugin = snd_pcm_plugin_build(pcm,
- channel == SND_PCM_CHANNEL_PLAYBACK ?
- "I/O block playback" :
- "I/O block capture",
- format, format,
- sizeof(block_t) + sizeof(struct iovec) * format->voices);
- if (plugin == NULL)
- return -ENOMEM;
+ err = snd_pcm_plugin_build(pcm, channel,
+ "I/O block",
+ format, format,
+ sizeof(block_t) + sizeof(struct iovec) * format->voices,
+ &plugin);
+ if (err < 0)
+ return err;
data = (block_t *)plugin->extra_data;
- data->channel = channel;
+ data->slave = slave;
plugin->transfer = block_transfer;
+ if (format->interleave && channel == SND_PCM_CHANNEL_PLAYBACK)
+ plugin->client_voices = block_src_voices;
*r_plugin = plugin;
return 0;
}
--- /dev/null
+/*
+ * Linear conversion Plug-In
+ * 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.
+ *
+ */
+
+#ifdef __KERNEL__
+#include "../../include/driver.h"
+#include "../../include/pcm.h"
+#include "../../include/pcm_plugin.h"
+#else
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <endian.h>
+#include <byteswap.h>
+#include <sys/uio.h>
+#include "../pcm_local.h"
+#endif
+
+typedef struct copy_private_data {
+ int copy;
+} copy_t;
+
+static void copy(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_voice_t *src_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
+ size_t samples)
+{
+#define COPY_LABELS
+#include "plugin_ops.h"
+#undef COPY_LABELS
+ copy_t *data = (copy_t *)plugin->extra_data;
+ void *copy = copy_labels[data->copy];
+ int voice;
+ int nvoices = plugin->src_format.voices;
+ for (voice = 0; voice < nvoices; ++voice) {
+ char *src;
+ char *dst;
+ int src_step, dst_step;
+ size_t samples1;
+ if (!src_voices[voice].enabled) {
+ if (dst_voices[voice].wanted)
+ snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples);
+ dst_voices[voice].enabled = 0;
+ continue;
+ }
+ dst_voices[voice].enabled = 1;
+ src = src_voices[voice].addr + src_voices[voice].first / 8;
+ dst = dst_voices[voice].addr + dst_voices[voice].first / 8;
+ src_step = src_voices[voice].step / 8;
+ dst_step = dst_voices[voice].step / 8;
+ samples1 = samples;
+ while (samples1-- > 0) {
+ goto *copy;
+#define COPY_END after
+#include "plugin_ops.h"
+#undef COPY_END
+ after:
+ src += src_step;
+ dst += dst_step;
+ }
+ }
+}
+
+static ssize_t copy_transfer(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_voice_t *src_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
+ size_t samples)
+{
+ copy_t *data;
+ unsigned int voice;
+
+ if (plugin == NULL || src_voices == NULL || dst_voices == NULL)
+ return -EFAULT;
+ data = (copy_t *)plugin->extra_data;
+ if (samples == 0)
+ return 0;
+ for (voice = 0; voice < plugin->src_format.voices; voice++) {
+ if (src_voices[voice].first % 8 != 0 ||
+ src_voices[voice].step % 8 != 0)
+ return -EINVAL;
+ if (dst_voices[voice].first % 8 != 0 ||
+ dst_voices[voice].step % 8 != 0)
+ return -EINVAL;
+ }
+ copy(plugin, src_voices, dst_voices, samples);
+ return samples;
+}
+
+int copy_index(int format)
+{
+ int size = snd_pcm_format_physical_width(format);
+ switch (size) {
+ case 8:
+ return 0;
+ case 16:
+ return 1;
+ case 32:
+ return 2;
+ case 64:
+ return 3;
+ default:
+ return -EINVAL;
+ }
+}
+
+int snd_pcm_plugin_build_copy(snd_pcm_plugin_handle_t *handle,
+ int channel,
+ snd_pcm_format_t *format,
+ snd_pcm_plugin_t **r_plugin)
+{
+ int err;
+ struct copy_private_data *data;
+ snd_pcm_plugin_t *plugin;
+ int copy;
+
+ if (r_plugin == NULL)
+ return -EFAULT;
+ *r_plugin = NULL;
+
+ copy = copy_index(format->format);
+ if (copy < 0)
+ return -EINVAL;
+
+ err = snd_pcm_plugin_build(handle, channel,
+ "copy",
+ format,
+ format,
+ sizeof(copy_t),
+ &plugin);
+ if (err < 0)
+ return err;
+ data = (copy_t *)plugin->extra_data;
+ data->copy = copy;
+ plugin->transfer = copy_transfer;
+ *r_plugin = plugin;
+ return 0;
+}
*/
typedef struct linear_private_data {
- int copy;
+ int conv;
} linear_t;
static void convert(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
size_t samples)
{
-#define COPY_LABELS
+#define CONV_LABELS
#include "plugin_ops.h"
-#undef COPY_LABELS
+#undef CONV_LABELS
linear_t *data = (linear_t *)plugin->extra_data;
- void *copy = copy_labels[data->copy];
+ void *conv = conv_labels[data->conv];
int voice;
int nvoices = plugin->src_format.voices;
for (voice = 0; voice < nvoices; ++voice) {
char *dst;
int src_step, dst_step;
size_t samples1;
- if (src_voices[voice].addr == NULL) {
- if (dst_voices[voice].addr != NULL) {
-// null_voice(&dst_voices[voice]);
- zero_voice(plugin, &dst_voices[voice], samples);
- }
+ if (!src_voices[voice].enabled) {
+ if (dst_voices[voice].wanted)
+ snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples);
+ dst_voices[voice].enabled = 0;
continue;
}
+ dst_voices[voice].enabled = 1;
src = src_voices[voice].addr + src_voices[voice].first / 8;
dst = dst_voices[voice].addr + dst_voices[voice].first / 8;
src_step = src_voices[voice].step / 8;
dst_step = dst_voices[voice].step / 8;
samples1 = samples;
while (samples1-- > 0) {
- goto *copy;
-#define COPY_END after
+ goto *conv;
+#define CONV_END after
#include "plugin_ops.h"
-#undef COPY_END
+#undef CONV_END
after:
src += src_step;
dst += dst_step;
static ssize_t linear_transfer(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
size_t samples)
{
linear_t *data;
- int voice;
+ unsigned int voice;
if (plugin == NULL || src_voices == NULL || dst_voices == NULL)
return -EFAULT;
data = (linear_t *)plugin->extra_data;
- if (samples < 0)
- return -EINVAL;
if (samples == 0)
return 0;
for (voice = 0; voice < plugin->src_format.voices; voice++) {
- if (src_voices[voice].addr != NULL &&
- dst_voices[voice].addr == NULL)
- return -EFAULT;
if (src_voices[voice].first % 8 != 0 ||
src_voices[voice].step % 8 != 0)
return -EINVAL;
return samples;
}
-int copy_index(int src_format, int dst_format)
+int conv_index(int src_format, int dst_format)
{
int src_endian, dst_endian, sign, src_width, dst_width;
}
int snd_pcm_plugin_build_linear(snd_pcm_plugin_handle_t *handle,
+ int channel,
snd_pcm_format_t *src_format,
snd_pcm_format_t *dst_format,
snd_pcm_plugin_t **r_plugin)
{
+ int err;
struct linear_private_data *data;
snd_pcm_plugin_t *plugin;
snd_pcm_format_linear(dst_format->format)))
return -EINVAL;
- plugin = snd_pcm_plugin_build(handle,
- "linear format conversion",
- src_format,
- dst_format,
- sizeof(linear_t));
- if (plugin == NULL)
- return -ENOMEM;
+ err = snd_pcm_plugin_build(handle, channel,
+ "linear format conversion",
+ src_format,
+ dst_format,
+ sizeof(linear_t),
+ &plugin);
+ if (err < 0)
+ return err;
data = (linear_t *)plugin->extra_data;
- data->copy = copy_index(src_format->format, dst_format->format);
+ data->conv = conv_index(src_format->format, dst_format->format);
plugin->transfer = linear_transfer;
*r_plugin = plugin;
return 0;
#include <unistd.h>
#include <string.h>
#include <errno.h>
+#include <assert.h>
#include <sys/poll.h>
#include <sys/uio.h>
#include "../pcm_local.h"
*/
typedef struct mmap_private_data {
- int channel;
+ snd_pcm_t *slave;
snd_pcm_mmap_control_t *control;
- char *buffer;
- int frag;
- int frag_size, samples_frag_size;
- int start_mode, stop_mode;
- int frags, frags_used;
- int frags_min, frags_max;
- unsigned int lastblock;
+ void *buffer;
+ unsigned int frag;
+ size_t samples_frag_size;
+ char *silence;
snd_pcm_plugin_voice_t voices[0];
} mmap_t;
-static int playback_ok(snd_pcm_plugin_t *plugin)
-{
- mmap_t *data = (mmap_t *)plugin->extra_data;
- snd_pcm_mmap_control_t *control = data->control;
- int delta = control->status.block;
-
- if (delta < data->lastblock) {
- delta += (~0 - data->lastblock) + 1;
- } else {
- delta -= data->lastblock;
- }
- data->frags_used -= delta;
- if (data->frags_used < 0) {
- /* correction for rollover */
- data->frag += -data->frags_used;
- data->frag %= data->frags;
- data->frags_used = 0;
- }
- data->lastblock += delta;
- return data->frags_used <= data->frags_max &&
- (data->frags - data->frags_used) >= data->frags_min;
-}
-static int poll_playback(snd_pcm_t *pcm)
+static int mmap_src_voices(snd_pcm_plugin_t *plugin,
+ size_t samples,
+ snd_pcm_plugin_voice_t **voices)
{
- int err;
+ mmap_t *data;
+ unsigned int voice;
+ snd_pcm_plugin_voice_t *dv, *sv;
+ struct snd_pcm_chan *chan;
+ snd_pcm_channel_setup_t *setup;
+ snd_pcm_mmap_control_t *ctrl;
+ int frag, f;
struct pollfd pfd;
-
- if (pcm->mode & SND_PCM_OPEN_NONBLOCK)
- return -EAGAIN;
- pfd.fd = pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd;
- pfd.events = POLLOUT;
- pfd.revents = 0;
- err = poll(&pfd, 1, 1000);
- return err < 0 ? err : 0;
-}
+ int ready;
-static int query_playback(snd_pcm_plugin_t *plugin, int not_use_poll)
-{
- mmap_t *data = (mmap_t *)plugin->extra_data;
- snd_pcm_mmap_control_t *control = data->control;
- int err;
+ if (plugin == NULL || voices == NULL)
+ return -EINVAL;
+ data = (mmap_t *)plugin->extra_data;
+ if (samples != data->samples_frag_size)
+ return -EINVAL;
- switch (control->status.status) {
- case SND_PCM_STATUS_PREPARED:
- if (data->start_mode == SND_PCM_START_GO)
+ ctrl = data->control;
+ chan = &plugin->handle->chan[plugin->channel];
+ setup = &chan->setup;
+ if (ctrl->status < SND_PCM_STATUS_PREPARED)
+ return -EBADFD;
+
+ ready = snd_pcm_mmap_ready(data->slave, plugin->channel);
+ if (ready < 0)
+ return ready;
+ if (!ready) {
+ if (ctrl->status != SND_PCM_STATUS_RUNNING)
+ return -EPIPE;
+ if (chan->mode & SND_PCM_NONBLOCK)
return -EAGAIN;
- if ((data->start_mode == SND_PCM_START_DATA &&
- playback_ok(plugin)) ||
- (data->start_mode == SND_PCM_START_FULL &&
- data->frags_used == data->frags)) {
- err = snd_pcm_channel_go(plugin->handle, data->channel);
- if (err < 0)
- return err;
- }
- break;
- case SND_PCM_STATUS_RUNNING:
- if (!not_use_poll) {
- control->status.expblock = control->status.block + 1;
- err = poll_playback(plugin->handle);
- if (err < 0)
- return err;
- }
- break;
- case SND_PCM_STATUS_UNDERRUN:
- return -EPIPE;
- default:
- return -EIO;
+ pfd.fd = snd_pcm_file_descriptor(plugin->handle, plugin->channel);
+ pfd.events = POLLOUT | POLLERR;
+ ready = poll(&pfd, 1, 10000);
+ if (ready < 0)
+ return ready;
+ if (ready && pfd.revents & POLLERR)
+ return -EPIPE;
+ assert(snd_pcm_mmap_ready(data->slave, plugin->channel));
}
- return 0;
-}
+ frag = ctrl->frag_data;
+ f = frag % setup->frags;
-static int capture_ok(snd_pcm_plugin_t *plugin)
-{
- mmap_t *data = (mmap_t *)plugin->extra_data;
- snd_pcm_mmap_control_t *control = data->control;
- int delta = control->status.block;
-
- if (delta < data->lastblock) {
- delta += (~0 - data->lastblock) + 1;
- } else {
- delta -= data->lastblock;
- }
- data->frags_used += delta;
- if (data->frags_used > data->frags) {
- /* correction for rollover */
- data->frag += data->frags_used - data->frags;
- data->frag %= data->frags;
- data->frags_used = data->frags;
+ dv = data->voices;
+ sv = plugin->src_voices;
+ *voices = sv;
+ for (voice = 0; voice < plugin->src_format.voices; ++voice) {
+ sv->enabled = 1;
+ sv->wanted = !data->silence[voice * setup->frags + f];
+ sv->aptr = 0;
+ sv->addr = dv->addr + (dv->step * data->samples_frag_size * f) / 8;
+ sv->first = dv->first;
+ sv->step = dv->step;
+ ++sv;
+ ++dv;
}
- data->lastblock += delta;
- return data->frags_used >= data->frags_min;
+ data->frag = frag;
+ return 0;
}
-static int poll_capture(snd_pcm_t *pcm)
+static int mmap_dst_voices(snd_pcm_plugin_t *plugin,
+ size_t samples,
+ snd_pcm_plugin_voice_t **voices)
{
+ mmap_t *data;
int err;
+ unsigned int voice;
+ snd_pcm_plugin_voice_t *dv, *sv;
+ struct snd_pcm_chan *chan;
+ snd_pcm_channel_setup_t *setup;
+ snd_pcm_mmap_control_t *ctrl;
+ int frag, f;
struct pollfd pfd;
+ int ready;
- if (pcm->mode & SND_PCM_OPEN_NONBLOCK)
- return -EAGAIN;
- pfd.fd = pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd;
- pfd.events = POLLIN;
- pfd.revents = 0;
- err = poll(&pfd, 1, 1000);
- return err < 0 ? err : 0;
-}
-
-static int query_capture(snd_pcm_plugin_t *plugin, int not_use_poll)
-{
- mmap_t *data = (mmap_t *)plugin->extra_data;
- snd_pcm_mmap_control_t *control = data->control;
- int err;
+ if (plugin == NULL || voices == NULL)
+ return -EINVAL;
+ data = (mmap_t *)plugin->extra_data;
+ if (samples != data->samples_frag_size)
+ return -EINVAL;
- switch (control->status.status) {
- case SND_PCM_STATUS_PREPARED:
- if (data->start_mode != SND_PCM_START_DATA)
- return -EAGAIN;
- err = snd_pcm_channel_go(plugin->handle, data->channel);
+ chan = &plugin->handle->chan[plugin->channel];
+ setup = &chan->setup;
+ ctrl = data->control;
+ if (ctrl->status < SND_PCM_STATUS_PREPARED)
+ return -EBADFD;
+ if (ctrl->status == SND_PCM_STATUS_PREPARED &&
+ chan->setup.start_mode == SND_PCM_START_DATA) {
+ err = snd_pcm_channel_go(data->slave, plugin->channel);
if (err < 0)
return err;
- break;
- case SND_PCM_STATUS_RUNNING:
- if (!not_use_poll) {
- control->status.expblock = control->status.block + data->frags_min;
- err = poll_capture(plugin->handle);
+ }
+ ready = snd_pcm_mmap_ready(data->slave, plugin->channel);
+ if (ready < 0)
+ return ready;
+ if (!ready) {
+ if (ctrl->status == SND_PCM_STATUS_PREPARED &&
+ chan->setup.start_mode == SND_PCM_START_FULL) {
+ err = snd_pcm_channel_go(data->slave, plugin->channel);
if (err < 0)
return err;
}
- break;
- case SND_PCM_STATUS_OVERRUN:
- return -EPIPE;
- default:
- return -EIO;
+ if (ctrl->status != SND_PCM_STATUS_RUNNING)
+ return -EPIPE;
+ if (chan->mode & SND_PCM_NONBLOCK)
+ return -EAGAIN;
+ pfd.fd = snd_pcm_file_descriptor(plugin->handle, plugin->channel);
+ pfd.events = POLLIN | POLLERR;
+ ready = poll(&pfd, 1, 10000);
+ if (ready < 0)
+ return ready;
+ if (ready && pfd.revents & POLLERR)
+ return -EPIPE;
+ assert(snd_pcm_mmap_ready(data->slave, plugin->channel));
}
- return 0;
-}
-static int mmap_src_voices(snd_pcm_plugin_t *plugin,
- snd_pcm_plugin_voice_t **voices,
- size_t samples)
-{
- mmap_t *data;
- int err;
- int voice;
- snd_pcm_plugin_voice_t *dv, *sv;
+ frag = ctrl->frag_data;
+ f = frag % setup->frags;
- if (plugin == NULL || voices == NULL)
- return -EINVAL;
- data = (mmap_t *)plugin->extra_data;
- if (data->channel != SND_PCM_CHANNEL_PLAYBACK)
- return -EINVAL;
- if (snd_pcm_plugin_dst_samples_to_size(plugin, samples) != data->frag_size)
- return -EINVAL;
- /* wait until the block is not free */
- while (!playback_ok(plugin)) {
- err = query_playback(plugin, 0);
- if (err < 0)
- return err;
- }
sv = data->voices;
- dv = plugin->voices;
- for (voice = 0; voice < plugin->src_format.voices; ++voice) {
- dv->addr = sv->addr + (sv->step * data->samples_frag_size * data->frag) / 8;
+ dv = plugin->dst_voices;
+ *voices = dv;
+ for (voice = 0; voice < plugin->dst_format.voices; ++voice) {
+ dv->enabled = 1;
+ dv->wanted = 0;
+ dv->aptr = 0;
+ dv->addr = sv->addr + (sv->step * data->samples_frag_size * f) / 8;
dv->first = sv->first;
dv->step = sv->step;
++sv;
++dv;
}
- *voices = plugin->voices;
+ data->frag = frag;
return 0;
}
-static int mmap_dst_voices(snd_pcm_plugin_t *plugin,
- snd_pcm_plugin_voice_t **voices,
- size_t samples)
+static ssize_t mmap_playback_transfer(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_voice_t *src_voices,
+ snd_pcm_plugin_voice_t *dst_voices UNUSED,
+ size_t samples)
{
mmap_t *data;
- int voice;
- snd_pcm_plugin_voice_t *dv, *sv;
+ unsigned int voice;
+ snd_pcm_channel_setup_t *setup;
+ snd_pcm_mmap_control_t *ctrl;
+ struct snd_pcm_chan *chan;
+ unsigned int frag, f;
+ int err;
- if (plugin == NULL || voices == NULL)
+ if (plugin == NULL)
return -EINVAL;
data = (mmap_t *)plugin->extra_data;
- if (data->channel != SND_PCM_CHANNEL_CAPTURE)
+ if (src_voices == NULL)
return -EINVAL;
- if (snd_pcm_plugin_src_samples_to_size(plugin, samples) != data->frag_size)
+ if (plugin->prev == NULL)
return -EINVAL;
- sv = data->voices;
- dv = plugin->voices;
- for (voice = 0; voice < plugin->src_format.voices; ++voice) {
- dv->addr = sv->addr + (sv->step * data->samples_frag_size * data->frag) / 8;
- dv->first = sv->first;
- dv->step = sv->step;
- ++sv;
- ++dv;
+ ctrl = data->control;
+ if (ctrl == NULL)
+ return -EINVAL;
+ chan = &data->slave->chan[SND_PCM_CHANNEL_PLAYBACK];
+ setup = &chan->setup;
+ frag = ctrl->frag_data;
+ if (frag != data->frag)
+ return -EIO;
+ f = frag % setup->frags;
+
+ for (voice = 0; voice < plugin->src_format.voices; voice++) {
+ if (src_voices[voice].enabled)
+ data->silence[voice * setup->frags + f] = 0;
}
- *voices = plugin->voices;
- return 0;
-}
-static ssize_t mmap_transfer(snd_pcm_plugin_t *plugin,
- const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
- size_t samples)
+ frag++;
+ if (frag == setup->frag_boundary) {
+ ctrl->frag_data = 0;
+ ctrl->pos_data = 0;
+ } else {
+ ctrl->frag_data = frag;
+ ctrl->pos_data += setup->frag_size;
+ }
+ if (ctrl->status == SND_PCM_STATUS_PREPARED &&
+ (chan->setup.start_mode == SND_PCM_START_DATA ||
+ (chan->setup.start_mode == SND_PCM_START_FULL &&
+ !snd_pcm_mmap_ready(data->slave, plugin->channel)))) {
+ err = snd_pcm_channel_go(data->slave, plugin->channel);
+ if (err < 0)
+ return err;
+ }
+ return samples;
+}
+
+static ssize_t mmap_capture_transfer(snd_pcm_plugin_t *plugin,
+ const snd_pcm_plugin_voice_t *src_voices UNUSED,
+ snd_pcm_plugin_voice_t *dst_voices UNUSED,
+ size_t samples)
{
mmap_t *data;
- snd_pcm_mmap_control_t *control;
- ssize_t size;
- int err;
+ snd_pcm_channel_setup_t *setup;
+ snd_pcm_mmap_control_t *ctrl;
+ unsigned int frag;
if (plugin == NULL)
return -EINVAL;
data = (mmap_t *)plugin->extra_data;
- control = data->control;
- if (control == NULL)
+ if (plugin->next == NULL)
return -EINVAL;
- if (data->channel == SND_PCM_CHANNEL_PLAYBACK) {
- if (src_voices == NULL)
- return -EINVAL;
- while (!playback_ok(plugin)) {
- err = query_playback(plugin, 0);
- if (err < 0)
- return err;
- }
- size = snd_pcm_plugin_src_samples_to_size(plugin, samples);
- if (size != data->frag_size)
- return -EINVAL;
- if (plugin->prev == NULL) {
- if (plugin->src_format.interleave) {
- void *dst = data->voices[0].addr + data->frag * data->frag_size;
- /* Paranoia: add check for src_voices */
- memcpy(dst, src_voices[0].addr, size);
- } else {
- int voice;
- size /= plugin->src_format.voices;
- for (voice = 0; voice < plugin->src_format.voices; ++voice) {
- void *dst = data->voices[voice].addr + (data->voices[voice].step * data->samples_frag_size * data->frag) / 8;
- /* Paranoia: add check for src_voices */
- memcpy(dst, src_voices[voice].addr, size);
- }
- }
- }
- control->fragments[data->frag].data = 1;
- data->frag++;
- data->frag %= data->frags;
- data->frags_used++;
- return samples;
- } else if (data->channel == SND_PCM_CHANNEL_CAPTURE) {
- if (dst_voices == NULL)
- return -EINVAL;
- while (!capture_ok(plugin)) {
- err = query_capture(plugin, 0);
- if (err < 0)
- return err;
- }
- size = snd_pcm_plugin_dst_samples_to_size(plugin, samples);
- if (size != data->frag_size)
- return -EINVAL;
- if (plugin->next == NULL) {
- if (plugin->dst_format.interleave) {
- void *src = data->voices[0].addr + data->frag * data->frag_size;
- /* Paranoia: add check for dst_voices */
- memcpy(dst_voices[0].addr, src, size);
- } else {
- int voice;
- size /= plugin->src_format.voices;
- for (voice = 0; voice < plugin->src_format.voices; ++voice) {
- void *src = data->voices[voice].addr + (data->voices[voice].step * data->samples_frag_size * data->frag) / 8;
- /* Paranoia: add check for dst_voices */
- memcpy(dst_voices[voice].addr, src, size);
- }
- }
- control->fragments[data->frag].data = 0;
- } else {
- int prev_frag = data->frag - 1;
- if (prev_frag < 0)
- prev_frag = data->frags - 1;
- control->fragments[prev_frag].data = 0;
- }
- data->frag++;
- data->frag %= data->frags;
- data->frags_used--;
- return samples;
- } else {
+
+ ctrl = data->control;
+ if (ctrl == NULL)
return -EINVAL;
+ frag = ctrl->frag_data;
+ if (frag != data->frag)
+ return -EIO;
+ setup = &data->slave->chan[SND_PCM_CHANNEL_CAPTURE].setup;
+
+ /* FIXME: not here the increment */
+ frag++;
+ if (frag == setup->frag_boundary) {
+ ctrl->frag_data = 0;
+ ctrl->pos_data = 0;
+ } else {
+ ctrl->frag_data = frag;
+ ctrl->pos_data += setup->frag_size;
}
+ return samples;
}
static int mmap_action(snd_pcm_plugin_t *plugin,
snd_pcm_plugin_action_t action,
- unsigned long udata)
+ unsigned long udata UNUSED)
{
struct mmap_private_data *data;
return -EINVAL;
data = (mmap_t *)plugin->extra_data;
if (action == INIT) {
- snd_pcm_channel_params_t *params;
- snd_pcm_channel_setup_t setup;
+ snd_pcm_channel_setup_t *setup;
int result;
- int voice;
+ unsigned int voice;
snd_pcm_plugin_voice_t *v;
if (data->control)
- snd_pcm_munmap(plugin->handle, data->channel);
- result = snd_pcm_mmap(plugin->handle, data->channel, &data->control, (void **)&data->buffer);
+ snd_pcm_munmap(data->slave, plugin->channel);
+ result = snd_pcm_mmap(data->slave, plugin->channel, &data->control, (void **)&data->buffer);
if (result < 0)
return result;
- params = (snd_pcm_channel_params_t *)udata;
- data->start_mode = params->start_mode;
- data->stop_mode = params->stop_mode;
- memset(&setup, 0, sizeof(setup));
- setup.channel = data->channel;
- if ((result = snd_pcm_channel_setup(plugin->handle, &setup)) < 0)
- return result;
- data->frags = setup.buf.block.frags;
- data->frag_size = setup.buf.block.frag_size;
- data->samples_frag_size = data->frag_size / snd_pcm_format_size(plugin->src_format.format, plugin->src_format.voices);
- data->frags_min = setup.buf.block.frags_min;
- data->frags_max = setup.buf.block.frags_max;
- if (data->frags_min < 0)
- data->frags_min = 0;
- if (data->frags_min >= setup.buf.block.frags)
- data->frags_min = setup.buf.block.frags - 1;
- if (data->frags_max < 0)
- data->frags_max = setup.buf.block.frags + data->frags_max;
- if (data->frags_max < data->frags_min)
- data->frags_max = data->frags_min;
- if (data->frags_max < 1)
- data->frags_max = 1;
- if (data->frags_max > setup.buf.block.frags)
- data->frags_max = setup.buf.block.frags;
+ setup = &data->slave->chan[plugin->channel].setup;
+ data->samples_frag_size = setup->frag_size / snd_pcm_format_size(setup->format.format, setup->format.voices);
v = data->voices;
- for (voice = 0; voice < plugin->src_format.voices; ++voice) {
+ for (voice = 0; voice < setup->format.voices; ++voice) {
snd_pcm_voice_setup_t vsetup;
vsetup.voice = voice;
- if ((result = snd_pcm_voice_setup(plugin->handle, data->channel, &vsetup)) < 0)
+ if ((result = snd_pcm_voice_setup(data->slave, plugin->channel, &vsetup)) < 0)
return result;
if (vsetup.addr < 0)
return -EBADFD;
v->step = vsetup.step;
v++;
}
+ if (plugin->channel == SND_PCM_CHANNEL_PLAYBACK) {
+ data->silence = malloc(setup->frags * setup->format.voices);
+ memset(data->silence, 0, setup->frags * setup->format.voices);
+ } else
+ data->silence = 0;
return 0;
- } else if (action == PREPARE) {
- data->frag = 0;
- data->lastblock = 0;
- } else if (action == DRAIN && data->channel == SND_PCM_CHANNEL_PLAYBACK) {
- data->frag = 0;
- data->lastblock = 0;
- } else if (action == FLUSH) {
- data->frag = 0;
- data->lastblock = 0;
}
return 0; /* silenty ignore other actions */
}
-static void mmap_free(snd_pcm_plugin_t *plugin, void *private_data)
+static void mmap_free(snd_pcm_plugin_t *plugin, void *private_data UNUSED)
{
struct mmap_private_data *data;
if (plugin == NULL)
return;
data = (mmap_t *)plugin->extra_data;
+ if (data->silence)
+ free(data->silence);
if (data->control)
- snd_pcm_munmap(plugin->handle, data->channel);
+ snd_pcm_munmap(data->slave, plugin->channel);
}
-int snd_pcm_plugin_build_mmap(snd_pcm_t *pcm, int channel,
+int snd_pcm_plugin_build_mmap(snd_pcm_plugin_handle_t *pcm,
+ int channel,
+ snd_pcm_t *slave,
snd_pcm_format_t *format,
snd_pcm_plugin_t **r_plugin)
{
+ int err;
mmap_t *data;
snd_pcm_plugin_t *plugin;
if (r_plugin == NULL)
return -EINVAL;
*r_plugin = NULL;
- if (!pcm || channel < 0 || channel > 1)
+ if (!pcm)
return -EINVAL;
- plugin = snd_pcm_plugin_build(pcm,
- channel == SND_PCM_CHANNEL_PLAYBACK ?
- "I/O mmap playback" :
- "I/O mmap capture",
- format, format,
- sizeof(mmap_t) + sizeof(snd_pcm_plugin_voice_t) * format->voices);
- if (plugin == NULL)
- return -ENOMEM;
+ err = snd_pcm_plugin_build(pcm, channel,
+ "I/O mmap",
+ format, format,
+ sizeof(mmap_t) + sizeof(snd_pcm_plugin_voice_t) * format->voices,
+ &plugin);
+ if (err < 0)
+ return err;
data = (mmap_t *)plugin->extra_data;
- data->channel = channel;
- plugin->src_voices = mmap_src_voices;
- plugin->dst_voices = mmap_dst_voices;
- plugin->transfer = mmap_transfer;
+ data->slave = slave;
+ if (channel == SND_PCM_CHANNEL_PLAYBACK) {
+ plugin->client_voices = mmap_src_voices;
+ plugin->transfer = mmap_playback_transfer;
+ } else {
+ plugin->client_voices = mmap_dst_voices;
+ plugin->transfer = mmap_capture_transfer;
+ }
plugin->action = mmap_action;
plugin->private_free = mmap_free;
*r_plugin = plugin;
typedef void (*mulaw_f)(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
size_t samples);
typedef struct mulaw_private_data {
static void mulaw_decode(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
size_t samples)
{
#define PUT16_LABELS
char *dst;
int src_step, dst_step;
size_t samples1;
- if (src_voices[voice].addr == NULL) {
- if (dst_voices[voice].addr != NULL) {
-// null_voice(&dst_voices[voice]);
- zero_voice(plugin, &dst_voices[voice], samples);
- }
+ if (!src_voices[voice].enabled) {
+ if (dst_voices[voice].wanted)
+ snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples);
+ dst_voices[voice].enabled = 0;
continue;
}
+ dst_voices[voice].enabled = 1;
src = src_voices[voice].addr + src_voices[voice].first / 8;
dst = dst_voices[voice].addr + dst_voices[voice].first / 8;
src_step = src_voices[voice].step / 8;
static void mulaw_encode(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
size_t samples)
{
#define GET16_LABELS
char *dst;
int src_step, dst_step;
size_t samples1;
- if (src_voices[voice].addr == NULL) {
- if (dst_voices[voice].addr != NULL) {
-// null_voice(&dst_voices[voice]);
- zero_voice(plugin, &dst_voices[voice], samples);
- }
+ if (!src_voices[voice].enabled) {
+ if (dst_voices[voice].wanted)
+ snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples);
+ dst_voices[voice].enabled = 0;
continue;
}
+ dst_voices[voice].enabled = 1;
src = src_voices[voice].addr + src_voices[voice].first / 8;
dst = dst_voices[voice].addr + dst_voices[voice].first / 8;
src_step = src_voices[voice].step / 8;
static ssize_t mulaw_transfer(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
size_t samples)
{
mulaw_t *data;
- int voice;
+ unsigned int voice;
if (plugin == NULL || src_voices == NULL || dst_voices == NULL)
return -EFAULT;
- if (samples < 0)
- return -EINVAL;
if (samples == 0)
return 0;
for (voice = 0; voice < plugin->src_format.voices; voice++) {
- if (src_voices[voice].addr != NULL &&
- dst_voices[voice].addr == NULL)
- return -EFAULT;
if (src_voices[voice].first % 8 != 0 ||
src_voices[voice].step % 8 != 0)
return -EINVAL;
}
int snd_pcm_plugin_build_mulaw(snd_pcm_plugin_handle_t *handle,
+ int channel,
snd_pcm_format_t *src_format,
snd_pcm_format_t *dst_format,
snd_pcm_plugin_t **r_plugin)
{
+ int err;
mulaw_t *data;
snd_pcm_plugin_t *plugin;
snd_pcm_format_t *format;
if (!snd_pcm_format_linear(format->format))
return -EINVAL;
- plugin = snd_pcm_plugin_build(handle,
- "Mu-Law<->linear conversion",
- src_format,
- dst_format,
- sizeof(mulaw_t));
- if (plugin == NULL)
- return -ENOMEM;
+ err = snd_pcm_plugin_build(handle, channel,
+ "Mu-Law<->linear conversion",
+ src_format,
+ dst_format,
+ sizeof(mulaw_t),
+ &plugin);
+ if (err < 0)
+ return err;
data = (mulaw_t*)plugin->extra_data;
data->func = func;
data->conv = getput_index(format->format);
#define as_u8(ptr) (*(u_int8_t*)(ptr))
#define as_u16(ptr) (*(u_int16_t*)(ptr))
#define as_u32(ptr) (*(u_int32_t*)(ptr))
+#define as_u64(ptr) (*(u_int64_t*)(ptr))
#define as_s8(ptr) (*(int8_t*)(ptr))
#define as_s16(ptr) (*(int16_t*)(ptr))
#define as_s32(ptr) (*(int32_t*)(ptr))
-
+#define as_s64(ptr) (*(int64_t*)(ptr))
#ifdef COPY_LABELS
-/* src_wid src_endswap sign_toggle dst_wid dst_endswap */
-static void *copy_labels[4 * 2 * 2 * 4 * 2] = {
- &©_xxx1_xxx1, /* 8h -> 8h */
- &©_xxx1_xxx1, /* 8h -> 8s */
- &©_xxx1_xx10, /* 8h -> 16h */
- &©_xxx1_xx01, /* 8h -> 16s */
- &©_xxx1_x100, /* 8h -> 24h */
- &©_xxx1_001x, /* 8h -> 24s */
- &©_xxx1_1000, /* 8h -> 32h */
- &©_xxx1_0001, /* 8h -> 32s */
- &©_xxx1_xxx9, /* 8h ^> 8h */
- &©_xxx1_xxx9, /* 8h ^> 8s */
- &©_xxx1_xx90, /* 8h ^> 16h */
- &©_xxx1_xx09, /* 8h ^> 16s */
- &©_xxx1_x900, /* 8h ^> 24h */
- &©_xxx1_009x, /* 8h ^> 24s */
- &©_xxx1_9000, /* 8h ^> 32h */
- &©_xxx1_0009, /* 8h ^> 32s */
- &©_xxx1_xxx1, /* 8s -> 8h */
- &©_xxx1_xxx1, /* 8s -> 8s */
- &©_xxx1_xx10, /* 8s -> 16h */
- &©_xxx1_xx01, /* 8s -> 16s */
- &©_xxx1_x100, /* 8s -> 24h */
- &©_xxx1_001x, /* 8s -> 24s */
- &©_xxx1_1000, /* 8s -> 32h */
- &©_xxx1_0001, /* 8s -> 32s */
- &©_xxx1_xxx9, /* 8s ^> 8h */
- &©_xxx1_xxx9, /* 8s ^> 8s */
- &©_xxx1_xx90, /* 8s ^> 16h */
- &©_xxx1_xx09, /* 8s ^> 16s */
- &©_xxx1_x900, /* 8s ^> 24h */
- &©_xxx1_009x, /* 8s ^> 24s */
- &©_xxx1_9000, /* 8s ^> 32h */
- &©_xxx1_0009, /* 8s ^> 32s */
- &©_xx12_xxx1, /* 16h -> 8h */
- &©_xx12_xxx1, /* 16h -> 8s */
- &©_xx12_xx12, /* 16h -> 16h */
- &©_xx12_xx21, /* 16h -> 16s */
- &©_xx12_x120, /* 16h -> 24h */
- &©_xx12_021x, /* 16h -> 24s */
- &©_xx12_1200, /* 16h -> 32h */
- &©_xx12_0021, /* 16h -> 32s */
- &©_xx12_xxx9, /* 16h ^> 8h */
- &©_xx12_xxx9, /* 16h ^> 8s */
- &©_xx12_xx92, /* 16h ^> 16h */
- &©_xx12_xx29, /* 16h ^> 16s */
- &©_xx12_x920, /* 16h ^> 24h */
- &©_xx12_029x, /* 16h ^> 24s */
- &©_xx12_9200, /* 16h ^> 32h */
- &©_xx12_0029, /* 16h ^> 32s */
- &©_xx12_xxx2, /* 16s -> 8h */
- &©_xx12_xxx2, /* 16s -> 8s */
- &©_xx12_xx21, /* 16s -> 16h */
- &©_xx12_xx12, /* 16s -> 16s */
- &©_xx12_x210, /* 16s -> 24h */
- &©_xx12_012x, /* 16s -> 24s */
- &©_xx12_2100, /* 16s -> 32h */
- &©_xx12_0012, /* 16s -> 32s */
- &©_xx12_xxxA, /* 16s ^> 8h */
- &©_xx12_xxxA, /* 16s ^> 8s */
- &©_xx12_xxA1, /* 16s ^> 16h */
- &©_xx12_xx1A, /* 16s ^> 16s */
- &©_xx12_xA10, /* 16s ^> 24h */
- &©_xx12_01Ax, /* 16s ^> 24s */
- &©_xx12_A100, /* 16s ^> 32h */
- &©_xx12_001A, /* 16s ^> 32s */
- &©_x123_xxx1, /* 24h -> 8h */
- &©_x123_xxx1, /* 24h -> 8s */
- &©_x123_xx12, /* 24h -> 16h */
- &©_x123_xx21, /* 24h -> 16s */
- &©_x123_x123, /* 24h -> 24h */
- &©_x123_321x, /* 24h -> 24s */
- &©_x123_1230, /* 24h -> 32h */
- &©_x123_0321, /* 24h -> 32s */
- &©_x123_xxx9, /* 24h ^> 8h */
- &©_x123_xxx9, /* 24h ^> 8s */
- &©_x123_xx92, /* 24h ^> 16h */
- &©_x123_xx29, /* 24h ^> 16s */
- &©_x123_x923, /* 24h ^> 24h */
- &©_x123_329x, /* 24h ^> 24s */
- &©_x123_9230, /* 24h ^> 32h */
- &©_x123_0329, /* 24h ^> 32s */
- &©_123x_xxx3, /* 24s -> 8h */
- &©_123x_xxx3, /* 24s -> 8s */
- &©_123x_xx32, /* 24s -> 16h */
- &©_123x_xx23, /* 24s -> 16s */
- &©_123x_x321, /* 24s -> 24h */
- &©_123x_123x, /* 24s -> 24s */
- &©_123x_3210, /* 24s -> 32h */
- &©_123x_0123, /* 24s -> 32s */
- &©_123x_xxxB, /* 24s ^> 8h */
- &©_123x_xxxB, /* 24s ^> 8s */
- &©_123x_xxB2, /* 24s ^> 16h */
- &©_123x_xx2B, /* 24s ^> 16s */
- &©_123x_xB21, /* 24s ^> 24h */
- &©_123x_12Bx, /* 24s ^> 24s */
- &©_123x_B210, /* 24s ^> 32h */
- &©_123x_012B, /* 24s ^> 32s */
- &©_1234_xxx1, /* 32h -> 8h */
- &©_1234_xxx1, /* 32h -> 8s */
- &©_1234_xx12, /* 32h -> 16h */
- &©_1234_xx21, /* 32h -> 16s */
- &©_1234_x123, /* 32h -> 24h */
- &©_1234_321x, /* 32h -> 24s */
- &©_1234_1234, /* 32h -> 32h */
- &©_1234_4321, /* 32h -> 32s */
- &©_1234_xxx9, /* 32h ^> 8h */
- &©_1234_xxx9, /* 32h ^> 8s */
- &©_1234_xx92, /* 32h ^> 16h */
- &©_1234_xx29, /* 32h ^> 16s */
- &©_1234_x923, /* 32h ^> 24h */
- &©_1234_329x, /* 32h ^> 24s */
- &©_1234_9234, /* 32h ^> 32h */
- &©_1234_4329, /* 32h ^> 32s */
- &©_1234_xxx4, /* 32s -> 8h */
- &©_1234_xxx4, /* 32s -> 8s */
- &©_1234_xx43, /* 32s -> 16h */
- &©_1234_xx34, /* 32s -> 16s */
- &©_1234_x432, /* 32s -> 24h */
- &©_1234_234x, /* 32s -> 24s */
- &©_1234_4321, /* 32s -> 32h */
- &©_1234_1234, /* 32s -> 32s */
- &©_1234_xxxC, /* 32s ^> 8h */
- &©_1234_xxxC, /* 32s ^> 8s */
- &©_1234_xxC3, /* 32s ^> 16h */
- &©_1234_xx3C, /* 32s ^> 16s */
- &©_1234_xC32, /* 32s ^> 24h */
- &©_1234_23Cx, /* 32s ^> 24s */
- &©_1234_C321, /* 32s ^> 32h */
- &©_1234_123C, /* 32s ^> 32s */
+static void *copy_labels[4] = {
+ &©_8,
+ &©_16,
+ &©_32,
+ &©_64
};
#endif
#ifdef COPY_END
while(0) {
-copy_xxx1_xxx1: as_u8(dst) = as_u8(src); goto COPY_END;
-copy_xxx1_xx10: as_u16(dst) = (u_int16_t)as_u8(src) << 8; goto COPY_END;
-copy_xxx1_xx01: as_u16(dst) = (u_int16_t)as_u8(src); goto COPY_END;
-copy_xxx1_x100: as_u32(dst) = (u_int32_t)as_u8(src) << 16; goto COPY_END;
-copy_xxx1_001x: as_u32(dst) = (u_int32_t)as_u8(src) << 8; goto COPY_END;
-copy_xxx1_1000: as_u32(dst) = (u_int32_t)as_u8(src) << 24; goto COPY_END;
-copy_xxx1_0001: as_u32(dst) = (u_int32_t)as_u8(src); goto COPY_END;
-copy_xxx1_xxx9: as_u8(dst) = as_u8(src) ^ 0x80; goto COPY_END;
-copy_xxx1_xx90: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto COPY_END;
-copy_xxx1_xx09: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80); goto COPY_END;
-copy_xxx1_x900: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 16; goto COPY_END;
-copy_xxx1_009x: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 8; goto COPY_END;
-copy_xxx1_9000: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto COPY_END;
-copy_xxx1_0009: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80); goto COPY_END;
-copy_xx12_xxx1: as_u8(dst) = as_u16(src) >> 8; goto COPY_END;
-copy_xx12_xx12: as_u16(dst) = as_u16(src); goto COPY_END;
-copy_xx12_xx21: as_u16(dst) = bswap_16(as_u16(src)); goto COPY_END;
-copy_xx12_x120: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto COPY_END;
-copy_xx12_021x: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src)) << 8; goto COPY_END;
-copy_xx12_1200: as_u32(dst) = (u_int32_t)as_u16(src) << 16; goto COPY_END;
-copy_xx12_0021: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src)); goto COPY_END;
-copy_xx12_xxx9: as_u8(dst) = (as_u16(src) >> 8) ^ 0x80; goto COPY_END;
-copy_xx12_xx92: as_u16(dst) = as_u16(src) ^ 0x8000; goto COPY_END;
-copy_xx12_xx29: as_u16(dst) = bswap_16(as_u16(src)) ^ 0x80; goto COPY_END;
-copy_xx12_x920: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 8; goto COPY_END;
-copy_xx12_029x: as_u32(dst) = (u_int32_t)(bswap_16(as_u16(src)) ^ 0x80) << 8; goto COPY_END;
-copy_xx12_9200: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto COPY_END;
-copy_xx12_0029: as_u32(dst) = (u_int32_t)(bswap_16(as_u16(src)) ^ 0x80); goto COPY_END;
-copy_xx12_xxx2: as_u8(dst) = as_u16(src) & 0xff; goto COPY_END;
-copy_xx12_x210: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src)) << 8; goto COPY_END;
-copy_xx12_012x: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto COPY_END;
-copy_xx12_2100: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src)) << 16; goto COPY_END;
-copy_xx12_0012: as_u32(dst) = (u_int32_t)as_u16(src); goto COPY_END;
-copy_xx12_xxxA: as_u8(dst) = (as_u16(src) ^ 0x80) & 0xff; goto COPY_END;
-copy_xx12_xxA1: as_u16(dst) = bswap_16(as_u16(src) ^ 0x80); goto COPY_END;
-copy_xx12_xx1A: as_u16(dst) = as_u16(src) ^ 0x80; goto COPY_END;
-copy_xx12_xA10: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src) ^ 0x80) << 8; goto COPY_END;
-copy_xx12_01Ax: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80) << 8; goto COPY_END;
-copy_xx12_A100: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src) ^ 0x80) << 16; goto COPY_END;
-copy_xx12_001A: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80); goto COPY_END;
-copy_x123_xxx1: as_u8(dst) = as_u32(src) >> 16; goto COPY_END;
-copy_x123_xx12: as_u16(dst) = as_u32(src) >> 8; goto COPY_END;
-copy_x123_xx21: as_u16(dst) = bswap_16(as_u32(src) >> 8); goto COPY_END;
-copy_x123_x123: as_u32(dst) = as_u32(src); goto COPY_END;
-copy_x123_321x: as_u32(dst) = bswap_32(as_u32(src)); goto COPY_END;
-copy_x123_1230: as_u32(dst) = as_u32(src) << 8; goto COPY_END;
-copy_x123_0321: as_u32(dst) = bswap_32(as_u32(src)) >> 8; goto COPY_END;
-copy_x123_xxx9: as_u8(dst) = (as_u32(src) >> 16) ^ 0x80; goto COPY_END;
-copy_x123_xx92: as_u16(dst) = (as_u32(src) >> 8) ^ 0x8000; goto COPY_END;
-copy_x123_xx29: as_u16(dst) = bswap_16(as_u32(src) >> 8) ^ 0x80; goto COPY_END;
-copy_x123_x923: as_u32(dst) = as_u32(src) ^ 0x800000; goto COPY_END;
-copy_x123_329x: as_u32(dst) = bswap_32(as_u32(src)) ^ 0x8000; goto COPY_END;
-copy_x123_9230: as_u32(dst) = (as_u32(src) ^ 0x800000) << 8; goto COPY_END;
-copy_x123_0329: as_u32(dst) = (bswap_32(as_u32(src)) >> 8) ^ 0x80; goto COPY_END;
-copy_123x_xxx3: as_u8(dst) = (as_u32(src) >> 8) & 0xff; goto COPY_END;
-copy_123x_xx32: as_u16(dst) = bswap_16(as_u32(src) >> 8); goto COPY_END;
-copy_123x_xx23: as_u16(dst) = (as_u32(src) >> 8) & 0xffff; goto COPY_END;
-copy_123x_x321: as_u32(dst) = bswap_32(as_u32(src)); goto COPY_END;
-copy_123x_123x: as_u32(dst) = as_u32(src); goto COPY_END;
-copy_123x_3210: as_u32(dst) = bswap_32(as_u32(src)) << 8; goto COPY_END;
-copy_123x_0123: as_u32(dst) = as_u32(src) >> 8; goto COPY_END;
-copy_123x_xxxB: as_u8(dst) = ((as_u32(src) >> 8) & 0xff) ^ 0x80; goto COPY_END;
-copy_123x_xxB2: as_u16(dst) = bswap_16((as_u32(src) >> 8) ^ 0x80); goto COPY_END;
-copy_123x_xx2B: as_u16(dst) = ((as_u32(src) >> 8) & 0xffff) ^ 0x80; goto COPY_END;
-copy_123x_xB21: as_u32(dst) = bswap_32(as_u32(src)) ^ 0x800000; goto COPY_END;
-copy_123x_12Bx: as_u32(dst) = as_u32(src) ^ 0x8000; goto COPY_END;
-copy_123x_B210: as_u32(dst) = bswap_32(as_u32(src) ^ 0x8000) << 8; goto COPY_END;
-copy_123x_012B: as_u32(dst) = (as_u32(src) >> 8) ^ 0x80; goto COPY_END;
-copy_1234_xxx1: as_u8(dst) = as_u32(src) >> 24; goto COPY_END;
-copy_1234_xx12: as_u16(dst) = as_u32(src) >> 16; goto COPY_END;
-copy_1234_xx21: as_u16(dst) = bswap_16(as_u32(src) >> 16); goto COPY_END;
-copy_1234_x123: as_u32(dst) = as_u32(src) >> 8; goto COPY_END;
-copy_1234_321x: as_u32(dst) = bswap_32(as_u32(src)) << 8; goto COPY_END;
-copy_1234_1234: as_u32(dst) = as_u32(src); goto COPY_END;
-copy_1234_4321: as_u32(dst) = bswap_32(as_u32(src)); goto COPY_END;
-copy_1234_xxx9: as_u8(dst) = (as_u32(src) >> 24) ^ 0x80; goto COPY_END;
-copy_1234_xx92: as_u16(dst) = (as_u32(src) >> 16) ^ 0x8000; goto COPY_END;
-copy_1234_xx29: as_u16(dst) = bswap_16(as_u32(src) >> 16) ^ 0x80; goto COPY_END;
-copy_1234_x923: as_u32(dst) = (as_u32(src) >> 8) ^ 0x800000; goto COPY_END;
-copy_1234_329x: as_u32(dst) = (bswap_32(as_u32(src)) ^ 0x80) << 8; goto COPY_END;
-copy_1234_9234: as_u32(dst) = as_u32(src) ^ 0x80000000; goto COPY_END;
-copy_1234_4329: as_u32(dst) = bswap_32(as_u32(src)) ^ 0x80; goto COPY_END;
-copy_1234_xxx4: as_u8(dst) = as_u32(src) & 0xff; goto COPY_END;
-copy_1234_xx43: as_u16(dst) = bswap_16(as_u32(src)); goto COPY_END;
-copy_1234_xx34: as_u16(dst) = as_u32(src) & 0xffff; goto COPY_END;
-copy_1234_x432: as_u32(dst) = bswap_32(as_u32(src)) >> 8; goto COPY_END;
-copy_1234_234x: as_u32(dst) = as_u32(src) << 8; goto COPY_END;
-copy_1234_xxxC: as_u8(dst) = (as_u32(src) & 0xff) ^ 0x80; goto COPY_END;
-copy_1234_xxC3: as_u16(dst) = bswap_16(as_u32(src) ^ 0x80); goto COPY_END;
-copy_1234_xx3C: as_u16(dst) = (as_u32(src) & 0xffff) ^ 0x80; goto COPY_END;
-copy_1234_xC32: as_u32(dst) = (bswap_32(as_u32(src)) >> 8) ^ 0x800000; goto COPY_END;
-copy_1234_23Cx: as_u32(dst) = (as_u32(src) ^ 0x80) << 8; goto COPY_END;
-copy_1234_C321: as_u32(dst) = bswap_32(as_u32(src) ^ 0x80); goto COPY_END;
-copy_1234_123C: as_u32(dst) = as_u32(src) ^ 0x80; goto COPY_END;
+copy_8: as_s8(dst) = as_s8(src); goto COPY_END;
+copy_16: as_s16(dst) = as_s16(src); goto COPY_END;
+copy_32: as_s32(dst) = as_s32(src); goto COPY_END;
+copy_64: as_s64(dst) = as_s64(src); goto COPY_END;
+}
+#endif
+
+#ifdef CONV_LABELS
+/* src_wid src_endswap sign_toggle dst_wid dst_endswap */
+static void *conv_labels[4 * 2 * 2 * 4 * 2] = {
+ &&conv_xxx1_xxx1, /* 8h -> 8h */
+ &&conv_xxx1_xxx1, /* 8h -> 8s */
+ &&conv_xxx1_xx10, /* 8h -> 16h */
+ &&conv_xxx1_xx01, /* 8h -> 16s */
+ &&conv_xxx1_x100, /* 8h -> 24h */
+ &&conv_xxx1_001x, /* 8h -> 24s */
+ &&conv_xxx1_1000, /* 8h -> 32h */
+ &&conv_xxx1_0001, /* 8h -> 32s */
+ &&conv_xxx1_xxx9, /* 8h ^> 8h */
+ &&conv_xxx1_xxx9, /* 8h ^> 8s */
+ &&conv_xxx1_xx90, /* 8h ^> 16h */
+ &&conv_xxx1_xx09, /* 8h ^> 16s */
+ &&conv_xxx1_x900, /* 8h ^> 24h */
+ &&conv_xxx1_009x, /* 8h ^> 24s */
+ &&conv_xxx1_9000, /* 8h ^> 32h */
+ &&conv_xxx1_0009, /* 8h ^> 32s */
+ &&conv_xxx1_xxx1, /* 8s -> 8h */
+ &&conv_xxx1_xxx1, /* 8s -> 8s */
+ &&conv_xxx1_xx10, /* 8s -> 16h */
+ &&conv_xxx1_xx01, /* 8s -> 16s */
+ &&conv_xxx1_x100, /* 8s -> 24h */
+ &&conv_xxx1_001x, /* 8s -> 24s */
+ &&conv_xxx1_1000, /* 8s -> 32h */
+ &&conv_xxx1_0001, /* 8s -> 32s */
+ &&conv_xxx1_xxx9, /* 8s ^> 8h */
+ &&conv_xxx1_xxx9, /* 8s ^> 8s */
+ &&conv_xxx1_xx90, /* 8s ^> 16h */
+ &&conv_xxx1_xx09, /* 8s ^> 16s */
+ &&conv_xxx1_x900, /* 8s ^> 24h */
+ &&conv_xxx1_009x, /* 8s ^> 24s */
+ &&conv_xxx1_9000, /* 8s ^> 32h */
+ &&conv_xxx1_0009, /* 8s ^> 32s */
+ &&conv_xx12_xxx1, /* 16h -> 8h */
+ &&conv_xx12_xxx1, /* 16h -> 8s */
+ &&conv_xx12_xx12, /* 16h -> 16h */
+ &&conv_xx12_xx21, /* 16h -> 16s */
+ &&conv_xx12_x120, /* 16h -> 24h */
+ &&conv_xx12_021x, /* 16h -> 24s */
+ &&conv_xx12_1200, /* 16h -> 32h */
+ &&conv_xx12_0021, /* 16h -> 32s */
+ &&conv_xx12_xxx9, /* 16h ^> 8h */
+ &&conv_xx12_xxx9, /* 16h ^> 8s */
+ &&conv_xx12_xx92, /* 16h ^> 16h */
+ &&conv_xx12_xx29, /* 16h ^> 16s */
+ &&conv_xx12_x920, /* 16h ^> 24h */
+ &&conv_xx12_029x, /* 16h ^> 24s */
+ &&conv_xx12_9200, /* 16h ^> 32h */
+ &&conv_xx12_0029, /* 16h ^> 32s */
+ &&conv_xx12_xxx2, /* 16s -> 8h */
+ &&conv_xx12_xxx2, /* 16s -> 8s */
+ &&conv_xx12_xx21, /* 16s -> 16h */
+ &&conv_xx12_xx12, /* 16s -> 16s */
+ &&conv_xx12_x210, /* 16s -> 24h */
+ &&conv_xx12_012x, /* 16s -> 24s */
+ &&conv_xx12_2100, /* 16s -> 32h */
+ &&conv_xx12_0012, /* 16s -> 32s */
+ &&conv_xx12_xxxA, /* 16s ^> 8h */
+ &&conv_xx12_xxxA, /* 16s ^> 8s */
+ &&conv_xx12_xxA1, /* 16s ^> 16h */
+ &&conv_xx12_xx1A, /* 16s ^> 16s */
+ &&conv_xx12_xA10, /* 16s ^> 24h */
+ &&conv_xx12_01Ax, /* 16s ^> 24s */
+ &&conv_xx12_A100, /* 16s ^> 32h */
+ &&conv_xx12_001A, /* 16s ^> 32s */
+ &&conv_x123_xxx1, /* 24h -> 8h */
+ &&conv_x123_xxx1, /* 24h -> 8s */
+ &&conv_x123_xx12, /* 24h -> 16h */
+ &&conv_x123_xx21, /* 24h -> 16s */
+ &&conv_x123_x123, /* 24h -> 24h */
+ &&conv_x123_321x, /* 24h -> 24s */
+ &&conv_x123_1230, /* 24h -> 32h */
+ &&conv_x123_0321, /* 24h -> 32s */
+ &&conv_x123_xxx9, /* 24h ^> 8h */
+ &&conv_x123_xxx9, /* 24h ^> 8s */
+ &&conv_x123_xx92, /* 24h ^> 16h */
+ &&conv_x123_xx29, /* 24h ^> 16s */
+ &&conv_x123_x923, /* 24h ^> 24h */
+ &&conv_x123_329x, /* 24h ^> 24s */
+ &&conv_x123_9230, /* 24h ^> 32h */
+ &&conv_x123_0329, /* 24h ^> 32s */
+ &&conv_123x_xxx3, /* 24s -> 8h */
+ &&conv_123x_xxx3, /* 24s -> 8s */
+ &&conv_123x_xx32, /* 24s -> 16h */
+ &&conv_123x_xx23, /* 24s -> 16s */
+ &&conv_123x_x321, /* 24s -> 24h */
+ &&conv_123x_123x, /* 24s -> 24s */
+ &&conv_123x_3210, /* 24s -> 32h */
+ &&conv_123x_0123, /* 24s -> 32s */
+ &&conv_123x_xxxB, /* 24s ^> 8h */
+ &&conv_123x_xxxB, /* 24s ^> 8s */
+ &&conv_123x_xxB2, /* 24s ^> 16h */
+ &&conv_123x_xx2B, /* 24s ^> 16s */
+ &&conv_123x_xB21, /* 24s ^> 24h */
+ &&conv_123x_12Bx, /* 24s ^> 24s */
+ &&conv_123x_B210, /* 24s ^> 32h */
+ &&conv_123x_012B, /* 24s ^> 32s */
+ &&conv_1234_xxx1, /* 32h -> 8h */
+ &&conv_1234_xxx1, /* 32h -> 8s */
+ &&conv_1234_xx12, /* 32h -> 16h */
+ &&conv_1234_xx21, /* 32h -> 16s */
+ &&conv_1234_x123, /* 32h -> 24h */
+ &&conv_1234_321x, /* 32h -> 24s */
+ &&conv_1234_1234, /* 32h -> 32h */
+ &&conv_1234_4321, /* 32h -> 32s */
+ &&conv_1234_xxx9, /* 32h ^> 8h */
+ &&conv_1234_xxx9, /* 32h ^> 8s */
+ &&conv_1234_xx92, /* 32h ^> 16h */
+ &&conv_1234_xx29, /* 32h ^> 16s */
+ &&conv_1234_x923, /* 32h ^> 24h */
+ &&conv_1234_329x, /* 32h ^> 24s */
+ &&conv_1234_9234, /* 32h ^> 32h */
+ &&conv_1234_4329, /* 32h ^> 32s */
+ &&conv_1234_xxx4, /* 32s -> 8h */
+ &&conv_1234_xxx4, /* 32s -> 8s */
+ &&conv_1234_xx43, /* 32s -> 16h */
+ &&conv_1234_xx34, /* 32s -> 16s */
+ &&conv_1234_x432, /* 32s -> 24h */
+ &&conv_1234_234x, /* 32s -> 24s */
+ &&conv_1234_4321, /* 32s -> 32h */
+ &&conv_1234_1234, /* 32s -> 32s */
+ &&conv_1234_xxxC, /* 32s ^> 8h */
+ &&conv_1234_xxxC, /* 32s ^> 8s */
+ &&conv_1234_xxC3, /* 32s ^> 16h */
+ &&conv_1234_xx3C, /* 32s ^> 16s */
+ &&conv_1234_xC32, /* 32s ^> 24h */
+ &&conv_1234_23Cx, /* 32s ^> 24s */
+ &&conv_1234_C321, /* 32s ^> 32h */
+ &&conv_1234_123C, /* 32s ^> 32s */
+};
+#endif
+
+#ifdef CONV_END
+while(0) {
+conv_xxx1_xxx1: as_u8(dst) = as_u8(src); goto CONV_END;
+conv_xxx1_xx10: as_u16(dst) = (u_int16_t)as_u8(src) << 8; goto CONV_END;
+conv_xxx1_xx01: as_u16(dst) = (u_int16_t)as_u8(src); goto CONV_END;
+conv_xxx1_x100: as_u32(dst) = (u_int32_t)as_u8(src) << 16; goto CONV_END;
+conv_xxx1_001x: as_u32(dst) = (u_int32_t)as_u8(src) << 8; goto CONV_END;
+conv_xxx1_1000: as_u32(dst) = (u_int32_t)as_u8(src) << 24; goto CONV_END;
+conv_xxx1_0001: as_u32(dst) = (u_int32_t)as_u8(src); goto CONV_END;
+conv_xxx1_xxx9: as_u8(dst) = as_u8(src) ^ 0x80; goto CONV_END;
+conv_xxx1_xx90: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END;
+conv_xxx1_xx09: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80); goto CONV_END;
+conv_xxx1_x900: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 16; goto CONV_END;
+conv_xxx1_009x: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END;
+conv_xxx1_9000: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto CONV_END;
+conv_xxx1_0009: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80); goto CONV_END;
+conv_xx12_xxx1: as_u8(dst) = as_u16(src) >> 8; goto CONV_END;
+conv_xx12_xx12: as_u16(dst) = as_u16(src); goto CONV_END;
+conv_xx12_xx21: as_u16(dst) = bswap_16(as_u16(src)); goto CONV_END;
+conv_xx12_x120: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END;
+conv_xx12_021x: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src)) << 8; goto CONV_END;
+conv_xx12_1200: as_u32(dst) = (u_int32_t)as_u16(src) << 16; goto CONV_END;
+conv_xx12_0021: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src)); goto CONV_END;
+conv_xx12_xxx9: as_u8(dst) = (as_u16(src) >> 8) ^ 0x80; goto CONV_END;
+conv_xx12_xx92: as_u16(dst) = as_u16(src) ^ 0x8000; goto CONV_END;
+conv_xx12_xx29: as_u16(dst) = bswap_16(as_u16(src)) ^ 0x80; goto CONV_END;
+conv_xx12_x920: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 8; goto CONV_END;
+conv_xx12_029x: as_u32(dst) = (u_int32_t)(bswap_16(as_u16(src)) ^ 0x80) << 8; goto CONV_END;
+conv_xx12_9200: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto CONV_END;
+conv_xx12_0029: as_u32(dst) = (u_int32_t)(bswap_16(as_u16(src)) ^ 0x80); goto CONV_END;
+conv_xx12_xxx2: as_u8(dst) = as_u16(src) & 0xff; goto CONV_END;
+conv_xx12_x210: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src)) << 8; goto CONV_END;
+conv_xx12_012x: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END;
+conv_xx12_2100: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src)) << 16; goto CONV_END;
+conv_xx12_0012: as_u32(dst) = (u_int32_t)as_u16(src); goto CONV_END;
+conv_xx12_xxxA: as_u8(dst) = (as_u16(src) ^ 0x80) & 0xff; goto CONV_END;
+conv_xx12_xxA1: as_u16(dst) = bswap_16(as_u16(src) ^ 0x80); goto CONV_END;
+conv_xx12_xx1A: as_u16(dst) = as_u16(src) ^ 0x80; goto CONV_END;
+conv_xx12_xA10: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src) ^ 0x80) << 8; goto CONV_END;
+conv_xx12_01Ax: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80) << 8; goto CONV_END;
+conv_xx12_A100: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src) ^ 0x80) << 16; goto CONV_END;
+conv_xx12_001A: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80); goto CONV_END;
+conv_x123_xxx1: as_u8(dst) = as_u32(src) >> 16; goto CONV_END;
+conv_x123_xx12: as_u16(dst) = as_u32(src) >> 8; goto CONV_END;
+conv_x123_xx21: as_u16(dst) = bswap_16(as_u32(src) >> 8); goto CONV_END;
+conv_x123_x123: as_u32(dst) = as_u32(src); goto CONV_END;
+conv_x123_321x: as_u32(dst) = bswap_32(as_u32(src)); goto CONV_END;
+conv_x123_1230: as_u32(dst) = as_u32(src) << 8; goto CONV_END;
+conv_x123_0321: as_u32(dst) = bswap_32(as_u32(src)) >> 8; goto CONV_END;
+conv_x123_xxx9: as_u8(dst) = (as_u32(src) >> 16) ^ 0x80; goto CONV_END;
+conv_x123_xx92: as_u16(dst) = (as_u32(src) >> 8) ^ 0x8000; goto CONV_END;
+conv_x123_xx29: as_u16(dst) = bswap_16(as_u32(src) >> 8) ^ 0x80; goto CONV_END;
+conv_x123_x923: as_u32(dst) = as_u32(src) ^ 0x800000; goto CONV_END;
+conv_x123_329x: as_u32(dst) = bswap_32(as_u32(src)) ^ 0x8000; goto CONV_END;
+conv_x123_9230: as_u32(dst) = (as_u32(src) ^ 0x800000) << 8; goto CONV_END;
+conv_x123_0329: as_u32(dst) = (bswap_32(as_u32(src)) >> 8) ^ 0x80; goto CONV_END;
+conv_123x_xxx3: as_u8(dst) = (as_u32(src) >> 8) & 0xff; goto CONV_END;
+conv_123x_xx32: as_u16(dst) = bswap_16(as_u32(src) >> 8); goto CONV_END;
+conv_123x_xx23: as_u16(dst) = (as_u32(src) >> 8) & 0xffff; goto CONV_END;
+conv_123x_x321: as_u32(dst) = bswap_32(as_u32(src)); goto CONV_END;
+conv_123x_123x: as_u32(dst) = as_u32(src); goto CONV_END;
+conv_123x_3210: as_u32(dst) = bswap_32(as_u32(src)) << 8; goto CONV_END;
+conv_123x_0123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END;
+conv_123x_xxxB: as_u8(dst) = ((as_u32(src) >> 8) & 0xff) ^ 0x80; goto CONV_END;
+conv_123x_xxB2: as_u16(dst) = bswap_16((as_u32(src) >> 8) ^ 0x80); goto CONV_END;
+conv_123x_xx2B: as_u16(dst) = ((as_u32(src) >> 8) & 0xffff) ^ 0x80; goto CONV_END;
+conv_123x_xB21: as_u32(dst) = bswap_32(as_u32(src)) ^ 0x800000; goto CONV_END;
+conv_123x_12Bx: as_u32(dst) = as_u32(src) ^ 0x8000; goto CONV_END;
+conv_123x_B210: as_u32(dst) = bswap_32(as_u32(src) ^ 0x8000) << 8; goto CONV_END;
+conv_123x_012B: as_u32(dst) = (as_u32(src) >> 8) ^ 0x80; goto CONV_END;
+conv_1234_xxx1: as_u8(dst) = as_u32(src) >> 24; goto CONV_END;
+conv_1234_xx12: as_u16(dst) = as_u32(src) >> 16; goto CONV_END;
+conv_1234_xx21: as_u16(dst) = bswap_16(as_u32(src) >> 16); goto CONV_END;
+conv_1234_x123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END;
+conv_1234_321x: as_u32(dst) = bswap_32(as_u32(src)) << 8; goto CONV_END;
+conv_1234_1234: as_u32(dst) = as_u32(src); goto CONV_END;
+conv_1234_4321: as_u32(dst) = bswap_32(as_u32(src)); goto CONV_END;
+conv_1234_xxx9: as_u8(dst) = (as_u32(src) >> 24) ^ 0x80; goto CONV_END;
+conv_1234_xx92: as_u16(dst) = (as_u32(src) >> 16) ^ 0x8000; goto CONV_END;
+conv_1234_xx29: as_u16(dst) = bswap_16(as_u32(src) >> 16) ^ 0x80; goto CONV_END;
+conv_1234_x923: as_u32(dst) = (as_u32(src) >> 8) ^ 0x800000; goto CONV_END;
+conv_1234_329x: as_u32(dst) = (bswap_32(as_u32(src)) ^ 0x80) << 8; goto CONV_END;
+conv_1234_9234: as_u32(dst) = as_u32(src) ^ 0x80000000; goto CONV_END;
+conv_1234_4329: as_u32(dst) = bswap_32(as_u32(src)) ^ 0x80; goto CONV_END;
+conv_1234_xxx4: as_u8(dst) = as_u32(src) & 0xff; goto CONV_END;
+conv_1234_xx43: as_u16(dst) = bswap_16(as_u32(src)); goto CONV_END;
+conv_1234_xx34: as_u16(dst) = as_u32(src) & 0xffff; goto CONV_END;
+conv_1234_x432: as_u32(dst) = bswap_32(as_u32(src)) >> 8; goto CONV_END;
+conv_1234_234x: as_u32(dst) = as_u32(src) << 8; goto CONV_END;
+conv_1234_xxxC: as_u8(dst) = (as_u32(src) & 0xff) ^ 0x80; goto CONV_END;
+conv_1234_xxC3: as_u16(dst) = bswap_16(as_u32(src) ^ 0x80); goto CONV_END;
+conv_1234_xx3C: as_u16(dst) = (as_u32(src) & 0xffff) ^ 0x80; goto CONV_END;
+conv_1234_xC32: as_u32(dst) = (bswap_32(as_u32(src)) >> 8) ^ 0x800000; goto CONV_END;
+conv_1234_23Cx: as_u32(dst) = (as_u32(src) ^ 0x80) << 8; goto CONV_END;
+conv_1234_C321: as_u32(dst) = bswap_32(as_u32(src) ^ 0x80); goto CONV_END;
+conv_1234_123C: as_u32(dst) = as_u32(src) ^ 0x80; goto CONV_END;
}
#endif
typedef void (*rate_f)(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
int src_samples, int dst_samples);
typedef struct rate_private_data {
static void rate_init(snd_pcm_plugin_t *plugin)
{
- int voice;
+ unsigned int voice;
rate_t *data = (rate_t *)plugin->extra_data;
data->pos = 0;
for (voice = 0; voice < plugin->src_format.voices; voice++) {
static void resample_expand(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
int src_samples, int dst_samples)
{
unsigned int pos = 0;
signed int val;
signed short S1, S2;
char *src, *dst;
- int voice;
+ unsigned int voice;
int src_step, dst_step;
int src_samples1, dst_samples1;
rate_t *data = (rate_t *)plugin->extra_data;
pos = data->pos;
S1 = rvoices->last_S1;
S2 = rvoices->last_S2;
- if (src_voices[voice].addr == NULL) {
- if (dst_voices[voice].addr != NULL) {
-// null_voice(&dst_voices[voice]);
- zero_voice(plugin, &dst_voices[voice], dst_samples);
- }
+ if (!src_voices[voice].enabled) {
+ if (dst_voices[voice].wanted)
+ snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], dst_samples);
+ dst_voices[voice].enabled = 0;
continue;
}
+ dst_voices[voice].enabled = 1;
src = (char *)src_voices[voice].addr + src_voices[voice].first / 8;
dst = (char *)dst_voices[voice].addr + dst_voices[voice].first / 8;
src_step = src_voices[voice].step / 8;
static void resample_shrink(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
int src_samples, int dst_samples)
{
unsigned int pos = 0;
signed int val;
signed short S1, S2;
char *src, *dst;
- int voice;
+ unsigned int voice;
int src_step, dst_step;
int src_samples1, dst_samples1;
rate_t *data = (rate_t *)plugin->extra_data;
pos = data->pos;
S1 = rvoices->last_S1;
S2 = rvoices->last_S2;
- if (src_voices[voice].addr == NULL) {
- if (dst_voices[voice].addr != NULL) {
-// null_voice(&dst_voices[voice]);
- zero_voice(plugin, &dst_voices[voice], dst_samples);
- }
+ if (!src_voices[voice].enabled) {
+ if (dst_voices[voice].wanted)
+ snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], dst_samples);
+ dst_voices[voice].enabled = 0;
continue;
}
+ dst_voices[voice].enabled = 1;
src = (char *)src_voices[voice].addr + src_voices[voice].first / 8;
dst = (char *)dst_voices[voice].addr + dst_voices[voice].first / 8;
src_step = src_voices[voice].step / 8;
static ssize_t rate_transfer(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
size_t samples)
{
size_t dst_samples;
- int voice;
+ unsigned int voice;
rate_t *data;
if (plugin == NULL || src_voices == NULL || dst_voices == NULL)
return -EFAULT;
- if (samples < 0)
- return -EINVAL;
if (samples == 0)
return 0;
for (voice = 0; voice < plugin->src_format.voices; voice++) {
- if (src_voices[voice].addr != NULL &&
- dst_voices[voice].addr == NULL)
- return -EFAULT;
if (src_voices[voice].first % 8 != 0 ||
src_voices[voice].step % 8 != 0)
return -EINVAL;
static int rate_action(snd_pcm_plugin_t *plugin,
snd_pcm_plugin_action_t action,
- unsigned long udata)
+ unsigned long udata UNUSED)
{
if (plugin == NULL)
return -EINVAL;
case FLUSH:
rate_init(plugin);
break;
+ default:
+ break;
}
return 0; /* silenty ignore other actions */
}
int snd_pcm_plugin_build_rate(snd_pcm_plugin_handle_t *handle,
+ int channel,
snd_pcm_format_t *src_format,
snd_pcm_format_t *dst_format,
snd_pcm_plugin_t **r_plugin)
{
+ int err;
rate_t *data;
snd_pcm_plugin_t *plugin;
if (src_format->rate == dst_format->rate)
return -EINVAL;
- plugin = snd_pcm_plugin_build(handle,
- "rate conversion",
- src_format,
- dst_format,
- sizeof(rate_t) + src_format->voices * sizeof(rate_voice_t));
- if (plugin == NULL)
- return -ENOMEM;
+ err = snd_pcm_plugin_build(handle, channel,
+ "rate conversion",
+ src_format,
+ dst_format,
+ sizeof(rate_t) + src_format->voices * sizeof(rate_voice_t),
+ &plugin);
+ if (err < 0)
+ return err;
data = (rate_t *)plugin->extra_data;
data->get = getput_index(src_format->format);
data->put = getput_index(dst_format->format);
/*
* Attenuated route Plug-In
- * Copyright (c) 2000 by Abramo Bagnara <abbagnara@racine.ra.it>
+ * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
*
*
* This library is free software; you can redistribute it and/or modify
#include "../../include/driver.h"
#include "../../include/pcm.h"
#include "../../include/pcm_plugin.h"
-#define my_calloc(size) snd_kmalloc(size, GFP_KERNEL)
-#define my_free(ptr) snd_kfree(ptr)
#else
#include <stdio.h>
#include <stdlib.h>
#include <byteswap.h>
#include <math.h>
#include "../pcm_local.h"
-#define my_calloc(size) calloc(1, size)
-#define my_free(ptr) free(ptr)
#endif
typedef struct ttable_dst ttable_dst_t;
typedef void (*route_voice_f)(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voice,
+ snd_pcm_plugin_voice_t *dst_voice,
ttable_dst_t* ttable, size_t samples);
typedef struct {
struct route_private_data {
enum {INT32=0, INT64=1, FLOAT=2} sum_type;
int get, put;
- int copy;
+ int conv;
int src_sample_size;
ttable_dst_t ttable[0];
};
} sum_t;
-void zero_voice(snd_pcm_plugin_t *plugin,
- const snd_pcm_plugin_voice_t *dst_voice,
- size_t samples)
-{
- char *dst = dst_voice->addr + dst_voice->first / 8;
- int dst_step = dst_voice->step / 8;
- switch (plugin->dst_width) {
- case 4: {
- int dstbit = dst_voice->first % 8;
- int dstbit_step = dst_voice->step % 8;
- while (samples-- > 0) {
- if (dstbit)
- *dst &= 0x0f;
- else
- *dst &= 0xf0;
- dst += dst_step;
- dstbit += dstbit_step;
- if (dstbit == 8) {
- dst++;
- dstbit = 0;
- }
- }
- break;
- }
- case 8:
- while (samples-- > 0) {
- *dst = 0;
- dst += dst_step;
- }
- break;
- case 16:
- while (samples-- > 0) {
- *(int16_t*)dst = 0;
- dst += dst_step;
- }
- break;
- case 32:
- while (samples-- > 0) {
- *(int32_t*)dst = 0;
- dst += dst_step;
- }
- break;
- case 64:
- while (samples-- > 0) {
- *(int64_t*)dst = 0;
- dst += dst_step;
- }
- break;
- }
-}
-
-
static void route_to_voice_zero(snd_pcm_plugin_t *plugin,
- const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voice,
- ttable_dst_t* ttable, size_t samples)
+ const snd_pcm_plugin_voice_t *src_voices UNUSED,
+ snd_pcm_plugin_voice_t *dst_voice,
+ ttable_dst_t* ttable UNUSED, size_t samples)
{
-// null_voice(&dst_voices[voice]);
- zero_voice(plugin, dst_voice, samples);
+ if (dst_voice->wanted)
+ snd_pcm_plugin_silence_voice(plugin, dst_voice, samples);
+ dst_voice->enabled = 0;
}
static void route_to_voice_one(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voice,
+ snd_pcm_plugin_voice_t *dst_voice,
ttable_dst_t* ttable, size_t samples)
{
-#define COPY_LABELS
+#define CONV_LABELS
#include "plugin_ops.h"
-#undef COPY_LABELS
+#undef CONV_LABELS
route_t *data = (route_t *)plugin->extra_data;
- void *copy;
+ void *conv;
const snd_pcm_plugin_voice_t *src_voice = 0;
int srcidx;
char *src, *dst;
return;
}
- copy = copy_labels[data->copy];
+ dst_voice->enabled = 1;
+ conv = conv_labels[data->conv];
src = src_voice->addr + src_voice->first / 8;
src_step = src_voice->step / 8;
dst = dst_voice->addr + dst_voice->first / 8;
dst_step = dst_voice->step / 8;
while (samples-- > 0) {
- goto *copy;
-#define COPY_END after
+ goto *conv;
+#define CONV_END after
#include "plugin_ops.h"
-#undef COPY_END
+#undef CONV_END
after:
src += src_step;
dst += dst_step;
static void route_to_voice(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voice,
+ snd_pcm_plugin_voice_t *dst_voice,
ttable_dst_t* ttable, size_t samples)
{
#define GET_LABELS
int srcidx, srcidx1 = 0;
for (srcidx = 0; srcidx < nsrcs; ++srcidx) {
const snd_pcm_plugin_voice_t *src_voice = &src_voices[ttable->srcs[srcidx].voice];
- if (src_voice->addr == NULL)
+ if (!src_voice->enabled)
continue;
srcs[srcidx1] = src_voice->addr + src_voices->first / 8;
src_steps[srcidx1] = src_voice->step / 8;
return;
}
+ dst_voice->enabled = 1;
zero = zero_labels[data->sum_type];
get = get_labels[data->get];
add = add_labels[data->sum_type * 2 + ttable->att];
}
}
+int route_src_voices_mask(snd_pcm_plugin_t *plugin,
+ bitset_t *dst_vmask,
+ bitset_t **src_vmask)
+{
+ route_t *data = (route_t *)plugin->extra_data;
+ int svoices = plugin->src_format.voices;
+ int dvoices = plugin->dst_format.voices;
+ bitset_t *vmask = plugin->src_vmask;
+ int voice;
+ ttable_dst_t *dp = data->ttable;
+ bitset_zero(vmask, svoices);
+ for (voice = 0; voice < dvoices; voice++, dp++) {
+ int src;
+ ttable_src_t *sp;
+ if (!bitset_get(dst_vmask, voice))
+ continue;
+ sp = dp->srcs;
+ for (src = 0; src < dp->nsrcs; src++, sp++)
+ bitset_set(vmask, sp->voice);
+ }
+ *src_vmask = vmask;
+ return 0;
+}
+
+int route_dst_voices_mask(snd_pcm_plugin_t *plugin,
+ bitset_t *src_vmask,
+ bitset_t **dst_vmask)
+{
+ route_t *data = (route_t *)plugin->extra_data;
+ int dvoices = plugin->dst_format.voices;
+ bitset_t *vmask = plugin->dst_vmask;
+ int voice;
+ ttable_dst_t *dp = data->ttable;
+ bitset_zero(vmask, dvoices);
+ for (voice = 0; voice < dvoices; voice++, dp++) {
+ int src;
+ ttable_src_t *sp;
+ sp = dp->srcs;
+ for (src = 0; src < dp->nsrcs; src++, sp++) {
+ if (bitset_get(src_vmask, sp->voice)) {
+ bitset_set(vmask, voice);
+ break;
+ }
+ }
+ }
+ *dst_vmask = vmask;
+ return 0;
+}
+
#ifdef __KERNEL__
#define FULL ROUTE_PLUGIN_RESOLUTION
typedef int src_ttable_entry_t;
typedef float src_ttable_entry_t;
#endif
-static void route_free(snd_pcm_plugin_t *plugin, void* private_data)
+static void route_free(snd_pcm_plugin_t *plugin, void* private_data UNUSED)
{
route_t *data = (route_t *)plugin->extra_data;
- int dst_voice;
+ unsigned int dst_voice;
for (dst_voice = 0; dst_voice < plugin->dst_format.voices; ++dst_voice) {
if (data->ttable[dst_voice].srcs != NULL)
- my_free(data->ttable[dst_voice].srcs);
+ free(data->ttable[dst_voice].srcs);
}
}
const src_ttable_entry_t* src_ttable)
{
route_t *data;
- int src_voice, dst_voice;
+ unsigned int src_voice, dst_voice;
const src_ttable_entry_t *sptr;
ttable_dst_t *dptr;
if (src_ttable == NULL)
dptr->func = route_to_voice;
break;
}
- dptr->srcs = my_calloc(sizeof(*srcs) * nsrcs);
- memcpy(dptr->srcs, srcs, sizeof(*srcs) * nsrcs);
+ if (nsrcs > 0) {
+ dptr->srcs = calloc(nsrcs, sizeof(*srcs));
+ memcpy(dptr->srcs, srcs, sizeof(*srcs) * nsrcs);
+ } else
+ dptr->srcs = 0;
dptr++;
}
return 0;
static ssize_t route_transfer(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
size_t samples)
{
route_t *data;
int src_nvoices, dst_nvoices;
int src_voice, dst_voice;
ttable_dst_t *ttp;
- const snd_pcm_plugin_voice_t *dvp;
+ snd_pcm_plugin_voice_t *dvp;
if (plugin == NULL || src_voices == NULL || dst_voices == NULL)
return -EFAULT;
- if (samples < 0)
- return -EINVAL;
if (samples == 0)
return 0;
data = (route_t *)plugin->extra_data;
}
int snd_pcm_plugin_build_route(snd_pcm_plugin_handle_t *handle,
+ int channel,
snd_pcm_format_t *src_format,
snd_pcm_format_t *dst_format,
src_ttable_entry_t *ttable,
snd_pcm_format_linear(dst_format->format)))
return -EINVAL;
- plugin = snd_pcm_plugin_build(handle,
- "attenuated route conversion",
- src_format,
- dst_format,
- sizeof(route_t) + sizeof(data->ttable[0]) * dst_format->voices);
- if (plugin == NULL)
- return -ENOMEM;
+ err = snd_pcm_plugin_build(handle, channel,
+ "attenuated route conversion",
+ src_format,
+ dst_format,
+ sizeof(route_t) + sizeof(data->ttable[0]) * dst_format->voices,
+ &plugin);
+ if (err < 0)
+ return err;
data = (route_t *) plugin->extra_data;
data->get = getput_index(src_format->format);
data->put = getput_index(dst_format->format);
- data->copy = copy_index(src_format->format, dst_format->format);
+ data->conv = conv_index(src_format->format, dst_format->format);
#ifdef __KERNEL__
if (snd_pcm_format_width(src_format->format) == 32)
return err;
}
plugin->transfer = route_transfer;
+ plugin->src_voices_mask = route_src_voices_mask;
+ plugin->dst_voices_mask = route_dst_voices_mask;
*r_plugin = plugin;
return 0;
}
*/
typedef struct stream_private_data {
- int channel;
+ snd_pcm_t *slave;
} stream_t;
static ssize_t stream_transfer(snd_pcm_plugin_t *plugin,
const snd_pcm_plugin_voice_t *src_voices,
- const snd_pcm_plugin_voice_t *dst_voices,
+ snd_pcm_plugin_voice_t *dst_voices,
size_t samples)
{
stream_t *data;
if (data == NULL)
return -EINVAL;
vec = (struct iovec *)((char *)data + sizeof(*data));
- if (data->channel == SND_PCM_CHANNEL_PLAYBACK) {
+ if (plugin->channel == SND_PCM_CHANNEL_PLAYBACK) {
if (src_voices == NULL)
return -EINVAL;
if ((result = snd_pcm_plugin_src_samples_to_size(plugin, samples)) < 0)
return result;
+ count = plugin->src_format.voices;
if (plugin->src_format.interleave) {
- result = snd_pcm_write(plugin->handle, src_voices->addr, result);
+ result = snd_pcm_write(data->slave, src_voices->addr, result);
} else {
- count = plugin->src_format.voices;
result /= count;
for (voice = 0; voice < count; voice++) {
- vec[voice].iov_base = src_voices[voice].addr;
+ if (src_voices[voice].enabled)
+ vec[voice].iov_base = src_voices[voice].addr;
+ else
+ vec[voice].iov_base = 0;
vec[voice].iov_len = result;
}
- result = snd_pcm_writev(plugin->handle, vec, count);
+ result = snd_pcm_writev(data->slave, vec, count);
}
if (result < 0)
return result;
return snd_pcm_plugin_src_size_to_samples(plugin, result);
- } else if (data->channel == SND_PCM_CHANNEL_CAPTURE) {
+ } else if (plugin->channel == SND_PCM_CHANNEL_CAPTURE) {
if (dst_voices == NULL)
return -EINVAL;
if ((result = snd_pcm_plugin_dst_samples_to_size(plugin, samples)) < 0)
return result;
+ count = plugin->dst_format.voices;
if (plugin->dst_format.interleave) {
- result = snd_pcm_read(plugin->handle, dst_voices->addr, result);
-
+ result = snd_pcm_read(data->slave, dst_voices->addr, result);
+ for (voice = 0; voice < count; voice++)
+ dst_voices[voice].enabled = src_voices[voice].enabled;
} else {
- count = plugin->dst_format.voices;
result /= count;
for (voice = 0; voice < count; voice++) {
- vec[voice].iov_base = dst_voices[voice].addr;
+ dst_voices[voice].enabled = src_voices[voice].enabled;
+ if (dst_voices[voice].enabled)
+ vec[voice].iov_base = dst_voices[voice].addr;
+ else
+ vec[voice].iov_base = 0;
vec[voice].iov_len = result;
}
- result = snd_pcm_readv(plugin->handle, vec, count);
+ result = snd_pcm_readv(data->slave, vec, count);
}
if (result < 0)
return result;
}
}
-int snd_pcm_plugin_build_stream(snd_pcm_t *pcm, int channel,
+static int stream_src_voices(snd_pcm_plugin_t *plugin,
+ size_t samples,
+ snd_pcm_plugin_voice_t **voices)
+{
+ int err;
+ unsigned int voice;
+ snd_pcm_plugin_voice_t *v;
+ err = snd_pcm_plugin_client_voices(plugin, samples, &v);
+ if (err < 0)
+ return err;
+ *voices = v;
+ for (voice = 0; voice < plugin->src_format.voices; ++voice, ++v)
+ v->wanted = 1;
+ return 0;
+}
+
+int snd_pcm_plugin_build_stream(snd_pcm_plugin_handle_t *pcm,
+ int channel,
+ snd_pcm_t *slave,
snd_pcm_format_t *format,
snd_pcm_plugin_t **r_plugin)
{
+ int err;
stream_t *data;
snd_pcm_plugin_t *plugin;
*r_plugin = NULL;
if (!pcm || channel < 0 || channel > 1)
return -EINVAL;
- plugin = snd_pcm_plugin_build(pcm,
- channel == SND_PCM_CHANNEL_PLAYBACK ?
- "I/O stream playback" :
- "I/O stream capture",
- format, format,
- sizeof(stream_t) + sizeof(struct iovec) * format->voices);
- if (plugin == NULL)
- return -ENOMEM;
+ err = snd_pcm_plugin_build(pcm, channel,
+ "I/O stream",
+ format, format,
+ sizeof(stream_t) + sizeof(struct iovec) * format->voices,
+ &plugin);
+ if (err < 0)
+ return err;
data = (stream_t *)plugin->extra_data;
- data->channel = channel;
+ data->slave = slave;
plugin->transfer = stream_transfer;
+ if (format->interleave && channel == SND_PCM_CHANNEL_PLAYBACK)
+ plugin->client_voices = stream_src_voices;
*r_plugin = plugin;
return 0;
}
{
ssize_t result;
- if (!rmidi || (!buffer && size > 0) || size < 0)
+ if (!rmidi || (!buffer && size > 0))
return -EINVAL;
result = write(rmidi->fd, buffer, size);
if (result < 0)
{
ssize_t result;
- if (!rmidi || (!buffer && size > 0) || size < 0)
+ if (!rmidi || (!buffer && size > 0))
return -EINVAL;
result = read(rmidi->fd, buffer, size);
if (result < 0)
* prototypes
*/
static int snd_seq_free_event_static(snd_seq_event_t *ev);
-static int snd_seq_decode_event(char **buf, int *len, snd_seq_event_t *ev);
+static int snd_seq_decode_event(char **buf, size_t *len, snd_seq_event_t *ev);
/*
/*
* resize output buffer
*/
-int snd_seq_resize_output_buffer(snd_seq_t *seq, int size)
+int snd_seq_resize_output_buffer(snd_seq_t *seq, size_t size)
{
if (!seq || !seq->obuf)
return -EINVAL;
/*
* resize input buffer
*/
-int snd_seq_resize_input_buffer(snd_seq_t *seq, int size)
+int snd_seq_resize_input_buffer(snd_seq_t *seq, size_t size)
{
if (!seq || !seq->ibuf)
return -EINVAL;
/*
* calculates the (encoded) byte-stream size of the event
*/
-int snd_seq_event_length(snd_seq_event_t *ev)
+ssize_t snd_seq_event_length(snd_seq_event_t *ev)
{
- int len = sizeof(snd_seq_event_t);
+ ssize_t len = sizeof(snd_seq_event_t);
if (!ev)
return -EINVAL;
- if (snd_seq_ev_is_variable(ev)) {
- if (ev->data.ext.len < 0)
- return -EINVAL;
+ if (snd_seq_ev_is_variable(ev))
len += ev->data.ext.len;
- }
return len;
}
len = snd_seq_event_length(ev);
if (len < 0)
return -EINVAL;
- if ((seq->obufsize - seq->obufused) < len)
+ if ((seq->obufsize - seq->obufused) < (size_t) len)
return -EAGAIN;
memcpy(seq->obuf + seq->obufused, ev, sizeof(snd_seq_event_t));
seq->obufused += sizeof(snd_seq_event_t);
result = write(seq->fd, seq->obuf, seq->obufused);
if (result < 0)
return -errno;
- if (result < seq->obufused)
+ if ((size_t)result < seq->obufused)
memmove(seq->obuf, seq->obuf + result, seq->obufused - result);
seq->obufused -= result;
}
/*
* decode from byte-stream to an event record
*/
-static int snd_seq_decode_event(char **buf, int *len, snd_seq_event_t *ev)
+static int snd_seq_decode_event(char **buf, size_t *len, snd_seq_event_t *ev)
{
if (!ev || !buf || !*buf || !len )
return -EINVAL;
snd_seq_drain_output_buffer(seq);
} else {
char *ep;
- int len;
+ size_t len;
snd_seq_event_t *ev;
ep = seq->obuf;
- while (ep - seq->obuf < seq->obufused) {
+ while (ep - seq->obuf < (ssize_t)seq->obufused) {
ev = (snd_seq_event_t *) ep;
len = snd_seq_event_length(ev);
int fd;
/* buffers */
char *obuf; /* output buffer */
- int obufsize; /* output buffer size */
- int obufused; /* output buffer used size */
+ size_t obufsize; /* output buffer size */
+ size_t obufused; /* output buffer used size */
char *ibuf; /* input buffer */
- int ibufsize; /* input buffer size */
+ size_t ibufsize; /* input buffer size */
/* input queue */
int cells;
snd_seq_cell_t *head;
ssize_t result;
tmr = handle;
- if (!tmr || (!buffer && size > 0) || size < 0)
+ if (!tmr || (!buffer && size > 0))
return -EINVAL;
result = read(tmr->fd, buffer, size);
if (result < 0)
#endif
#define USED_RATE 48000
-#define LATENCY_LIMIT 8192 /* in bytes */
-#define LOOP_LIMIT (30 * 176400) /* 30 seconds */
+//#define LATENCY_MIN 8192
+#define LATENCY_MIN 32
+#define LATENCY_MAX 8192 /* in bytes */
+//#define LOOP_LIMIT (8192UL * 2)
+#define LOOP_LIMIT (30 * 176400UL) /* 30 seconds */
#if 0
static char *xitoa(int aaa)
bzero(&cinfo, sizeof(cinfo));
pinfo.channel = SND_PCM_CHANNEL_PLAYBACK;
cinfo.channel = SND_PCM_CHANNEL_CAPTURE;
- pinfo.mode = cinfo.mode = SND_PCM_MODE_STREAM;
if ((err = snd_pcm_channel_info(phandle, &pinfo)) < 0) {
printf("Playback info error: %s\n", snd_strerror(err));
exit(0);
params.format.voices = 2;
params.format.rate = USED_RATE;
params.start_mode = SND_PCM_START_GO;
- params.stop_mode = SND_PCM_STOP_STOP;
+ params.xrun_mode = SND_PCM_XRUN_DRAIN;
params.time = 1;
*queue += 16;
#if 0
if (sync)
syncro_id(¶ms.sync);
__again:
- if (*queue > LATENCY_LIMIT)
+ if (*queue > LATENCY_MAX)
return -1;
again = 0;
params.channel = SND_PCM_CHANNEL_PLAYBACK;
+ params.frag_size = *queue;
#ifdef USE_BLOCK_MODE
- params.buf.block.frag_size = *queue;
+ params.buffer_size = *queue * 2;
params.buf.block.frags_min = 1;
- params.buf.block.frags_max = -1;
#else
- params.buf.stream.queue_size = *queue;
+ params.buffer_size = *queue;
+ params.buf.stream.bytes_min = 2;
#endif
- if ((err = snd_pcm_plugin_params(phandle, ¶ms)) < 0) {
+ if ((err = snd_pcm_channel_params(phandle, ¶ms)) < 0) {
printf("Playback params error: %s\n", snd_strerror(err));
exit(0);
}
params.channel = SND_PCM_CHANNEL_CAPTURE;
- if ((err = snd_pcm_plugin_params(chandle, ¶ms)) < 0) {
+ if ((err = snd_pcm_channel_params(chandle, ¶ms)) < 0) {
printf("Capture params error: %s\n", snd_strerror(err));
exit(0);
}
bzero(&psetup, sizeof(psetup));
psetup.channel = SND_PCM_CHANNEL_PLAYBACK;
- if ((err = snd_pcm_plugin_setup(phandle, &psetup)) < 0) {
+ if ((err = snd_pcm_channel_setup(phandle, &psetup)) < 0) {
printf("Playback setup error: %s\n", snd_strerror(err));
exit(0);
}
bzero(&csetup, sizeof(csetup));
csetup.channel = SND_PCM_CHANNEL_CAPTURE;
- if ((err = snd_pcm_plugin_setup(chandle, &csetup)) < 0) {
+ if ((err = snd_pcm_channel_setup(chandle, &csetup)) < 0) {
printf("Capture setup error: %s\n", snd_strerror(err));
exit(0);
}
- if (psetup.buf.stream.queue_size > *queue) {
- *queue = psetup.buf.stream.queue_size;
+#ifdef USE_BLOCK_MODE
+ if (psetup.buffer_size / 2 > *queue) {
+ *queue = psetup.buffer_size / 2;
again++;
}
- if (csetup.buf.stream.queue_size > *queue) {
- *queue = csetup.buf.stream.queue_size;
+#else
+ if (psetup.buffer_size > *queue) {
+ *queue = psetup.buffer_size;
again++;
}
+#endif
if (again)
goto __again;
if ((err = snd_pcm_playback_prepare(phandle)) < 0) {
*queue,
#endif
psetup.format.rate, csetup.format.rate);
+ printf("Fragment boundary = %li/%li, Position boundary = %li/%li\n", (long)psetup.frag_boundary, (long)csetup.frag_boundary, (long)psetup.pos_boundary, (long)psetup.pos_boundary);
+ printf("Frags = %li/%li, Buffer size = %li/%li\n", (long)psetup.frags, (long)csetup.frags, (long)psetup.buffer_size, (long)csetup.buffer_size);
fflush(stdout);
return 0;
}
-void showstat(snd_pcm_t *handle, int channel, snd_pcm_channel_status_t *rstatus)
+void showstat(snd_pcm_t *handle, int channel, snd_pcm_channel_status_t *rstatus, size_t bytes)
{
int err;
snd_pcm_channel_status_t status;
str = channel == SND_PCM_CHANNEL_CAPTURE ? "Capture" : "Playback";
bzero(&status, sizeof(status));
status.channel = channel;
- status.mode = SND_PCM_MODE_STREAM;
- if ((err = snd_pcm_plugin_status(handle, &status)) < 0) {
+ if ((err = snd_pcm_channel_status(handle, &status)) < 0) {
printf("Channel %s status error: %s\n", str, snd_strerror(err));
exit(0);
}
printf("%s:\n", str);
printf(" status = %i\n", status.status);
- printf(" position = %u\n", status.scount);
- printf(" free = %i\n", status.free);
- printf(" count = %i\n", status.count);
+ printf(" bytes = %i\n", bytes);
+ printf(" frag_io = %li\n", (long)status.frag_io);
+ printf(" frag_data = %li\n", (long)status.frag_data);
+ printf(" frag_used = %li\n", (long)status.frags_used);
+ printf(" frag_free = %li\n", (long)status.frags_free);
+ printf(" pos_io = %li\n", (long)status.pos_io);
+ printf(" pos_data = %li\n", (long)status.pos_data);
+ printf(" bytes_used = %li\n", (long)status.bytes_used);
+ printf(" bytes_free = %li\n", (long)status.bytes_free);
if (rstatus)
*rstatus = status;
}
return (t1.tv_sec * 1000000) + l;
}
-long readbuf(snd_pcm_t *handle, char *buf, long len)
+long readbuf(snd_pcm_t *handle, char *buf, long len, size_t *bytes)
{
long r;
do {
- r = snd_pcm_plugin_read(handle, buf, len);
+ r = snd_pcm_read(handle, buf, len);
} while (r == -EAGAIN);
+ if (r > 0)
+ *bytes += r;
// printf("read = %li\n", r);
// showstat(handle, SND_PCM_CHANNEL_CAPTURE, NULL);
return r;
}
-long writebuf(snd_pcm_t *handle, char *buf, long len)
+long writebuf(snd_pcm_t *handle, char *buf, long len, size_t *bytes)
{
long r;
while (len > 0) {
- r = snd_pcm_plugin_write(handle, buf, len);
+ r = snd_pcm_write(handle, buf, len);
#ifndef USE_BLOCK_MODE
if (r == -EAGAIN)
continue;
// showstat(handle, SND_PCM_CHANNEL_PLAYBACK, NULL);
buf += r;
len -= r;
+ *bytes += r;
}
return 0;
}
int main(void)
{
snd_pcm_t *phandle, *chandle;
- char buffer[4096 * 2]; /* max two fragments by 4096 bytes */
+ char buffer[LATENCY_MAX]; /* max two fragments by 4096 bytes */
int pcard = 0, pdevice = 0;
int ccard = 0, cdevice = 0;
- int err, latency = 16;
+ int err, latency = LATENCY_MIN - 16;
int size, ok;
int sync;
snd_pcm_sync_t ssync;
snd_pcm_channel_status_t pstatus, cstatus;
- long r;
+ ssize_t r;
+ size_t bytes_in, bytes_out;
- // latency = 4096 - 16;
setscheduler();
- if ((err = snd_pcm_open(&phandle, pcard, pdevice, SND_PCM_OPEN_PLAYBACK)) < 0) {
+ if ((err = snd_pcm_plug_open(&phandle, pcard, pdevice, SND_PCM_OPEN_PLAYBACK|SND_PCM_NONBLOCK_PLAYBACK)) < 0) {
printf("Playback open error: %s\n", snd_strerror(err));
return 0;
}
- if ((err = snd_pcm_open(&chandle, ccard, cdevice, SND_PCM_OPEN_CAPTURE)) < 0) {
+ if ((err = snd_pcm_plug_open(&chandle, ccard, cdevice, SND_PCM_OPEN_CAPTURE|SND_PCM_NONBLOCK_CAPTURE)) < 0) {
printf("Record open error: %s\n", snd_strerror(err));
return 0;
}
sync = syncro(phandle, chandle);
if (sync)
printf("Using hardware synchronization mode\n");
+ printf("Loop limit is %li bytes\n", LOOP_LIMIT);
while (1) {
+ bytes_in = bytes_out = 0;
if (setparams(phandle, chandle, sync, &latency) < 0)
break;
- memset(buffer, 0, latency);
- if (writebuf(phandle, buffer, latency) < 0)
+ if (snd_pcm_format_set_silence(SND_PCM_SFMT_S16_LE, buffer, latency) < 0) {
+ fprintf(stderr, "silence error\n");
break;
+ }
+ if (writebuf(phandle, buffer, latency, &bytes_out) < 0) {
+ fprintf(stderr, "write error\n");
+ break;
+ }
#ifdef USE_BLOCK_MODE /* at least two fragments MUST BE filled !!! */
- if (writebuf(phandle, buffer, latency) < 0)
+ if (writebuf(phandle, buffer, latency, &bytes_out) < 0)
break;
#endif
if (sync) {
}
ok = 1;
size = 0;
- while (ok && size < LOOP_LIMIT) {
- if ((r = readbuf(chandle, buffer, latency)) < 0)
+ while (ok && bytes_in < LOOP_LIMIT) {
+ if ((r = readbuf(chandle, buffer, latency, &bytes_in)) < 0)
ok = 0;
- if (writebuf(phandle, buffer, r) < 0)
+ if (r > 0 && writebuf(phandle, buffer, r, &bytes_out) < 0)
ok = 0;
- size += r;
}
- showstat(phandle, SND_PCM_CHANNEL_PLAYBACK, &pstatus);
- showstat(chandle, SND_PCM_CHANNEL_CAPTURE, &cstatus);
+ showstat(phandle, SND_PCM_CHANNEL_PLAYBACK, &pstatus, bytes_out);
+ showstat(chandle, SND_PCM_CHANNEL_CAPTURE, &cstatus, bytes_in);
snd_pcm_capture_flush(chandle);
snd_pcm_playback_flush(phandle);
if (ok) {