mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-28 05:40:23 -04:00
Merged pcmplug branch.
This commit is contained in:
parent
40d1ee071d
commit
6962a9d6ce
61 changed files with 7172 additions and 6743 deletions
4
TODO
4
TODO
|
|
@ -1,4 +1,4 @@
|
|||
H solve multi/server mmap'ing problem
|
||||
H add now_ptr stuff and fix appl_ptr seek
|
||||
M add abstraction layer to timer, rawmidi, hwdep, seq
|
||||
M plug sync and pos problems
|
||||
M Loopback implementation?
|
||||
L move OSS emulation to user space? (pseudo device driver and daemon)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ void main(void)
|
|||
#if !defined(SND_PROTOCOL_VERSION) || !defined(SND_PROTOCOL_INCOMPATIBLE)
|
||||
#error not found
|
||||
#else
|
||||
#if !defined(SND_PCM_IOCTL_HW_PTR)
|
||||
#if !defined(SND_PCM_IOCTL_APPL_PTR)
|
||||
#error wrong version
|
||||
#endif
|
||||
exit(0);
|
||||
|
|
|
|||
|
|
@ -19,13 +19,16 @@
|
|||
*/
|
||||
|
||||
|
||||
#define SND_PCM_IOCTL_MMAP_DATA _IO ('A', 0xf0)
|
||||
#define SND_PCM_IOCTL_MMAP_CONTROL _IO ('A', 0xf1)
|
||||
#define SND_PCM_IOCTL_MMAP_STATUS _IO ('A', 0xf2)
|
||||
#define SND_PCM_IOCTL_MUNMAP_DATA _IO ('A', 0xf3)
|
||||
#define SND_PCM_IOCTL_MUNMAP_CONTROL _IO ('A', 0xf4)
|
||||
#define SND_PCM_IOCTL_MUNMAP_STATUS _IO ('A', 0xf5)
|
||||
#define SND_PCM_IOCTL_CLOSE _IO ('A', 0xf6)
|
||||
#define SND_PCM_IOCTL_STATE _IO ('A', 0xf0)
|
||||
#define SND_PCM_IOCTL_MMAP_DATA _IO ('A', 0xf1)
|
||||
#define SND_PCM_IOCTL_MMAP_CONTROL _IO ('A', 0xf2)
|
||||
#define SND_PCM_IOCTL_MMAP_STATUS _IO ('A', 0xf3)
|
||||
#define SND_PCM_IOCTL_MUNMAP_DATA _IO ('A', 0xf4)
|
||||
#define SND_PCM_IOCTL_MUNMAP_CONTROL _IO ('A', 0xf5)
|
||||
#define SND_PCM_IOCTL_MUNMAP_STATUS _IO ('A', 0xf6)
|
||||
#define SND_PCM_IOCTL_MMAP_FORWARD _IOW('A', 0xf7, size_t)
|
||||
#define SND_PCM_IOCTL_AVAIL_UPDATE _IO ('A', 0xf8)
|
||||
#define SND_PCM_IOCTL_CLOSE _IO ('A', 0xf9)
|
||||
|
||||
typedef struct {
|
||||
int result;
|
||||
|
|
@ -36,6 +39,7 @@ typedef struct {
|
|||
snd_pcm_params_info_t params_info;
|
||||
snd_pcm_setup_t setup;
|
||||
snd_pcm_status_t status;
|
||||
ssize_t delay;
|
||||
int pause;
|
||||
snd_pcm_channel_info_t channel_info;
|
||||
snd_pcm_channel_params_t channel_params;
|
||||
|
|
@ -43,10 +47,7 @@ typedef struct {
|
|||
off_t appl_ptr;
|
||||
int hw_ptr;
|
||||
int link;
|
||||
snd_xfer_t read;
|
||||
snd_xfer_t write;
|
||||
snd_xferv_t readv;
|
||||
snd_xferv_t writev;
|
||||
size_t mmap_forward;
|
||||
} u;
|
||||
char data[0];
|
||||
} snd_pcm_client_shm_t;
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ int snd_ctl_client_open(snd_ctl_t **handlep, char *host, int port, int transport
|
|||
snd_ctl_type_t snd_ctl_type(snd_ctl_t *handle);
|
||||
int snd_ctl_open(snd_ctl_t **handle, char *name);
|
||||
int snd_ctl_close(snd_ctl_t *handle);
|
||||
int snd_ctl_file_descriptor(snd_ctl_t *handle);
|
||||
int snd_ctl_poll_descriptor(snd_ctl_t *handle);
|
||||
int snd_ctl_hw_info(snd_ctl_t *handle, snd_ctl_hw_info_t *info);
|
||||
int snd_ctl_clist(snd_ctl_t *handle, snd_control_list_t * list);
|
||||
int snd_ctl_cinfo(snd_ctl_t *handle, snd_control_info_t * sw);
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#ifndef ATTRIBUTE_UNUSED
|
||||
#define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ typedef struct snd_hwdep snd_hwdep_t;
|
|||
|
||||
int snd_hwdep_open(snd_hwdep_t **handle, int card, int device, int mode);
|
||||
int snd_hwdep_close(snd_hwdep_t *handle);
|
||||
int snd_hwdep_file_descriptor(snd_hwdep_t *handle);
|
||||
int snd_hwdep_poll_descriptor(snd_hwdep_t *handle);
|
||||
int snd_hwdep_block_mode(snd_hwdep_t *handle, int enable);
|
||||
int snd_hwdep_info(snd_hwdep_t *handle, snd_hwdep_info_t * info);
|
||||
int snd_hwdep_ioctl(snd_hwdep_t *handle, int request, void * arg);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ extern "C" {
|
|||
|
||||
int snd_mixer_open(snd_mixer_t **handle, char *name);
|
||||
int snd_mixer_close(snd_mixer_t *handle);
|
||||
int snd_mixer_file_descriptor(snd_mixer_t *handle);
|
||||
int snd_mixer_poll_descriptor(snd_mixer_t *handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
258
include/pcm.h
258
include/pcm.h
|
|
@ -98,18 +98,42 @@ static inline size_t bitset_count(bitset_t *bitset, size_t nbits)
|
|||
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_MULTI, SND_PCM_TYPE_CLIENT } snd_pcm_type_t;
|
||||
typedef enum {
|
||||
SND_PCM_TYPE_HW,
|
||||
SND_PCM_TYPE_MULTI,
|
||||
SND_PCM_TYPE_FILE,
|
||||
SND_PCM_TYPE_NULL,
|
||||
SND_PCM_TYPE_CLIENT,
|
||||
SND_PCM_TYPE_LINEAR,
|
||||
SND_PCM_TYPE_ALAW,
|
||||
SND_PCM_TYPE_MULAW,
|
||||
SND_PCM_TYPE_ADPCM,
|
||||
SND_PCM_TYPE_RATE,
|
||||
SND_PCM_TYPE_ROUTE,
|
||||
SND_PCM_TYPE_COPY,
|
||||
SND_PCM_TYPE_PLUG,
|
||||
SND_PCM_TYPE_DROUTE,
|
||||
SND_PCM_TYPE_LBSERVER,
|
||||
} snd_pcm_type_t;
|
||||
|
||||
|
||||
int snd_pcm_open(snd_pcm_t **handle, char *name,
|
||||
int stream, int mode);
|
||||
|
||||
/* Obsolete functions */
|
||||
int snd_pcm_hw_open_subdevice(snd_pcm_t **handle, int card, int device, int subdevice, int stream, int mode);
|
||||
int snd_pcm_hw_open(snd_pcm_t **handle, int card, int device, int stream, int mode);
|
||||
int snd_pcm_hw_open_device(snd_pcm_t **handle, int card, int device, int stream, int mode);
|
||||
int snd_pcm_plug_open_subdevice(snd_pcm_t **handle, int card, int device, int subdevice, int stream, int mode);
|
||||
int snd_pcm_plug_open_device(snd_pcm_t **handle, int card, int device, int stream, int mode);
|
||||
#define snd_pcm_write snd_pcm_writei
|
||||
#define snd_pcm_read snd_pcm_readi
|
||||
ssize_t snd_pcm_writev(snd_pcm_t *handle, const struct iovec *vector, int count);
|
||||
ssize_t snd_pcm_readv(snd_pcm_t *handle, const struct iovec *vector, int count);
|
||||
|
||||
|
||||
snd_pcm_type_t snd_pcm_type(snd_pcm_t *handle);
|
||||
int snd_pcm_close(snd_pcm_t *handle);
|
||||
int snd_pcm_file_descriptor(snd_pcm_t *handle);
|
||||
int snd_pcm_poll_descriptor(snd_pcm_t *handle);
|
||||
int snd_pcm_nonblock(snd_pcm_t *handle, int nonblock);
|
||||
int snd_pcm_info(snd_pcm_t *handle, snd_pcm_info_t *info);
|
||||
int snd_pcm_params_info(snd_pcm_t *handle, snd_pcm_params_info_t *info);
|
||||
|
|
@ -120,65 +144,55 @@ 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_status(snd_pcm_t *handle, snd_pcm_status_t *status);
|
||||
int snd_pcm_prepare(snd_pcm_t *handle);
|
||||
int snd_pcm_go(snd_pcm_t *handle);
|
||||
int snd_pcm_drain(snd_pcm_t *handle);
|
||||
int snd_pcm_start(snd_pcm_t *handle);
|
||||
int snd_pcm_stop(snd_pcm_t *handle);
|
||||
int snd_pcm_flush(snd_pcm_t *handle);
|
||||
int snd_pcm_pause(snd_pcm_t *handle, int enable);
|
||||
int snd_pcm_state(snd_pcm_t *handle);
|
||||
ssize_t snd_pcm_hw_ptr(snd_pcm_t *handle, int update);
|
||||
int snd_pcm_delay(snd_pcm_t *handle, ssize_t *delayp);
|
||||
size_t snd_pcm_hw_ptr(snd_pcm_t *handle);
|
||||
ssize_t snd_pcm_appl_ptr(snd_pcm_t *handle, off_t offset);
|
||||
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 *handle, const struct iovec *vector, unsigned long count);
|
||||
ssize_t snd_pcm_readv(snd_pcm_t *handle, const struct iovec *vector, unsigned long count);
|
||||
ssize_t snd_pcm_writei(snd_pcm_t *handle, const void *buffer, size_t size);
|
||||
ssize_t snd_pcm_readi(snd_pcm_t *handle, void *buffer, size_t size);
|
||||
ssize_t snd_pcm_writen(snd_pcm_t *handle, void **bufs, size_t size);
|
||||
ssize_t snd_pcm_readn(snd_pcm_t *handle, void **bufs, size_t size);
|
||||
int snd_pcm_dump_setup(snd_pcm_t *handle, FILE *fp);
|
||||
int snd_pcm_dump(snd_pcm_t *handle, FILE *fp);
|
||||
int snd_pcm_dump_status(snd_pcm_status_t *status, FILE *fp);
|
||||
int snd_pcm_link(snd_pcm_t *handle1, snd_pcm_t *handle2);
|
||||
int snd_pcm_unlink(snd_pcm_t *handle);
|
||||
|
||||
int snd_pcm_channels_mask(snd_pcm_t *handle, bitset_t *client_vmask);
|
||||
int snd_pcm_channels_mask(snd_pcm_t *handle, bitset_t *cmask);
|
||||
int snd_pcm_wait(snd_pcm_t *pcm, int timeout);
|
||||
ssize_t snd_pcm_avail_update(snd_pcm_t *pcm);
|
||||
|
||||
|
||||
/* mmap */
|
||||
int snd_pcm_mmap(snd_pcm_t *handle, snd_pcm_mmap_status_t **status, snd_pcm_mmap_control_t **control, void **buffer);
|
||||
int snd_pcm_mmap(snd_pcm_t *handle, void **buffer);
|
||||
int snd_pcm_munmap(snd_pcm_t *handle);
|
||||
int snd_pcm_mmap_state(snd_pcm_t *handle);
|
||||
ssize_t snd_pcm_mmap_hw_ptr(snd_pcm_t *handle);
|
||||
ssize_t snd_pcm_mmap_appl_ptr(snd_pcm_t *handle, off_t offset);
|
||||
int snd_pcm_mmap_status(snd_pcm_t *handle, snd_pcm_mmap_status_t **status);
|
||||
int snd_pcm_mmap_control(snd_pcm_t *handle, snd_pcm_mmap_control_t **control);
|
||||
int snd_pcm_mmap_data(snd_pcm_t *handle, void **buffer);
|
||||
int snd_pcm_munmap_status(snd_pcm_t *handle);
|
||||
int snd_pcm_munmap_control(snd_pcm_t *handle);
|
||||
int snd_pcm_munmap_data(snd_pcm_t *handle);
|
||||
int snd_pcm_mmap_ready(snd_pcm_t *handle);
|
||||
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 *handle, const struct iovec *vector, unsigned long count);
|
||||
ssize_t snd_pcm_mmap_readv(snd_pcm_t *handle, const struct iovec *vector, unsigned long count);
|
||||
int snd_pcm_mmap_avail(snd_pcm_t *handle, ssize_t *frames);
|
||||
ssize_t snd_pcm_mmap_xfer(snd_pcm_t *handle, size_t frames);
|
||||
ssize_t snd_pcm_mmap_offset(snd_pcm_t *handle);
|
||||
ssize_t snd_pcm_mmap_write_areas(snd_pcm_t *handle, snd_pcm_channel_area_t *channels, size_t frames);
|
||||
ssize_t snd_pcm_mmap_write_frames(snd_pcm_t *handle, const void *buffer, size_t frames);
|
||||
ssize_t snd_pcm_mmap_read_areas(snd_pcm_t *handle, snd_pcm_channel_area_t *channels, size_t frames);
|
||||
ssize_t snd_pcm_mmap_read_frames(snd_pcm_t *handle, const void *buffer, size_t frames);
|
||||
int snd_pcm_mmap_get_areas(snd_pcm_t *handle, snd_pcm_channel_area_t *areas);
|
||||
|
||||
ssize_t snd_pcm_mmap_forward(snd_pcm_t *pcm, size_t size);
|
||||
size_t snd_pcm_mmap_offset(snd_pcm_t *pcm);
|
||||
size_t snd_pcm_mmap_xfer(snd_pcm_t *pcm, size_t size);
|
||||
ssize_t snd_pcm_mmap_writei(snd_pcm_t *handle, const void *buffer, size_t size);
|
||||
ssize_t snd_pcm_mmap_readi(snd_pcm_t *handle, void *buffer, size_t size);
|
||||
ssize_t snd_pcm_mmap_writen(snd_pcm_t *handle, void **bufs, size_t size);
|
||||
ssize_t snd_pcm_mmap_readn(snd_pcm_t *handle, void **bufs, size_t size);
|
||||
|
||||
const char *snd_pcm_format_name(int format);
|
||||
const char *snd_pcm_format_description(int format);
|
||||
int snd_pcm_format_value(const char* name);
|
||||
|
||||
int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_channel, size_t dst_offset,
|
||||
int snd_pcm_area_silence(snd_pcm_channel_area_t *dst_channel, size_t dst_offset,
|
||||
size_t samples, int format);
|
||||
int snd_pcm_areas_silence(const snd_pcm_channel_area_t *dst_channels, size_t dst_offset,
|
||||
int snd_pcm_areas_silence(snd_pcm_channel_area_t *dst_channels, size_t dst_offset,
|
||||
size_t vcount, size_t frames, int format);
|
||||
int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_channel, size_t src_offset,
|
||||
const snd_pcm_channel_area_t *dst_channel, size_t dst_offset,
|
||||
int snd_pcm_area_copy(snd_pcm_channel_area_t *src_channel, size_t src_offset,
|
||||
snd_pcm_channel_area_t *dst_channel, size_t dst_offset,
|
||||
size_t samples, int format);
|
||||
int snd_pcm_areas_copy(const snd_pcm_channel_area_t *src_channels, size_t src_offset,
|
||||
const snd_pcm_channel_area_t *dst_channels, size_t dst_offset,
|
||||
size_t vcount, size_t frames, int format);
|
||||
int snd_pcm_areas_copy(snd_pcm_channel_area_t *src_channels, size_t src_offset,
|
||||
snd_pcm_channel_area_t *dst_channels, size_t dst_offset,
|
||||
size_t channels, size_t frames, int format);
|
||||
|
||||
ssize_t snd_pcm_bytes_to_frames(snd_pcm_t *pcm, ssize_t bytes);
|
||||
ssize_t snd_pcm_frames_to_bytes(snd_pcm_t *pcm, ssize_t frames);
|
||||
|
|
@ -207,165 +221,3 @@ ssize_t snd_pcm_format_set_silence(int format, void *buf, size_t count);
|
|||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* PCM Plug-In interface
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct snd_stru_pcm_plugin snd_pcm_plugin_t;
|
||||
#define snd_pcm_plug_t struct snd_pcm_plug
|
||||
|
||||
typedef enum {
|
||||
INIT = 0,
|
||||
PREPARE = 1,
|
||||
DRAIN = 2,
|
||||
FLUSH = 3,
|
||||
PAUSE = 4,
|
||||
} snd_pcm_plugin_action_t;
|
||||
|
||||
typedef struct snd_stru_pcm_plugin_channel {
|
||||
snd_pcm_channel_area_t area;
|
||||
unsigned int enabled:1; /* channel need to be processed */
|
||||
unsigned int wanted:1; /* channel is wanted */
|
||||
} snd_pcm_plugin_channel_t;
|
||||
|
||||
struct snd_stru_pcm_plugin {
|
||||
char *name; /* plug-in name */
|
||||
int stream;
|
||||
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_frames)(snd_pcm_plugin_t *plugin, size_t dst_frames);
|
||||
ssize_t (*dst_frames)(snd_pcm_plugin_t *plugin, size_t src_frames);
|
||||
ssize_t (*client_channels)(snd_pcm_plugin_t *plugin,
|
||||
size_t frames,
|
||||
snd_pcm_plugin_channel_t **channels);
|
||||
int (*src_channels_mask)(snd_pcm_plugin_t *plugin,
|
||||
bitset_t *dst_vmask,
|
||||
bitset_t **src_vmask);
|
||||
int (*dst_channels_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_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
size_t frames);
|
||||
int (*action)(snd_pcm_plugin_t *plugin,
|
||||
snd_pcm_plugin_action_t action,
|
||||
unsigned long data);
|
||||
int (*parameter_set)(snd_pcm_plugin_t *plugin,
|
||||
const char *name,
|
||||
unsigned long value);
|
||||
int (*parameter_get)(snd_pcm_plugin_t *plugin,
|
||||
const char *name,
|
||||
unsigned long *value);
|
||||
void (*dump)(snd_pcm_plugin_t *plugin, FILE *fp);
|
||||
snd_pcm_plugin_t *prev;
|
||||
snd_pcm_plugin_t *next;
|
||||
snd_pcm_plug_t *plug;
|
||||
void *private_data;
|
||||
void (*private_free)(snd_pcm_plugin_t *plugin);
|
||||
char *buf;
|
||||
size_t buf_frames;
|
||||
snd_pcm_plugin_channel_t *buf_channels;
|
||||
bitset_t *src_vmask;
|
||||
bitset_t *dst_vmask;
|
||||
char extra_data[0];
|
||||
};
|
||||
|
||||
int snd_pcm_plug_create(snd_pcm_t **handle, snd_pcm_t *slave, int close_slave);
|
||||
int snd_pcm_plug_open_subdevice(snd_pcm_t **handle, int card, int device, int subdevice, int stream, int mode);
|
||||
int snd_pcm_plug_open(snd_pcm_t **handle, int card, int device, int stream, int mode);
|
||||
|
||||
int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin);
|
||||
int snd_pcm_plugin_insert(snd_pcm_plugin_t *plugin);
|
||||
int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin);
|
||||
void snd_pcm_plugin_dump(snd_pcm_plugin_t *plugin, FILE *fp);
|
||||
int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, size_t frames);
|
||||
int snd_pcm_plug_clear(snd_pcm_plug_t *plug);
|
||||
snd_pcm_plugin_t *snd_pcm_plug_first(snd_pcm_plug_t *plug);
|
||||
snd_pcm_plugin_t *snd_pcm_plug_last(snd_pcm_plug_t *plug);
|
||||
int snd_pcm_plug_direct(snd_pcm_plug_t *plug);
|
||||
ssize_t snd_pcm_plug_client_size(snd_pcm_plug_t *plug, size_t drv_frames);
|
||||
ssize_t snd_pcm_plug_slave_size(snd_pcm_plug_t *plug, size_t clt_frames);
|
||||
|
||||
/*
|
||||
* Plug-In constructors
|
||||
*/
|
||||
|
||||
int snd_pcm_plugin_build(snd_pcm_plug_t *plug,
|
||||
const char *name,
|
||||
snd_pcm_format_t *src_format,
|
||||
snd_pcm_format_t *dst_format,
|
||||
size_t extra,
|
||||
snd_pcm_plugin_t **ret);
|
||||
/* basic I/O */
|
||||
int snd_pcm_plugin_build_io(snd_pcm_plug_t *plug,
|
||||
snd_pcm_format_t *format,
|
||||
snd_pcm_plugin_t **r_plugin);
|
||||
int snd_pcm_plugin_build_mmap(snd_pcm_plug_t *plug,
|
||||
snd_pcm_format_t *format,
|
||||
snd_pcm_plugin_t **r_plugin);
|
||||
|
||||
#define ROUTE_PLUGIN_USE_FLOAT 1
|
||||
#if ROUTE_PLUGIN_USE_FLOAT
|
||||
#define FULL 1.0
|
||||
#define HALF 0.5
|
||||
typedef float route_ttable_entry_t;
|
||||
#else
|
||||
#define FULL ROUTE_PLUGIN_RESOLUTION
|
||||
#define HALF ROUTE_PLUGIN_RESOLUTION / 2
|
||||
typedef int route_ttable_entry_t;
|
||||
#endif
|
||||
|
||||
/* conversion plugins */
|
||||
int snd_pcm_plugin_build_interleave(snd_pcm_plug_t *plug,
|
||||
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_plug_t *plug,
|
||||
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_plug_t *plug,
|
||||
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_plug_t *plug,
|
||||
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_plug_t *plug,
|
||||
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_plug_t *plug,
|
||||
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_plug_t *plug,
|
||||
snd_pcm_format_t *src_format,
|
||||
snd_pcm_format_t *dst_format,
|
||||
route_ttable_entry_t *ttable,
|
||||
snd_pcm_plugin_t **r_plugin);
|
||||
int snd_pcm_plugin_build_copy(snd_pcm_plug_t *plug,
|
||||
snd_pcm_format_t *src_format,
|
||||
snd_pcm_format_t *dst_format,
|
||||
snd_pcm_plugin_t **r_plugin);
|
||||
|
||||
int snd_pcm_multi_create(snd_pcm_t **handlep, size_t slaves_count,
|
||||
snd_pcm_t **slaves_handle, size_t *slaves_channels_count,
|
||||
size_t binds_count, unsigned int *binds_client_channel,
|
||||
unsigned int *binds_slave, unsigned int *binds_slave_channel,
|
||||
int close_slaves);
|
||||
|
||||
int snd_pcm_client_create(snd_pcm_t **handlep, char *host, int port, int transport, char *name, int stream, int mode);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ typedef struct snd_rawmidi snd_rawmidi_t;
|
|||
|
||||
int snd_rawmidi_open(snd_rawmidi_t **handle, int card, int device, int mode);
|
||||
int snd_rawmidi_close(snd_rawmidi_t *handle);
|
||||
int snd_rawmidi_file_descriptor(snd_rawmidi_t *handle);
|
||||
int snd_rawmidi_poll_descriptor(snd_rawmidi_t *handle);
|
||||
int snd_rawmidi_block_mode(snd_rawmidi_t *handle, int enable);
|
||||
int snd_rawmidi_info(snd_rawmidi_t *handle, snd_rawmidi_info_t * info);
|
||||
int snd_rawmidi_stream_params(snd_rawmidi_t *handle, snd_rawmidi_params_t * params);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ typedef struct snd_seq snd_seq_t;
|
|||
|
||||
int snd_seq_open(snd_seq_t **handle, int mode);
|
||||
int snd_seq_close(snd_seq_t *handle);
|
||||
int snd_seq_file_descriptor(snd_seq_t *handle);
|
||||
int snd_seq_poll_descriptor(snd_seq_t *handle);
|
||||
int snd_seq_block_mode(snd_seq_t *handle, int enable);
|
||||
int snd_seq_client_id(snd_seq_t *handle);
|
||||
int snd_seq_output_buffer_size(snd_seq_t *handle);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ typedef struct snd_timer snd_timer_t;
|
|||
|
||||
int snd_timer_open(snd_timer_t **handle);
|
||||
int snd_timer_close(snd_timer_t *handle);
|
||||
int snd_timer_file_descriptor(snd_timer_t *handle);
|
||||
int snd_timer_poll_descriptor(snd_timer_t *handle);
|
||||
int snd_timer_general_info(snd_timer_t *handle, snd_timer_general_info_t * info);
|
||||
int snd_timer_select(snd_timer_t *handle, snd_timer_select_t *tselect);
|
||||
int snd_timer_info(snd_timer_t *handle, snd_timer_info_t *timer);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ 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 \
|
||||
compat/libcompat.la conf/libconf.la
|
||||
compat/libcompat.la conf/libconf.la -ldl
|
||||
|
||||
libasound_la_LDFLAGS = -version-info $(COMPATNUM)
|
||||
|
||||
|
|
|
|||
|
|
@ -287,7 +287,7 @@ int pcm_shm_open(client_t *client, int *cookie)
|
|||
if (err < 0)
|
||||
return err;
|
||||
client->device.pcm.handle = pcm;
|
||||
client->device.pcm.fd = snd_pcm_file_descriptor(pcm);
|
||||
client->device.pcm.fd = snd_pcm_poll_descriptor(pcm);
|
||||
|
||||
shmid = shmget(IPC_PRIVATE, PCM_SHM_SIZE, 0666);
|
||||
if (shmid < 0) {
|
||||
|
|
@ -368,20 +368,26 @@ int pcm_shm_cmd(client_t *client)
|
|||
case SND_PCM_IOCTL_STATUS:
|
||||
ctrl->result = snd_pcm_status(pcm, &ctrl->u.status);
|
||||
break;
|
||||
case SND_PCM_IOCTL_HW_PTR:
|
||||
ctrl->result = snd_pcm_hw_ptr(pcm, ctrl->u.hw_ptr);
|
||||
case SND_PCM_IOCTL_STATE:
|
||||
ctrl->result = snd_pcm_state(pcm);
|
||||
break;
|
||||
case SND_PCM_IOCTL_DELAY:
|
||||
ctrl->result = snd_pcm_delay(pcm, &ctrl->u.delay);
|
||||
break;
|
||||
case SND_PCM_IOCTL_AVAIL_UPDATE:
|
||||
ctrl->result = snd_pcm_avail_update(pcm);
|
||||
break;
|
||||
case SND_PCM_IOCTL_PREPARE:
|
||||
ctrl->result = snd_pcm_prepare(pcm);
|
||||
break;
|
||||
case SND_PCM_IOCTL_GO:
|
||||
ctrl->result = snd_pcm_go(pcm);
|
||||
case SND_PCM_IOCTL_START:
|
||||
ctrl->result = snd_pcm_start(pcm);
|
||||
break;
|
||||
case SND_PCM_IOCTL_FLUSH:
|
||||
ctrl->result = snd_pcm_flush(pcm);
|
||||
break;
|
||||
case SND_PCM_IOCTL_DRAIN:
|
||||
ctrl->result = snd_pcm_drain(pcm);
|
||||
case SND_PCM_IOCTL_STOP:
|
||||
ctrl->result = snd_pcm_stop(pcm);
|
||||
break;
|
||||
case SND_PCM_IOCTL_PAUSE:
|
||||
ctrl->result = snd_pcm_pause(pcm, ctrl->u.pause);
|
||||
|
|
@ -395,86 +401,6 @@ int pcm_shm_cmd(client_t *client)
|
|||
case SND_PCM_IOCTL_CHANNEL_SETUP:
|
||||
ctrl->result = snd_pcm_channel_setup(pcm, &ctrl->u.channel_setup);
|
||||
break;
|
||||
case SND_PCM_IOCTL_WRITE_FRAMES:
|
||||
{
|
||||
size_t maxsize = PCM_SHM_DATA_MAXLEN;
|
||||
maxsize = snd_pcm_bytes_to_frames(pcm, maxsize);
|
||||
if (ctrl->u.write.count > maxsize) {
|
||||
ctrl->result = -EFAULT;
|
||||
break;
|
||||
}
|
||||
/* FIXME: blocking */
|
||||
ctrl->result = snd_pcm_write(pcm, ctrl->data, ctrl->u.write.count);
|
||||
break;
|
||||
}
|
||||
case SND_PCM_IOCTL_READ_FRAMES:
|
||||
{
|
||||
size_t maxsize = PCM_SHM_DATA_MAXLEN;
|
||||
maxsize = snd_pcm_bytes_to_frames(pcm, maxsize);
|
||||
if (ctrl->u.read.count > maxsize) {
|
||||
ctrl->result = -EFAULT;
|
||||
break;
|
||||
}
|
||||
/* FIXME: blocking */
|
||||
ctrl->result = snd_pcm_read(pcm, ctrl->data, ctrl->u.read.count);
|
||||
break;
|
||||
}
|
||||
case SND_PCM_IOCTL_WRITEV_FRAMES:
|
||||
{
|
||||
/* FIXME: interleaved */
|
||||
size_t maxsize = PCM_SHM_DATA_MAXLEN;
|
||||
unsigned long k;
|
||||
struct iovec *vector;
|
||||
size_t vecsize;
|
||||
char *base;
|
||||
int bits_per_sample = snd_pcm_samples_to_bytes(pcm, 8);
|
||||
vecsize = ctrl->u.writev.count * sizeof(struct iovec);
|
||||
if (vecsize > maxsize) {
|
||||
ctrl->result = -EFAULT;
|
||||
break;
|
||||
}
|
||||
maxsize -= vecsize;
|
||||
vector = (struct iovec *) ctrl->data;
|
||||
base = ctrl->data + vecsize;
|
||||
for (k = 0; k < ctrl->u.writev.count; ++k) {
|
||||
unsigned long ofs = (unsigned long) vector[k].iov_base;
|
||||
size_t len = vector[k].iov_len * bits_per_sample / 8;
|
||||
if (ofs + len > maxsize)
|
||||
return -EFAULT;
|
||||
vector[k].iov_base = base + ofs;
|
||||
}
|
||||
/* FIXME: blocking */
|
||||
ctrl->result = snd_pcm_writev(pcm, vector, ctrl->u.writev.count);
|
||||
break;
|
||||
}
|
||||
case SND_PCM_IOCTL_READV_FRAMES:
|
||||
{
|
||||
/* FIXME: interleaved */
|
||||
size_t maxsize = PCM_SHM_DATA_MAXLEN;
|
||||
unsigned long k;
|
||||
struct iovec *vector;
|
||||
size_t vecsize;
|
||||
char *base;
|
||||
int bits_per_sample = snd_pcm_samples_to_bytes(pcm, 8);
|
||||
vecsize = ctrl->u.readv.count * sizeof(struct iovec);
|
||||
if (vecsize > maxsize) {
|
||||
ctrl->result = -EFAULT;
|
||||
break;
|
||||
}
|
||||
maxsize -= vecsize;
|
||||
vector = (struct iovec *) ctrl->data;
|
||||
base = ctrl->data + vecsize;
|
||||
for (k = 0; k < ctrl->u.readv.count; ++k) {
|
||||
unsigned long ofs = (unsigned long) vector[k].iov_base;
|
||||
size_t len = vector[k].iov_len * bits_per_sample / 8;
|
||||
if (ofs + len > maxsize)
|
||||
return -EFAULT;
|
||||
vector[k].iov_base = base + ofs;
|
||||
}
|
||||
/* FIXME: blocking */
|
||||
ctrl->result = snd_pcm_readv(pcm, vector, ctrl->u.readv.count);
|
||||
break;
|
||||
}
|
||||
case SND_PCM_IOCTL_APPL_PTR:
|
||||
ctrl->result = snd_pcm_appl_ptr(pcm, ctrl->u.appl_ptr);
|
||||
break;
|
||||
|
|
@ -510,13 +436,14 @@ int pcm_shm_cmd(client_t *client)
|
|||
ctrl->result = 0;
|
||||
return 0;
|
||||
}
|
||||
#if 0
|
||||
case SND_PCM_IOCTL_MUNMAP_DATA:
|
||||
case SND_PCM_IOCTL_MUNMAP_CONTROL:
|
||||
case SND_PCM_IOCTL_MUNMAP_STATUS:
|
||||
ctrl->result = 0;
|
||||
break;
|
||||
#endif
|
||||
case SND_PCM_IOCTL_MMAP_FORWARD:
|
||||
ctrl->result = snd_pcm_mmap_forward(pcm, ctrl->u.mmap_forward);
|
||||
break;
|
||||
case SND_PCM_IOCTL_CLOSE:
|
||||
client->ops->close(client);
|
||||
break;
|
||||
|
|
@ -571,7 +498,7 @@ int ctl_shm_open(client_t *client, int *cookie)
|
|||
if (err < 0)
|
||||
return err;
|
||||
client->device.control.handle = ctl;
|
||||
client->device.control.fd = snd_ctl_file_descriptor(ctl);
|
||||
client->device.control.fd = snd_ctl_poll_descriptor(ctl);
|
||||
|
||||
shmid = shmget(IPC_PRIVATE, CTL_SHM_SIZE, 0666);
|
||||
if (shmid < 0) {
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ static int get_quotedchar(input_t *input)
|
|||
}
|
||||
}
|
||||
|
||||
static int get_freestring(char **string, input_t *input)
|
||||
static int get_freestring(char **string, int id, input_t *input)
|
||||
{
|
||||
const size_t bufsize = 256;
|
||||
char _buf[bufsize];
|
||||
|
|
@ -197,13 +197,15 @@ static int get_freestring(char **string, input_t *input)
|
|||
while (1) {
|
||||
c = get_char(input);
|
||||
switch (c) {
|
||||
case '.':
|
||||
if (!id)
|
||||
break;
|
||||
case ' ':
|
||||
case '\f':
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case EOF:
|
||||
case '.':
|
||||
case '=':
|
||||
case '{':
|
||||
case '}':
|
||||
|
|
@ -283,7 +285,7 @@ static int get_delimstring(char **string, int delim, input_t *input)
|
|||
}
|
||||
|
||||
/* Return 0 for free string, 1 for delimited string */
|
||||
static int get_string(char **string, input_t *input)
|
||||
static int get_string(char **string, int id, input_t *input)
|
||||
{
|
||||
int c = get_nonwhite(input);
|
||||
int err;
|
||||
|
|
@ -312,7 +314,7 @@ static int get_string(char **string, input_t *input)
|
|||
return 1;
|
||||
default:
|
||||
unget_char(c, input);
|
||||
err = get_freestring(string, input);
|
||||
err = get_freestring(string, id, input);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
|
|
@ -401,7 +403,7 @@ static int parse_def(snd_config_t *father, input_t *input)
|
|||
#else
|
||||
mode = MERGE;
|
||||
#endif
|
||||
err = get_string(&id, input);
|
||||
err = get_string(&id, 1, input);
|
||||
if (err < 0)
|
||||
return err;
|
||||
c = get_nonwhite(input);
|
||||
|
|
@ -472,7 +474,7 @@ static int parse_def(snd_config_t *father, input_t *input)
|
|||
{
|
||||
char *s;
|
||||
unget_char(c, input);
|
||||
err = get_string(&s, input);
|
||||
err = get_string(&s, 0, input);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (!err && ((s[0] >= '0' && s[0] <= '9') || s[0] == '-')) {
|
||||
|
|
|
|||
|
|
@ -43,10 +43,10 @@ int snd_ctl_close(snd_ctl_t *ctl)
|
|||
return res;
|
||||
}
|
||||
|
||||
int snd_ctl_file_descriptor(snd_ctl_t *ctl)
|
||||
int snd_ctl_poll_descriptor(snd_ctl_t *ctl)
|
||||
{
|
||||
assert(ctl);
|
||||
return ctl->ops->file_descriptor(ctl);
|
||||
return ctl->ops->poll_descriptor(ctl);
|
||||
}
|
||||
|
||||
int snd_ctl_hw_info(snd_ctl_t *ctl, snd_ctl_hw_info_t *info)
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ static int snd_ctl_client_shm_close(snd_ctl_t *ctl)
|
|||
return result;
|
||||
}
|
||||
|
||||
static int snd_ctl_client_file_descriptor(snd_ctl_t *ctl)
|
||||
static int snd_ctl_client_poll_descriptor(snd_ctl_t *ctl)
|
||||
{
|
||||
snd_ctl_client_t *client = ctl->private;
|
||||
return client->data_fd;
|
||||
|
|
@ -255,7 +255,7 @@ static int snd_ctl_client_shm_read(snd_ctl_t *ctl, snd_ctl_event_t *event)
|
|||
|
||||
struct snd_ctl_ops snd_ctl_client_ops = {
|
||||
close: snd_ctl_client_shm_close,
|
||||
file_descriptor: snd_ctl_client_file_descriptor,
|
||||
poll_descriptor: snd_ctl_client_poll_descriptor,
|
||||
hw_info: snd_ctl_client_shm_hw_info,
|
||||
clist: snd_ctl_client_shm_clist,
|
||||
cinfo: snd_ctl_client_shm_cinfo,
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ static int snd_ctl_hw_close(snd_ctl_t *handle)
|
|||
return res;
|
||||
}
|
||||
|
||||
static int snd_ctl_hw_file_descriptor(snd_ctl_t *handle)
|
||||
static int snd_ctl_hw_poll_descriptor(snd_ctl_t *handle)
|
||||
{
|
||||
snd_ctl_hw_t *hw = handle->private;
|
||||
return hw->fd;
|
||||
|
|
@ -134,7 +134,7 @@ static int snd_ctl_hw_read(snd_ctl_t *handle, snd_ctl_event_t *event)
|
|||
|
||||
struct snd_ctl_ops snd_ctl_hw_ops = {
|
||||
close: snd_ctl_hw_close,
|
||||
file_descriptor: snd_ctl_hw_file_descriptor,
|
||||
poll_descriptor: snd_ctl_hw_poll_descriptor,
|
||||
hw_info: snd_ctl_hw_hw_info,
|
||||
clist: snd_ctl_hw_clist,
|
||||
cinfo: snd_ctl_hw_cinfo,
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
struct snd_ctl_ops {
|
||||
int (*close)(snd_ctl_t *handle);
|
||||
int (*file_descriptor)(snd_ctl_t *handle);
|
||||
int (*poll_descriptor)(snd_ctl_t *handle);
|
||||
int (*hw_info)(snd_ctl_t *handle, snd_ctl_hw_info_t *info);
|
||||
int (*clist)(snd_ctl_t *handle, snd_control_list_t *list);
|
||||
int (*cinfo)(snd_ctl_t *handle, snd_control_info_t *info);
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ int snd_hwdep_close(snd_hwdep_t *hwdep)
|
|||
return res;
|
||||
}
|
||||
|
||||
int snd_hwdep_file_descriptor(snd_hwdep_t *hwdep)
|
||||
int snd_hwdep_poll_descriptor(snd_hwdep_t *hwdep)
|
||||
{
|
||||
if (!hwdep)
|
||||
return -EINVAL;
|
||||
|
|
|
|||
|
|
@ -77,11 +77,11 @@ int snd_mixer_close(snd_mixer_t *handle)
|
|||
return err;
|
||||
}
|
||||
|
||||
int snd_mixer_file_descriptor(snd_mixer_t *handle)
|
||||
int snd_mixer_poll_descriptor(snd_mixer_t *handle)
|
||||
{
|
||||
if (handle == NULL || handle->ctl_handle == NULL)
|
||||
return -EIO;
|
||||
return snd_ctl_file_descriptor(handle->ctl_handle);
|
||||
return snd_ctl_poll_descriptor(handle->ctl_handle);
|
||||
}
|
||||
|
||||
const char *snd_mixer_simple_channel_name(int channel)
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
SUBDIRS = plugin
|
||||
|
||||
EXTRA_LTLIBRARIES = libpcm.la
|
||||
|
||||
libpcm_la_SOURCES = pcm.c pcm_hw.c pcm_plug.c pcm_common.c \
|
||||
pcm_misc.c pcm_mmap.c pcm_multi.c pcm_client.c
|
||||
libpcm_la_LIBADD = plugin/libpcmplugin.la
|
||||
noinst_HEADERS = pcm_local.h
|
||||
libpcm_la_SOURCES = pcm.c pcm_hw.c pcm_plugin.c pcm_linear.c pcm_route.c \
|
||||
pcm_mulaw.c pcm_alaw.c pcm_adpcm.c pcm_rate.c pcm_plug.c \
|
||||
pcm_misc.c pcm_mmap.c pcm_multi.c pcm_client.c pcm_file.c
|
||||
noinst_HEADERS = pcm_local.h pcm_plugin.h
|
||||
|
||||
all: libpcm.la
|
||||
|
||||
plugin/libpcmplugin.la:
|
||||
$(MAKE) -C plugin libpcmplugin.la
|
||||
|
||||
INCLUDES=-I$(top_srcdir)/include
|
||||
|
|
|
|||
1308
src/pcm/pcm.c
1308
src/pcm/pcm.c
File diff suppressed because it is too large
Load diff
652
src/pcm/pcm_adpcm.c
Normal file
652
src/pcm/pcm_adpcm.c
Normal file
|
|
@ -0,0 +1,652 @@
|
|||
/*
|
||||
* PCM - Ima-ADPC conversion
|
||||
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
|
||||
* Copyright (c) 1999 by Uros Bizjak <uros@kss-loka.si>
|
||||
* Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
* Based on Version 1.2, 18-Dec-92 implementation of Intel/DVI ADPCM code
|
||||
* by Jack Jansen, CWI, Amsterdam <Jack.Jansen@cwi.nl>, Copyright 1992
|
||||
* by Stichting Mathematisch Centrum, Amsterdam, The Netherlands.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
These routines convert 16 bit linear PCM samples to 4 bit ADPCM code
|
||||
and vice versa. The ADPCM code used is the Intel/DVI ADPCM code which
|
||||
is being recommended by the IMA Digital Audio Technical Working Group.
|
||||
|
||||
The algorithm for this coder was taken from:
|
||||
Proposal for Standardized Audio Interstreamge Formats,
|
||||
IMA compatability project proceedings, Vol 2, Issue 2, May 1992.
|
||||
|
||||
- No, this is *not* a G.721 coder/decoder. The algorithm used by G.721
|
||||
is very complicated, requiring oodles of floating-point ops per
|
||||
sample (resulting in very poor performance). I have not done any
|
||||
tests myself but various people have assured my that 721 quality is
|
||||
actually lower than DVI quality.
|
||||
|
||||
- No, it probably isn't a RIFF ADPCM decoder either. Trying to decode
|
||||
RIFF ADPCM with these routines seems to result in something
|
||||
recognizable but very distorted.
|
||||
|
||||
- No, it is not a CDROM-XA coder either, as far as I know. I haven't
|
||||
come across a good description of XA yet.
|
||||
*/
|
||||
|
||||
#include <byteswap.h>
|
||||
#include "pcm_local.h"
|
||||
#include "pcm_plugin.h"
|
||||
|
||||
typedef struct {
|
||||
int pred_val; /* Calculated predicted value */
|
||||
int step_idx; /* Previous StepSize lookup index */
|
||||
} adpcm_state_t;
|
||||
|
||||
typedef void (*adpcm_f)(snd_pcm_channel_area_t *src_areas,
|
||||
size_t src_offset,
|
||||
snd_pcm_channel_area_t *dst_areas,
|
||||
size_t dst_offset,
|
||||
size_t frames, size_t channels, int getputidx,
|
||||
adpcm_state_t *states);
|
||||
|
||||
typedef struct {
|
||||
/* This field need to be the first */
|
||||
snd_pcm_plugin_t plug;
|
||||
int getput_idx;
|
||||
adpcm_f func;
|
||||
int sformat;
|
||||
int cformat;
|
||||
int cxfer_mode, cmmap_shape;
|
||||
adpcm_state_t *states;
|
||||
} snd_pcm_adpcm_t;
|
||||
|
||||
/* First table lookup for Ima-ADPCM quantizer */
|
||||
static char IndexAdjust[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };
|
||||
|
||||
/* Second table lookup for Ima-ADPCM quantizer */
|
||||
static short StepSize[89] = {
|
||||
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
|
||||
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
|
||||
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
|
||||
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
|
||||
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
|
||||
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
|
||||
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
|
||||
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
|
||||
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
|
||||
};
|
||||
|
||||
static char adpcm_encoder(int sl, adpcm_state_t * state)
|
||||
{
|
||||
short diff; /* Difference between sl and predicted sample */
|
||||
short pred_diff; /* Predicted difference to next sample */
|
||||
|
||||
unsigned char sign; /* sign of diff */
|
||||
short step; /* holds previous StepSize value */
|
||||
unsigned char adjust_idx; /* Index to IndexAdjust lookup table */
|
||||
|
||||
int i;
|
||||
|
||||
/* Compute difference to previous predicted value */
|
||||
diff = sl - state->pred_val;
|
||||
sign = (diff < 0) ? 0x8 : 0x0;
|
||||
if (sign) {
|
||||
diff = -diff;
|
||||
}
|
||||
|
||||
/*
|
||||
* This code *approximately* computes:
|
||||
* adjust_idx = diff * 4 / step;
|
||||
* pred_diff = (adjust_idx + 0.5) * step / 4;
|
||||
*
|
||||
* But in shift step bits are dropped. The net result of this is
|
||||
* that even if you have fast mul/div hardware you cannot put it to
|
||||
* good use since the fixup would be too expensive.
|
||||
*/
|
||||
|
||||
step = StepSize[state->step_idx];
|
||||
|
||||
/* Divide and clamp */
|
||||
pred_diff = step >> 3;
|
||||
for (adjust_idx = 0, i = 0x4; i; i >>= 1, step >>= 1) {
|
||||
if (diff >= step) {
|
||||
adjust_idx |= i;
|
||||
diff -= step;
|
||||
pred_diff += step;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update and clamp previous predicted value */
|
||||
state->pred_val += sign ? -pred_diff : pred_diff;
|
||||
|
||||
if (state->pred_val > 32767) {
|
||||
state->pred_val = 32767;
|
||||
} else if (state->pred_val < -32768) {
|
||||
state->pred_val = -32768;
|
||||
}
|
||||
|
||||
/* Update and clamp StepSize lookup table index */
|
||||
state->step_idx += IndexAdjust[adjust_idx];
|
||||
|
||||
if (state->step_idx < 0) {
|
||||
state->step_idx = 0;
|
||||
} else if (state->step_idx > 88) {
|
||||
state->step_idx = 88;
|
||||
}
|
||||
return (sign | adjust_idx);
|
||||
}
|
||||
|
||||
|
||||
static int adpcm_decoder(unsigned char code, adpcm_state_t * state)
|
||||
{
|
||||
short pred_diff; /* Predicted difference to next sample */
|
||||
short step; /* holds previous StepSize value */
|
||||
char sign;
|
||||
|
||||
int i;
|
||||
|
||||
/* Separate sign and magnitude */
|
||||
sign = code & 0x8;
|
||||
code &= 0x7;
|
||||
|
||||
/*
|
||||
* Computes pred_diff = (code + 0.5) * step / 4,
|
||||
* but see comment in adpcm_coder.
|
||||
*/
|
||||
|
||||
step = StepSize[state->step_idx];
|
||||
|
||||
/* Compute difference and new predicted value */
|
||||
pred_diff = step >> 3;
|
||||
for (i = 0x4; i; i >>= 1, step >>= 1) {
|
||||
if (code & i) {
|
||||
pred_diff += step;
|
||||
}
|
||||
}
|
||||
state->pred_val += (sign) ? -pred_diff : pred_diff;
|
||||
|
||||
/* Clamp output value */
|
||||
if (state->pred_val > 32767) {
|
||||
state->pred_val = 32767;
|
||||
} else if (state->pred_val < -32768) {
|
||||
state->pred_val = -32768;
|
||||
}
|
||||
|
||||
/* Find new StepSize index value */
|
||||
state->step_idx += IndexAdjust[code];
|
||||
|
||||
if (state->step_idx < 0) {
|
||||
state->step_idx = 0;
|
||||
} else if (state->step_idx > 88) {
|
||||
state->step_idx = 88;
|
||||
}
|
||||
return (state->pred_val);
|
||||
}
|
||||
|
||||
static void adpcm_decode(snd_pcm_channel_area_t *src_areas,
|
||||
size_t src_offset,
|
||||
snd_pcm_channel_area_t *dst_areas,
|
||||
size_t dst_offset,
|
||||
size_t frames, size_t channels, int putidx,
|
||||
adpcm_state_t *states)
|
||||
{
|
||||
#define PUT_S16_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_S16_LABELS
|
||||
void *put = put_s16_labels[putidx];
|
||||
size_t channel;
|
||||
for (channel = 0; channel < channels; ++channel, ++states) {
|
||||
char *src;
|
||||
int srcbit;
|
||||
char *dst;
|
||||
int src_step, srcbit_step, dst_step;
|
||||
size_t frames1;
|
||||
snd_pcm_channel_area_t *src_area = &src_areas[channel];
|
||||
snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
|
||||
#if 0
|
||||
if (!src_area->enabled) {
|
||||
if (dst_area->wanted)
|
||||
snd_pcm_area_silence(dst_area, dst_offset, frames, dst_sfmt);
|
||||
dst_area->enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_area->enabled = 1;
|
||||
#endif
|
||||
srcbit = src_area->first + src_area->step * src_offset;
|
||||
src = src_area->addr + srcbit / 8;
|
||||
srcbit %= 8;
|
||||
src_step = src_area->step / 8;
|
||||
srcbit_step = src_area->step % 8;
|
||||
dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
|
||||
dst_step = snd_pcm_channel_area_step(dst_area);
|
||||
frames1 = frames;
|
||||
while (frames1-- > 0) {
|
||||
int16_t sample;
|
||||
int v;
|
||||
if (srcbit)
|
||||
v = *src & 0x0f;
|
||||
else
|
||||
v = (*src >> 4) & 0x0f;
|
||||
sample = adpcm_decoder(v, states);
|
||||
goto *put;
|
||||
#define PUT_S16_END after
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_S16_END
|
||||
after:
|
||||
src += src_step;
|
||||
srcbit += srcbit_step;
|
||||
if (srcbit == 8) {
|
||||
src++;
|
||||
srcbit = 0;
|
||||
}
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void adpcm_encode(snd_pcm_channel_area_t *src_areas,
|
||||
size_t src_offset,
|
||||
snd_pcm_channel_area_t *dst_areas,
|
||||
size_t dst_offset,
|
||||
size_t frames, size_t channels, int getidx,
|
||||
adpcm_state_t *states)
|
||||
{
|
||||
#define GET_S16_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_LABELS
|
||||
void *get = get_s16_labels[getidx];
|
||||
size_t channel;
|
||||
int16_t sample = 0;
|
||||
for (channel = 0; channel < channels; ++channel, ++states) {
|
||||
char *src;
|
||||
char *dst;
|
||||
int dstbit;
|
||||
int src_step, dst_step, dstbit_step;
|
||||
size_t frames1;
|
||||
snd_pcm_channel_area_t *src_area = &src_areas[channel];
|
||||
snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
|
||||
#if 0
|
||||
if (!src_area->enabled) {
|
||||
if (dst_area->wanted)
|
||||
snd_pcm_area_silence(dst_area, dst_offset, frames, dst_sfmt);
|
||||
dst_area->enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_area->enabled = 1;
|
||||
#endif
|
||||
src = snd_pcm_channel_area_addr(src_area, src_offset);
|
||||
src_step = snd_pcm_channel_area_step(src_area);
|
||||
dstbit = dst_area->first + dst_area->step * dst_offset;
|
||||
dst = dst_area->addr + dstbit / 8;
|
||||
dstbit %= 8;
|
||||
dst_step = dst_area->step / 8;
|
||||
dstbit_step = dst_area->step % 8;
|
||||
frames1 = frames;
|
||||
while (frames1-- > 0) {
|
||||
int v;
|
||||
goto *get;
|
||||
#define GET_S16_END after
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_END
|
||||
after:
|
||||
v = adpcm_encoder(sample, states);
|
||||
if (dstbit)
|
||||
*dst = (*dst & 0xf0) | v;
|
||||
else
|
||||
*dst = (*dst & 0x0f) | (v << 4);
|
||||
src += src_step;
|
||||
dst += dst_step;
|
||||
dstbit += dstbit_step;
|
||||
if (dstbit == 8) {
|
||||
dst++;
|
||||
dstbit = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int snd_pcm_adpcm_close(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_adpcm_t *adpcm = pcm->private;
|
||||
int err = 0;
|
||||
if (adpcm->plug.close_slave)
|
||||
err = snd_pcm_close(adpcm->plug.slave);
|
||||
if (adpcm->states)
|
||||
free(adpcm->states);
|
||||
free(adpcm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_adpcm_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info)
|
||||
{
|
||||
snd_pcm_adpcm_t *adpcm = pcm->private;
|
||||
unsigned int req_mask = info->req_mask;
|
||||
unsigned int sfmt = info->req.format.sfmt;
|
||||
int err;
|
||||
if (req_mask & SND_PCM_PARAMS_SFMT) {
|
||||
if (adpcm->sformat == SND_PCM_SFMT_IMA_ADPCM ?
|
||||
!snd_pcm_format_linear(sfmt) :
|
||||
sfmt != SND_PCM_SFMT_IMA_ADPCM) {
|
||||
info->req.fail_mask = SND_PCM_PARAMS_SFMT;
|
||||
info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
info->req_mask |= SND_PCM_PARAMS_SFMT;
|
||||
info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE |
|
||||
SND_PCM_PARAMS_XFER_MODE);
|
||||
info->req.format.sfmt = adpcm->sformat;
|
||||
err = snd_pcm_params_info(adpcm->plug.slave, info);
|
||||
info->req_mask = req_mask;
|
||||
info->req.format.sfmt = sfmt;
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (req_mask & SND_PCM_PARAMS_SFMT)
|
||||
info->formats = 1 << sfmt;
|
||||
else
|
||||
info->formats = adpcm->sformat == SND_PCM_SFMT_IMA_ADPCM ?
|
||||
SND_PCM_LINEAR_FORMATS : 1 << SND_PCM_SFMT_IMA_ADPCM;
|
||||
info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
|
||||
info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int snd_pcm_adpcm_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
|
||||
{
|
||||
snd_pcm_adpcm_t *adpcm = pcm->private;
|
||||
snd_pcm_t *slave = adpcm->plug.slave;
|
||||
int err;
|
||||
if (adpcm->sformat == SND_PCM_SFMT_IMA_ADPCM ?
|
||||
!snd_pcm_format_linear(params->format.sfmt) :
|
||||
params->format.sfmt != SND_PCM_SFMT_IMA_ADPCM) {
|
||||
params->fail_mask = SND_PCM_PARAMS_SFMT;
|
||||
params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
|
||||
return -EINVAL;
|
||||
}
|
||||
if (slave->mmap_data) {
|
||||
err = snd_pcm_munmap_data(slave);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
adpcm->cformat = params->format.sfmt;
|
||||
adpcm->cxfer_mode = params->xfer_mode;
|
||||
adpcm->cmmap_shape = params->mmap_shape;
|
||||
params->format.sfmt = adpcm->sformat;
|
||||
params->xfer_mode = SND_PCM_XFER_UNSPECIFIED;
|
||||
params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;;
|
||||
err = snd_pcm_params(slave, params);
|
||||
params->format.sfmt = adpcm->cformat;
|
||||
params->xfer_mode = adpcm->cxfer_mode;
|
||||
params->mmap_shape = adpcm->cmmap_shape;
|
||||
if (slave->valid_setup) {
|
||||
int r = snd_pcm_mmap_data(slave, NULL);
|
||||
assert(r >= 0);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int snd_pcm_adpcm_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
|
||||
{
|
||||
snd_pcm_adpcm_t *adpcm = pcm->private;
|
||||
int err = snd_pcm_setup(adpcm->plug.slave, setup);
|
||||
if (err < 0)
|
||||
return err;
|
||||
assert(adpcm->sformat == setup->format.sfmt);
|
||||
if (adpcm->cxfer_mode == SND_PCM_XFER_UNSPECIFIED)
|
||||
setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED;
|
||||
else
|
||||
setup->xfer_mode = adpcm->cxfer_mode;
|
||||
if (adpcm->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED)
|
||||
setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED;
|
||||
else
|
||||
setup->mmap_shape = adpcm->cmmap_shape;
|
||||
setup->format.sfmt = adpcm->cformat;
|
||||
setup->mmap_bytes = 0;
|
||||
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
|
||||
if (adpcm->sformat == SND_PCM_SFMT_IMA_ADPCM) {
|
||||
adpcm->getput_idx = getput_index(adpcm->cformat);
|
||||
adpcm->func = adpcm_encode;
|
||||
} else {
|
||||
adpcm->getput_idx = getput_index(adpcm->sformat);
|
||||
adpcm->func = adpcm_decode;
|
||||
}
|
||||
} else {
|
||||
if (adpcm->sformat == SND_PCM_SFMT_IMA_ADPCM) {
|
||||
adpcm->getput_idx = getput_index(adpcm->cformat);
|
||||
adpcm->func = adpcm_decode;
|
||||
} else {
|
||||
adpcm->getput_idx = getput_index(adpcm->sformat);
|
||||
adpcm->func = adpcm_encode;
|
||||
}
|
||||
}
|
||||
if (adpcm->states)
|
||||
free(adpcm->states);
|
||||
adpcm->states = malloc(setup->format.channels * sizeof(*adpcm->states));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_adpcm_init(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_adpcm_t *adpcm = pcm->private;
|
||||
unsigned int k;
|
||||
for (k = 0; k < pcm->setup.format.channels; ++k) {
|
||||
adpcm->states[k].pred_val = 0;
|
||||
adpcm->states[k].step_idx = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_adpcm_write_areas(snd_pcm_t *pcm,
|
||||
snd_pcm_channel_area_t *areas,
|
||||
size_t offset,
|
||||
size_t size,
|
||||
size_t *slave_sizep)
|
||||
{
|
||||
snd_pcm_adpcm_t *adpcm = pcm->private;
|
||||
snd_pcm_t *slave = adpcm->plug.slave;
|
||||
size_t xfer = 0;
|
||||
ssize_t err = 0;
|
||||
if (slave_sizep && *slave_sizep < size)
|
||||
size = *slave_sizep;
|
||||
assert(size > 0);
|
||||
while (xfer < size) {
|
||||
size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer);
|
||||
adpcm->func(areas, offset,
|
||||
slave->mmap_areas, snd_pcm_mmap_offset(slave),
|
||||
frames, pcm->setup.format.channels,
|
||||
adpcm->getput_idx, adpcm->states);
|
||||
err = snd_pcm_mmap_forward(slave, frames);
|
||||
if (err < 0)
|
||||
break;
|
||||
assert((size_t)err == frames);
|
||||
offset += err;
|
||||
xfer += err;
|
||||
snd_pcm_mmap_hw_forward(pcm, err);
|
||||
}
|
||||
if (xfer > 0) {
|
||||
if (slave_sizep)
|
||||
*slave_sizep = xfer;
|
||||
return xfer;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_adpcm_read_areas(snd_pcm_t *pcm,
|
||||
snd_pcm_channel_area_t *areas,
|
||||
size_t offset,
|
||||
size_t size,
|
||||
size_t *slave_sizep)
|
||||
{
|
||||
snd_pcm_adpcm_t *adpcm = pcm->private;
|
||||
snd_pcm_t *slave = adpcm->plug.slave;
|
||||
size_t xfer = 0;
|
||||
ssize_t err = 0;
|
||||
if (slave_sizep && *slave_sizep < size)
|
||||
size = *slave_sizep;
|
||||
assert(size > 0);
|
||||
while (xfer < size) {
|
||||
size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer);
|
||||
adpcm->func(slave->mmap_areas, snd_pcm_mmap_offset(slave),
|
||||
areas, offset,
|
||||
frames, pcm->setup.format.channels,
|
||||
adpcm->getput_idx, adpcm->states);
|
||||
err = snd_pcm_mmap_forward(slave, frames);
|
||||
if (err < 0)
|
||||
break;
|
||||
assert((size_t)err == frames);
|
||||
offset += err;
|
||||
xfer += err;
|
||||
snd_pcm_mmap_hw_forward(pcm, err);
|
||||
}
|
||||
if (xfer > 0) {
|
||||
if (slave_sizep)
|
||||
*slave_sizep = xfer;
|
||||
return xfer;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void snd_pcm_adpcm_dump(snd_pcm_t *pcm, FILE *fp)
|
||||
{
|
||||
snd_pcm_adpcm_t *adpcm = pcm->private;
|
||||
fprintf(fp, "Ima-ADPCM conversion PCM (%s)\n",
|
||||
snd_pcm_format_name(adpcm->sformat));
|
||||
if (pcm->valid_setup) {
|
||||
fprintf(fp, "Its setup is:\n");
|
||||
snd_pcm_dump_setup(pcm, fp);
|
||||
}
|
||||
fprintf(fp, "Slave: ");
|
||||
snd_pcm_dump(adpcm->plug.slave, fp);
|
||||
}
|
||||
|
||||
struct snd_pcm_ops snd_pcm_adpcm_ops = {
|
||||
close: snd_pcm_adpcm_close,
|
||||
info: snd_pcm_plugin_info,
|
||||
params_info: snd_pcm_adpcm_params_info,
|
||||
params: snd_pcm_adpcm_params,
|
||||
setup: snd_pcm_adpcm_setup,
|
||||
channel_info: snd_pcm_plugin_channel_info,
|
||||
channel_params: snd_pcm_plugin_channel_params,
|
||||
channel_setup: snd_pcm_plugin_channel_setup,
|
||||
dump: snd_pcm_adpcm_dump,
|
||||
nonblock: snd_pcm_plugin_nonblock,
|
||||
mmap_status: snd_pcm_plugin_mmap_status,
|
||||
mmap_control: snd_pcm_plugin_mmap_control,
|
||||
mmap_data: snd_pcm_plugin_mmap_data,
|
||||
munmap_status: snd_pcm_plugin_munmap_status,
|
||||
munmap_control: snd_pcm_plugin_munmap_control,
|
||||
munmap_data: snd_pcm_plugin_munmap_data,
|
||||
};
|
||||
|
||||
int snd_pcm_adpcm_open(snd_pcm_t **handlep, int sformat, snd_pcm_t *slave, int close_slave)
|
||||
{
|
||||
snd_pcm_t *handle;
|
||||
snd_pcm_adpcm_t *adpcm;
|
||||
int err;
|
||||
assert(handlep && slave);
|
||||
if (snd_pcm_format_linear(sformat) != 1 &&
|
||||
sformat != SND_PCM_SFMT_IMA_ADPCM)
|
||||
return -EINVAL;
|
||||
adpcm = calloc(1, sizeof(snd_pcm_adpcm_t));
|
||||
if (!adpcm) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
adpcm->sformat = sformat;
|
||||
adpcm->plug.read = snd_pcm_adpcm_read_areas;
|
||||
adpcm->plug.write = snd_pcm_adpcm_write_areas;
|
||||
adpcm->plug.init = snd_pcm_adpcm_init;
|
||||
adpcm->plug.slave = slave;
|
||||
adpcm->plug.close_slave = close_slave;
|
||||
|
||||
handle = calloc(1, sizeof(snd_pcm_t));
|
||||
if (!handle) {
|
||||
free(adpcm);
|
||||
return -ENOMEM;
|
||||
}
|
||||
handle->type = SND_PCM_TYPE_ADPCM;
|
||||
handle->stream = slave->stream;
|
||||
handle->ops = &snd_pcm_adpcm_ops;
|
||||
handle->op_arg = handle;
|
||||
handle->fast_ops = &snd_pcm_plugin_fast_ops;
|
||||
handle->fast_op_arg = handle;
|
||||
handle->mode = slave->mode;
|
||||
handle->private = adpcm;
|
||||
err = snd_pcm_init(handle);
|
||||
if (err < 0) {
|
||||
snd_pcm_close(handle);
|
||||
return err;
|
||||
}
|
||||
*handlep = handle;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _snd_pcm_adpcm_open(snd_pcm_t **pcmp, char *name,
|
||||
snd_config_t *conf,
|
||||
int stream, int mode)
|
||||
{
|
||||
snd_config_iterator_t i;
|
||||
char *sname = NULL;
|
||||
int err;
|
||||
snd_pcm_t *spcm;
|
||||
int sformat = -1;
|
||||
snd_config_foreach(i, conf) {
|
||||
snd_config_t *n = snd_config_entry(i);
|
||||
if (strcmp(n->id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "type") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "stream") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "sname") == 0) {
|
||||
err = snd_config_string_get(n, &sname);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(n->id, "sformat") == 0) {
|
||||
char *f;
|
||||
err = snd_config_string_get(n, &f);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
sformat = snd_pcm_format_value(f);
|
||||
if (sformat < 0)
|
||||
return -EINVAL;
|
||||
if (snd_pcm_format_linear(sformat) != 1 &&
|
||||
sformat != SND_PCM_SFMT_IMA_ADPCM)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!sname || !sformat)
|
||||
return -EINVAL;
|
||||
/* This is needed cause snd_config_update may destroy config */
|
||||
sname = strdup(sname);
|
||||
if (!sname)
|
||||
return -ENOMEM;
|
||||
err = snd_pcm_open(&spcm, sname, stream, mode);
|
||||
free(sname);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_pcm_adpcm_open(pcmp, sformat, spcm, 1);
|
||||
if (err < 0)
|
||||
snd_pcm_close(spcm);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
519
src/pcm/pcm_alaw.c
Normal file
519
src/pcm/pcm_alaw.c
Normal file
|
|
@ -0,0 +1,519 @@
|
|||
/*
|
||||
* PCM - A-Law conversion
|
||||
* 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 <byteswap.h>
|
||||
#include "pcm_local.h"
|
||||
#include "pcm_plugin.h"
|
||||
|
||||
typedef void (*alaw_f)(snd_pcm_channel_area_t *src_areas,
|
||||
size_t src_offset,
|
||||
snd_pcm_channel_area_t *dst_areas,
|
||||
size_t dst_offset,
|
||||
size_t frames, size_t channels, int getputidx);
|
||||
|
||||
typedef struct {
|
||||
/* This field need to be the first */
|
||||
snd_pcm_plugin_t plug;
|
||||
int getput_idx;
|
||||
alaw_f func;
|
||||
int sformat;
|
||||
int cformat;
|
||||
int cxfer_mode, cmmap_shape;
|
||||
} snd_pcm_alaw_t;
|
||||
|
||||
static inline int val_seg(int val)
|
||||
{
|
||||
int r = 1;
|
||||
val >>= 8;
|
||||
if (val & 0xf0) {
|
||||
val >>= 4;
|
||||
r += 4;
|
||||
}
|
||||
if (val & 0x0c) {
|
||||
val >>= 2;
|
||||
r += 2;
|
||||
}
|
||||
if (val & 0x02)
|
||||
r += 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
|
||||
*
|
||||
* s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data.
|
||||
*
|
||||
* Linear Input Code Compressed Code
|
||||
* ------------------------ ---------------
|
||||
* 0000000wxyza 000wxyz
|
||||
* 0000001wxyza 001wxyz
|
||||
* 000001wxyzab 010wxyz
|
||||
* 00001wxyzabc 011wxyz
|
||||
* 0001wxyzabcd 100wxyz
|
||||
* 001wxyzabcde 101wxyz
|
||||
* 01wxyzabcdef 110wxyz
|
||||
* 1wxyzabcdefg 111wxyz
|
||||
*
|
||||
* For further information see John C. Bellamy's Digital Telephony, 1982,
|
||||
* John Wiley & Sons, pps 98-111 and 472-476.
|
||||
*/
|
||||
|
||||
static unsigned char s16_to_alaw(int pcm_val)
|
||||
{
|
||||
int mask;
|
||||
int seg;
|
||||
unsigned char aval;
|
||||
|
||||
if (pcm_val >= 0) {
|
||||
mask = 0xD5;
|
||||
} else {
|
||||
mask = 0x55;
|
||||
pcm_val = -pcm_val;
|
||||
if (pcm_val > 0x7fff)
|
||||
pcm_val = 0x7fff;
|
||||
}
|
||||
|
||||
if (pcm_val < 256)
|
||||
aval = pcm_val >> 4;
|
||||
else {
|
||||
/* Convert the scaled magnitude to segment number. */
|
||||
seg = val_seg(pcm_val);
|
||||
aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
|
||||
}
|
||||
return aval ^ mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* alaw_to_s16() - Convert an A-law value to 16-bit linear PCM
|
||||
*
|
||||
*/
|
||||
static int alaw_to_s16(unsigned char a_val)
|
||||
{
|
||||
int t;
|
||||
int seg;
|
||||
|
||||
a_val ^= 0x55;
|
||||
t = a_val & 0x7f;
|
||||
if (t < 16)
|
||||
t = (t << 4) + 8;
|
||||
else {
|
||||
seg = (t >> 4) & 0x07;
|
||||
t = ((t & 0x0f) << 4) + 0x108;
|
||||
t <<= seg -1;
|
||||
}
|
||||
return ((a_val & 0x80) ? t : -t);
|
||||
}
|
||||
|
||||
static void alaw_decode(snd_pcm_channel_area_t *src_areas,
|
||||
size_t src_offset,
|
||||
snd_pcm_channel_area_t *dst_areas,
|
||||
size_t dst_offset,
|
||||
size_t frames, size_t channels, int putidx)
|
||||
{
|
||||
#define PUT_S16_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_S16_LABELS
|
||||
void *put = put_s16_labels[putidx];
|
||||
size_t channel;
|
||||
for (channel = 0; channel < channels; ++channel) {
|
||||
char *src;
|
||||
char *dst;
|
||||
int src_step, dst_step;
|
||||
size_t frames1;
|
||||
snd_pcm_channel_area_t *src_area = &src_areas[channel];
|
||||
snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
|
||||
#if 0
|
||||
if (!src_area->enabled) {
|
||||
if (dst_area->wanted)
|
||||
snd_pcm_area_silence(&dst_areas[channel], dst_offset, frames, dst_sfmt);
|
||||
dst_area->enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_area->enabled = 1;
|
||||
#endif
|
||||
src = snd_pcm_channel_area_addr(src_area, src_offset);
|
||||
dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
|
||||
src_step = snd_pcm_channel_area_step(src_area);
|
||||
dst_step = snd_pcm_channel_area_step(dst_area);
|
||||
frames1 = frames;
|
||||
while (frames1-- > 0) {
|
||||
int16_t sample = alaw_to_s16(*src);
|
||||
goto *put;
|
||||
#define PUT_S16_END after
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_S16_END
|
||||
after:
|
||||
src += src_step;
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void alaw_encode(snd_pcm_channel_area_t *src_areas,
|
||||
size_t src_offset,
|
||||
snd_pcm_channel_area_t *dst_areas,
|
||||
size_t dst_offset,
|
||||
size_t frames, size_t channels, int getidx)
|
||||
{
|
||||
#define GET_S16_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_LABELS
|
||||
void *get = get_s16_labels[getidx];
|
||||
size_t channel;
|
||||
int16_t sample = 0;
|
||||
for (channel = 0; channel < channels; ++channel) {
|
||||
char *src;
|
||||
char *dst;
|
||||
int src_step, dst_step;
|
||||
size_t frames1;
|
||||
snd_pcm_channel_area_t *src_area = &src_areas[channel];
|
||||
snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
|
||||
#if 0
|
||||
if (!src_area->enabled) {
|
||||
if (dst_area->wanted)
|
||||
snd_pcm_area_silence(&dst_area->area, 0, frames, dst_sfmt);
|
||||
dst_area->enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_area->enabled = 1;
|
||||
#endif
|
||||
src = snd_pcm_channel_area_addr(src_area, src_offset);
|
||||
dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
|
||||
src_step = snd_pcm_channel_area_step(src_area);
|
||||
dst_step = snd_pcm_channel_area_step(dst_area);
|
||||
frames1 = frames;
|
||||
while (frames1-- > 0) {
|
||||
goto *get;
|
||||
#define GET_S16_END after
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_END
|
||||
after:
|
||||
*dst = s16_to_alaw(sample);
|
||||
src += src_step;
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int snd_pcm_alaw_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info)
|
||||
{
|
||||
snd_pcm_alaw_t *alaw = pcm->private;
|
||||
unsigned int req_mask = info->req_mask;
|
||||
unsigned int sfmt = info->req.format.sfmt;
|
||||
int err;
|
||||
if (req_mask & SND_PCM_PARAMS_SFMT) {
|
||||
if (alaw->sformat == SND_PCM_SFMT_A_LAW ?
|
||||
!snd_pcm_format_linear(sfmt) :
|
||||
sfmt != SND_PCM_SFMT_A_LAW) {
|
||||
info->req.fail_mask = SND_PCM_PARAMS_SFMT;
|
||||
info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
info->req_mask |= SND_PCM_PARAMS_SFMT;
|
||||
info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE |
|
||||
SND_PCM_PARAMS_XFER_MODE);
|
||||
info->req.format.sfmt = alaw->sformat;
|
||||
err = snd_pcm_params_info(alaw->plug.slave, info);
|
||||
info->req_mask = req_mask;
|
||||
info->req.format.sfmt = sfmt;
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (req_mask & SND_PCM_PARAMS_SFMT)
|
||||
info->formats = 1 << sfmt;
|
||||
else
|
||||
info->formats = alaw->sformat == SND_PCM_SFMT_A_LAW ?
|
||||
SND_PCM_LINEAR_FORMATS : 1 << SND_PCM_SFMT_A_LAW;
|
||||
info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
|
||||
info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int snd_pcm_alaw_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
|
||||
{
|
||||
snd_pcm_alaw_t *alaw = pcm->private;
|
||||
snd_pcm_t *slave = alaw->plug.slave;
|
||||
int err;
|
||||
if (alaw->sformat == SND_PCM_SFMT_A_LAW ?
|
||||
!snd_pcm_format_linear(params->format.sfmt) :
|
||||
params->format.sfmt != SND_PCM_SFMT_A_LAW) {
|
||||
params->fail_mask = SND_PCM_PARAMS_SFMT;
|
||||
params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
|
||||
return -EINVAL;
|
||||
}
|
||||
if (slave->mmap_data) {
|
||||
err = snd_pcm_munmap_data(slave);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
alaw->cformat = params->format.sfmt;
|
||||
alaw->cxfer_mode = params->xfer_mode;
|
||||
alaw->cmmap_shape = params->mmap_shape;
|
||||
params->format.sfmt = alaw->sformat;
|
||||
params->xfer_mode = SND_PCM_XFER_UNSPECIFIED;
|
||||
params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;;
|
||||
err = snd_pcm_params(slave, params);
|
||||
params->format.sfmt = alaw->cformat;
|
||||
params->xfer_mode = alaw->cxfer_mode;
|
||||
params->mmap_shape = alaw->cmmap_shape;
|
||||
if (slave->valid_setup) {
|
||||
int r = snd_pcm_mmap_data(slave, NULL);
|
||||
assert(r >= 0);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int snd_pcm_alaw_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
|
||||
{
|
||||
snd_pcm_alaw_t *alaw = pcm->private;
|
||||
int err = snd_pcm_setup(alaw->plug.slave, setup);
|
||||
if (err < 0)
|
||||
return err;
|
||||
assert(alaw->sformat == setup->format.sfmt);
|
||||
if (alaw->cxfer_mode == SND_PCM_XFER_UNSPECIFIED)
|
||||
setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED;
|
||||
else
|
||||
setup->xfer_mode = alaw->cxfer_mode;
|
||||
if (alaw->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED)
|
||||
setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED;
|
||||
else
|
||||
setup->mmap_shape = alaw->cmmap_shape;
|
||||
setup->format.sfmt = alaw->cformat;
|
||||
setup->mmap_bytes = 0;
|
||||
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
|
||||
if (alaw->sformat == SND_PCM_SFMT_A_LAW) {
|
||||
alaw->getput_idx = getput_index(alaw->cformat);
|
||||
alaw->func = alaw_encode;
|
||||
} else {
|
||||
alaw->getput_idx = getput_index(alaw->sformat);
|
||||
alaw->func = alaw_decode;
|
||||
}
|
||||
} else {
|
||||
if (alaw->sformat == SND_PCM_SFMT_A_LAW) {
|
||||
alaw->getput_idx = getput_index(alaw->cformat);
|
||||
alaw->func = alaw_decode;
|
||||
} else {
|
||||
alaw->getput_idx = getput_index(alaw->sformat);
|
||||
alaw->func = alaw_encode;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_alaw_write_areas(snd_pcm_t *pcm,
|
||||
snd_pcm_channel_area_t *areas,
|
||||
size_t offset,
|
||||
size_t size,
|
||||
size_t *slave_sizep)
|
||||
{
|
||||
snd_pcm_alaw_t *alaw = pcm->private;
|
||||
snd_pcm_t *slave = alaw->plug.slave;
|
||||
size_t xfer = 0;
|
||||
ssize_t err = 0;
|
||||
if (slave_sizep && *slave_sizep < size)
|
||||
size = *slave_sizep;
|
||||
assert(size > 0);
|
||||
while (xfer < size) {
|
||||
size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer);
|
||||
alaw->func(areas, offset,
|
||||
slave->mmap_areas, snd_pcm_mmap_offset(slave),
|
||||
frames, pcm->setup.format.channels,
|
||||
alaw->getput_idx);
|
||||
err = snd_pcm_mmap_forward(slave, frames);
|
||||
if (err < 0)
|
||||
break;
|
||||
assert((size_t)err == frames);
|
||||
offset += err;
|
||||
xfer += err;
|
||||
snd_pcm_mmap_hw_forward(pcm, err);
|
||||
}
|
||||
if (xfer > 0) {
|
||||
if (slave_sizep)
|
||||
*slave_sizep = xfer;
|
||||
return xfer;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_alaw_read_areas(snd_pcm_t *pcm,
|
||||
snd_pcm_channel_area_t *areas,
|
||||
size_t offset,
|
||||
size_t size,
|
||||
size_t *slave_sizep)
|
||||
{
|
||||
snd_pcm_alaw_t *alaw = pcm->private;
|
||||
snd_pcm_t *slave = alaw->plug.slave;
|
||||
size_t xfer = 0;
|
||||
ssize_t err = 0;
|
||||
if (slave_sizep && *slave_sizep < size)
|
||||
size = *slave_sizep;
|
||||
assert(size > 0);
|
||||
while (xfer < size) {
|
||||
size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer);
|
||||
alaw->func(slave->mmap_areas, snd_pcm_mmap_offset(slave),
|
||||
areas, offset,
|
||||
frames, pcm->setup.format.channels,
|
||||
alaw->getput_idx);
|
||||
err = snd_pcm_mmap_forward(slave, frames);
|
||||
if (err < 0)
|
||||
break;
|
||||
assert((size_t)err == frames);
|
||||
offset += err;
|
||||
xfer += err;
|
||||
snd_pcm_mmap_hw_forward(pcm, err);
|
||||
}
|
||||
if (xfer > 0) {
|
||||
if (slave_sizep)
|
||||
*slave_sizep = xfer;
|
||||
return xfer;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void snd_pcm_alaw_dump(snd_pcm_t *pcm, FILE *fp)
|
||||
{
|
||||
snd_pcm_alaw_t *alaw = pcm->private;
|
||||
fprintf(fp, "A-Law conversion PCM (%s)\n",
|
||||
snd_pcm_format_name(alaw->sformat));
|
||||
if (pcm->valid_setup) {
|
||||
fprintf(fp, "Its setup is:\n");
|
||||
snd_pcm_dump_setup(pcm, fp);
|
||||
}
|
||||
fprintf(fp, "Slave: ");
|
||||
snd_pcm_dump(alaw->plug.slave, fp);
|
||||
}
|
||||
|
||||
struct snd_pcm_ops snd_pcm_alaw_ops = {
|
||||
close: snd_pcm_plugin_close,
|
||||
info: snd_pcm_plugin_info,
|
||||
params_info: snd_pcm_alaw_params_info,
|
||||
params: snd_pcm_alaw_params,
|
||||
setup: snd_pcm_alaw_setup,
|
||||
channel_info: snd_pcm_plugin_channel_info,
|
||||
channel_params: snd_pcm_plugin_channel_params,
|
||||
channel_setup: snd_pcm_plugin_channel_setup,
|
||||
dump: snd_pcm_alaw_dump,
|
||||
nonblock: snd_pcm_plugin_nonblock,
|
||||
mmap_status: snd_pcm_plugin_mmap_status,
|
||||
mmap_control: snd_pcm_plugin_mmap_control,
|
||||
mmap_data: snd_pcm_plugin_mmap_data,
|
||||
munmap_status: snd_pcm_plugin_munmap_status,
|
||||
munmap_control: snd_pcm_plugin_munmap_control,
|
||||
munmap_data: snd_pcm_plugin_munmap_data,
|
||||
};
|
||||
|
||||
int snd_pcm_alaw_open(snd_pcm_t **handlep, int sformat, snd_pcm_t *slave, int close_slave)
|
||||
{
|
||||
snd_pcm_t *handle;
|
||||
snd_pcm_alaw_t *alaw;
|
||||
int err;
|
||||
assert(handlep && slave);
|
||||
if (snd_pcm_format_linear(sformat) != 1 &&
|
||||
sformat != SND_PCM_SFMT_A_LAW)
|
||||
return -EINVAL;
|
||||
alaw = calloc(1, sizeof(snd_pcm_alaw_t));
|
||||
if (!alaw) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
alaw->sformat = sformat;
|
||||
alaw->plug.read = snd_pcm_alaw_read_areas;
|
||||
alaw->plug.write = snd_pcm_alaw_write_areas;
|
||||
alaw->plug.slave = slave;
|
||||
alaw->plug.close_slave = close_slave;
|
||||
|
||||
handle = calloc(1, sizeof(snd_pcm_t));
|
||||
if (!handle) {
|
||||
free(alaw);
|
||||
return -ENOMEM;
|
||||
}
|
||||
handle->type = SND_PCM_TYPE_ALAW;
|
||||
handle->stream = slave->stream;
|
||||
handle->ops = &snd_pcm_alaw_ops;
|
||||
handle->op_arg = handle;
|
||||
handle->fast_ops = &snd_pcm_plugin_fast_ops;
|
||||
handle->fast_op_arg = handle;
|
||||
handle->mode = slave->mode;
|
||||
handle->private = alaw;
|
||||
err = snd_pcm_init(handle);
|
||||
if (err < 0) {
|
||||
snd_pcm_close(handle);
|
||||
return err;
|
||||
}
|
||||
*handlep = handle;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _snd_pcm_alaw_open(snd_pcm_t **pcmp, char *name,
|
||||
snd_config_t *conf,
|
||||
int stream, int mode)
|
||||
{
|
||||
snd_config_iterator_t i;
|
||||
char *sname = NULL;
|
||||
int err;
|
||||
snd_pcm_t *spcm;
|
||||
int sformat = -1;
|
||||
snd_config_foreach(i, conf) {
|
||||
snd_config_t *n = snd_config_entry(i);
|
||||
if (strcmp(n->id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "type") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "stream") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "sname") == 0) {
|
||||
err = snd_config_string_get(n, &sname);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(n->id, "sformat") == 0) {
|
||||
char *f;
|
||||
err = snd_config_string_get(n, &f);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
sformat = snd_pcm_format_value(f);
|
||||
if (sformat < 0)
|
||||
return -EINVAL;
|
||||
if (snd_pcm_format_linear(sformat) != 1 &&
|
||||
sformat != SND_PCM_SFMT_A_LAW)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!sname || !sformat)
|
||||
return -EINVAL;
|
||||
/* This is needed cause snd_config_update may destroy config */
|
||||
sname = strdup(sname);
|
||||
if (!sname)
|
||||
return -ENOMEM;
|
||||
err = snd_pcm_open(&spcm, sname, stream, mode);
|
||||
free(sname);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_pcm_alaw_open(pcmp, sformat, spcm, 1);
|
||||
if (err < 0)
|
||||
snd_pcm_close(spcm);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -291,20 +291,35 @@ static int snd_pcm_client_shm_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
|
|||
|
||||
static int snd_pcm_client_shm_state(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_status_t status;
|
||||
int err = snd_pcm_client_shm_status(pcm, &status);
|
||||
snd_pcm_client_t *client = pcm->private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_STATE;
|
||||
err = snd_pcm_client_shm_action(pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return status.state;
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_client_shm_hw_ptr(snd_pcm_t *pcm, int update)
|
||||
static int snd_pcm_client_shm_delay(snd_pcm_t *pcm, ssize_t *delayp)
|
||||
{
|
||||
snd_pcm_client_t *client = pcm->private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_HW_PTR;
|
||||
ctrl->u.hw_ptr = update;
|
||||
ctrl->cmd = SND_PCM_IOCTL_DELAY;
|
||||
err = snd_pcm_client_shm_action(pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
*delayp = ctrl->u.delay;
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_client_avail_update(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_client_t *client = pcm->private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_AVAIL_UPDATE;
|
||||
err = snd_pcm_client_shm_action(pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
|
@ -323,24 +338,24 @@ static int snd_pcm_client_shm_prepare(snd_pcm_t *pcm)
|
|||
return ctrl->result;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_go(snd_pcm_t *pcm)
|
||||
static int snd_pcm_client_shm_start(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_client_t *client = pcm->private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_GO;
|
||||
ctrl->cmd = SND_PCM_IOCTL_START;
|
||||
err = snd_pcm_client_shm_action(pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_drain(snd_pcm_t *pcm)
|
||||
static int snd_pcm_client_shm_stop(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_client_t *client = pcm->private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_DRAIN;
|
||||
ctrl->cmd = SND_PCM_IOCTL_STOP;
|
||||
err = snd_pcm_client_shm_action(pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
|
@ -385,127 +400,7 @@ static ssize_t snd_pcm_client_shm_appl_ptr(snd_pcm_t *pcm, off_t offset)
|
|||
return ctrl->result;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_client_shm_write(snd_pcm_t *pcm, snd_timestamp_t *tstamp, const void *buffer, size_t size)
|
||||
{
|
||||
snd_pcm_client_t *client = pcm->private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
size_t maxsize = PCM_SHM_DATA_MAXLEN;
|
||||
size_t bytes = snd_pcm_frames_to_bytes(pcm, size);
|
||||
int err;
|
||||
if (bytes > maxsize)
|
||||
return -EINVAL;
|
||||
ctrl->cmd = SND_PCM_IOCTL_WRITE_FRAMES;
|
||||
// ctrl->u.write.tstamp = *tstamp;
|
||||
ctrl->u.write.count = size;
|
||||
memcpy(ctrl->data, buffer, bytes);
|
||||
err = snd_pcm_client_shm_action(pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_client_shm_writev(snd_pcm_t *pcm, snd_timestamp_t *tstamp, const struct iovec *vector, unsigned long count)
|
||||
{
|
||||
/* FIXME: interleaved */
|
||||
snd_pcm_client_t *client = pcm->private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
size_t vecsize = count * sizeof(struct iovec);
|
||||
size_t maxsize = PCM_SHM_DATA_MAXLEN;
|
||||
int bits_per_sample = pcm->bits_per_sample;
|
||||
char *base;
|
||||
struct iovec *vec;
|
||||
unsigned long k;
|
||||
size_t ofs;
|
||||
int err;
|
||||
if (vecsize > maxsize)
|
||||
return -EINVAL;
|
||||
maxsize -= vecsize;
|
||||
ctrl->cmd = SND_PCM_IOCTL_WRITEV_FRAMES;
|
||||
// ctrl->u.writev.tstamp = *tstamp;
|
||||
ctrl->u.writev.count = count;
|
||||
memcpy(ctrl->data, vector, vecsize);
|
||||
vec = (struct iovec *) ctrl->data;
|
||||
base = ctrl->data + vecsize;
|
||||
ofs = 0;
|
||||
for (k = 0; k < count; ++k) {
|
||||
size_t len = vector[k].iov_len * bits_per_sample / 8;
|
||||
memcpy(base + ofs, vector[k].iov_base, len);
|
||||
vec[k].iov_base = (void *) ofs;
|
||||
ofs += len;
|
||||
}
|
||||
err = snd_pcm_client_shm_action(pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_client_shm_read(snd_pcm_t *pcm, snd_timestamp_t *tstamp, void *buffer, size_t size)
|
||||
{
|
||||
snd_pcm_client_t *client = pcm->private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
size_t maxsize = PCM_SHM_DATA_MAXLEN;
|
||||
size_t bytes = snd_pcm_frames_to_bytes(pcm, size);
|
||||
int err;
|
||||
if (bytes > maxsize)
|
||||
return -EINVAL;
|
||||
ctrl->cmd = SND_PCM_IOCTL_READ_FRAMES;
|
||||
// ctrl->u.read.tstamp = *tstamp;
|
||||
ctrl->u.read.count = size;
|
||||
err = snd_pcm_client_shm_action(pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (ctrl->result <= 0)
|
||||
return ctrl->result;
|
||||
bytes = snd_pcm_frames_to_bytes(pcm, ctrl->result);
|
||||
memcpy(buffer, ctrl->data, bytes);
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_client_shm_readv(snd_pcm_t *pcm, snd_timestamp_t *tstamp, const struct iovec *vector, unsigned long count)
|
||||
{
|
||||
/* FIXME: interleaved */
|
||||
snd_pcm_client_t *client = pcm->private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
size_t vecsize = count * sizeof(struct iovec);
|
||||
size_t maxsize = PCM_SHM_DATA_MAXLEN;
|
||||
int bits_per_sample = pcm->bits_per_sample;
|
||||
char *base;
|
||||
struct iovec *vec;
|
||||
unsigned long k;
|
||||
size_t ofs, bytes;
|
||||
int err;
|
||||
if (vecsize > maxsize)
|
||||
return -EINVAL;
|
||||
maxsize -= vecsize;
|
||||
ctrl->cmd = SND_PCM_IOCTL_WRITEV_FRAMES;
|
||||
// ctrl->u.writev.tstamp = *tstamp;
|
||||
ctrl->u.writev.count = count;
|
||||
memcpy(ctrl->data, vector, vecsize);
|
||||
vec = (struct iovec *) ctrl->data;
|
||||
base = ctrl->data + vecsize;
|
||||
ofs = 0;
|
||||
for (k = 0; k < count; ++k) {
|
||||
size_t len = vector[k].iov_len * bits_per_sample / 8;
|
||||
vec[k].iov_base = (void *) ofs;
|
||||
ofs += len;
|
||||
}
|
||||
err = snd_pcm_client_shm_action(pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (ctrl->result <= 0)
|
||||
return ctrl->result;
|
||||
bytes = snd_pcm_frames_to_bytes(pcm, ctrl->result);
|
||||
ofs = 0;
|
||||
for (k = 0; k < count; ++k) {
|
||||
/* FIXME: optimize partial read */
|
||||
size_t len = vector[k].iov_len * bits_per_sample / 8;
|
||||
memcpy(vector[k].iov_base, base + ofs, len);
|
||||
ofs += len;
|
||||
}
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_mmap_status(snd_pcm_t *pcm, snd_pcm_mmap_status_t **status)
|
||||
static int snd_pcm_client_shm_mmap_status(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_client_t *client = pcm->private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
|
|
@ -515,16 +410,17 @@ static int snd_pcm_client_shm_mmap_status(snd_pcm_t *pcm, snd_pcm_mmap_status_t
|
|||
fd = snd_pcm_client_shm_action_fd(pcm);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
/* FIXME: not mmap */
|
||||
ptr = mmap(NULL, sizeof(snd_pcm_mmap_status_t), PROT_READ, MAP_FILE|MAP_SHARED,
|
||||
fd, SND_PCM_MMAP_OFFSET_STATUS);
|
||||
close(fd);
|
||||
if (ptr == MAP_FAILED || ptr == NULL)
|
||||
return -errno;
|
||||
*status = ptr;
|
||||
pcm->mmap_status = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_mmap_control(snd_pcm_t *pcm, snd_pcm_mmap_control_t **control)
|
||||
static int snd_pcm_client_shm_mmap_control(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_client_t *client = pcm->private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
|
|
@ -534,16 +430,17 @@ static int snd_pcm_client_shm_mmap_control(snd_pcm_t *pcm, snd_pcm_mmap_control_
|
|||
fd = snd_pcm_client_shm_action_fd(pcm);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
/* FIXME: not mmap */
|
||||
ptr = mmap(NULL, sizeof(snd_pcm_mmap_control_t), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED,
|
||||
fd, SND_PCM_MMAP_OFFSET_CONTROL);
|
||||
close(fd);
|
||||
if (ptr == MAP_FAILED || ptr == NULL)
|
||||
return -errno;
|
||||
*control = ptr;
|
||||
pcm->mmap_control = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_mmap_data(snd_pcm_t *pcm, void **buffer, size_t bsize ATTRIBUTE_UNUSED)
|
||||
static int snd_pcm_client_shm_mmap_data(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_client_t *client = pcm->private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
|
|
@ -554,19 +451,19 @@ static int snd_pcm_client_shm_mmap_data(snd_pcm_t *pcm, void **buffer, size_t bs
|
|||
fd = snd_pcm_client_shm_action_fd(pcm);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
/* FIXME: not mmap */
|
||||
prot = pcm->stream == SND_PCM_STREAM_PLAYBACK ? PROT_WRITE : PROT_READ;
|
||||
ptr = mmap(NULL, bsize, prot, MAP_FILE|MAP_SHARED,
|
||||
ptr = mmap(NULL, pcm->setup.mmap_bytes, prot, MAP_FILE|MAP_SHARED,
|
||||
fd, SND_PCM_MMAP_OFFSET_DATA);
|
||||
close(fd);
|
||||
if (ptr == MAP_FAILED || ptr == NULL)
|
||||
return -errno;
|
||||
*buffer = ptr;
|
||||
pcm->mmap_data = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_munmap_status(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_mmap_status_t *status ATTRIBUTE_UNUSED)
|
||||
static int snd_pcm_client_shm_munmap_status(snd_pcm_t *pcm)
|
||||
{
|
||||
#if 0
|
||||
snd_pcm_client_t *client = pcm->private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
|
|
@ -574,17 +471,14 @@ static int snd_pcm_client_shm_munmap_status(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd
|
|||
err = snd_pcm_client_shm_action(pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return ctrl->result;
|
||||
#else
|
||||
if (munmap(status, sizeof(*status)) < 0)
|
||||
/* FIXME: not mmap */
|
||||
if (munmap(pcm->mmap_status, sizeof(*pcm->mmap_status)) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
#endif
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_munmap_control(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_mmap_control_t *control ATTRIBUTE_UNUSED)
|
||||
static int snd_pcm_client_shm_munmap_control(snd_pcm_t *pcm)
|
||||
{
|
||||
#if 0
|
||||
snd_pcm_client_t *client = pcm->private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
|
|
@ -592,17 +486,14 @@ static int snd_pcm_client_shm_munmap_control(snd_pcm_t *pcm ATTRIBUTE_UNUSED, sn
|
|||
err = snd_pcm_client_shm_action(pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return ctrl->result;
|
||||
#else
|
||||
if (munmap(control, sizeof(*control)) < 0)
|
||||
/* FIXME: not mmap */
|
||||
if (munmap(pcm->mmap_control, sizeof(*pcm->mmap_control)) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
#endif
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_shm_munmap_data(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void *buffer, size_t bsize)
|
||||
static int snd_pcm_client_shm_munmap_data(snd_pcm_t *pcm)
|
||||
{
|
||||
#if 0
|
||||
snd_pcm_client_t *client = pcm->private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
|
|
@ -610,22 +501,33 @@ static int snd_pcm_client_shm_munmap_data(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void
|
|||
err = snd_pcm_client_shm_action(pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return ctrl->result;
|
||||
#else
|
||||
if (munmap(buffer, bsize) < 0)
|
||||
/* FIXME: not mmap */
|
||||
if (munmap(pcm->mmap_data, pcm->setup.mmap_bytes) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
#endif
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_file_descriptor(snd_pcm_t *pcm)
|
||||
static ssize_t snd_pcm_client_mmap_forward(snd_pcm_t *pcm, size_t size)
|
||||
{
|
||||
snd_pcm_client_t *client = pcm->private;
|
||||
snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl;
|
||||
int err;
|
||||
ctrl->cmd = SND_PCM_IOCTL_MMAP_FORWARD;
|
||||
ctrl->u.mmap_forward = size;
|
||||
err = snd_pcm_client_shm_action(pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return ctrl->result;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_poll_descriptor(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_client_t *client = pcm->private;
|
||||
return client->data_fd;
|
||||
}
|
||||
|
||||
static int snd_pcm_client_channels_mask(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
|
||||
bitset_t *client_vmask ATTRIBUTE_UNUSED)
|
||||
bitset_t *cmask ATTRIBUTE_UNUSED)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -645,35 +547,37 @@ struct snd_pcm_ops snd_pcm_client_ops = {
|
|||
params_info: snd_pcm_client_shm_params_info,
|
||||
params: snd_pcm_client_shm_params,
|
||||
setup: snd_pcm_client_shm_setup,
|
||||
dump: snd_pcm_client_dump,
|
||||
};
|
||||
|
||||
struct snd_pcm_fast_ops snd_pcm_client_fast_ops = {
|
||||
nonblock: snd_pcm_client_shm_nonblock,
|
||||
channel_info: snd_pcm_client_shm_channel_info,
|
||||
channel_params: snd_pcm_client_shm_channel_params,
|
||||
channel_setup: snd_pcm_client_shm_channel_setup,
|
||||
status: snd_pcm_client_shm_status,
|
||||
hw_ptr: snd_pcm_client_shm_hw_ptr,
|
||||
state: snd_pcm_client_shm_state,
|
||||
prepare: snd_pcm_client_shm_prepare,
|
||||
go: snd_pcm_client_shm_go,
|
||||
drain: snd_pcm_client_shm_drain,
|
||||
flush: snd_pcm_client_shm_flush,
|
||||
pause: snd_pcm_client_shm_pause,
|
||||
appl_ptr: snd_pcm_client_shm_appl_ptr,
|
||||
write: snd_pcm_client_shm_write,
|
||||
writev: snd_pcm_client_shm_writev,
|
||||
read: snd_pcm_client_shm_read,
|
||||
readv: snd_pcm_client_shm_readv,
|
||||
dump: snd_pcm_client_dump,
|
||||
nonblock: snd_pcm_client_shm_nonblock,
|
||||
mmap_status: snd_pcm_client_shm_mmap_status,
|
||||
mmap_control: snd_pcm_client_shm_mmap_control,
|
||||
mmap_data: snd_pcm_client_shm_mmap_data,
|
||||
munmap_status: snd_pcm_client_shm_munmap_status,
|
||||
munmap_control: snd_pcm_client_shm_munmap_control,
|
||||
munmap_data: snd_pcm_client_shm_munmap_data,
|
||||
file_descriptor: snd_pcm_client_file_descriptor,
|
||||
};
|
||||
|
||||
struct snd_pcm_fast_ops snd_pcm_client_fast_ops = {
|
||||
status: snd_pcm_client_shm_status,
|
||||
state: snd_pcm_client_shm_state,
|
||||
delay: snd_pcm_client_shm_delay,
|
||||
prepare: snd_pcm_client_shm_prepare,
|
||||
start: snd_pcm_client_shm_start,
|
||||
stop: snd_pcm_client_shm_stop,
|
||||
flush: snd_pcm_client_shm_flush,
|
||||
pause: snd_pcm_client_shm_pause,
|
||||
appl_ptr: snd_pcm_client_shm_appl_ptr,
|
||||
writei: snd_pcm_mmap_writei,
|
||||
writen: snd_pcm_mmap_writen,
|
||||
readi: snd_pcm_mmap_readi,
|
||||
readn: snd_pcm_mmap_readn,
|
||||
poll_descriptor: snd_pcm_client_poll_descriptor,
|
||||
channels_mask: snd_pcm_client_channels_mask,
|
||||
avail_update: snd_pcm_client_avail_update,
|
||||
mmap_forward: snd_pcm_client_mmap_forward,
|
||||
};
|
||||
|
||||
static int make_local_socket(const char *filename)
|
||||
|
|
@ -830,14 +734,8 @@ int snd_pcm_client_create(snd_pcm_t **handlep, char *host, int port, int transpo
|
|||
}
|
||||
}
|
||||
|
||||
handle = calloc(1, sizeof(snd_pcm_t));
|
||||
if (!handle) {
|
||||
result = -ENOMEM;
|
||||
goto _err;
|
||||
}
|
||||
client = calloc(1, sizeof(snd_pcm_client_t));
|
||||
if (!handle) {
|
||||
free(handle);
|
||||
if (!client) {
|
||||
result = -ENOMEM;
|
||||
goto _err;
|
||||
}
|
||||
|
|
@ -849,6 +747,13 @@ int snd_pcm_client_create(snd_pcm_t **handlep, char *host, int port, int transpo
|
|||
client->u.shm.ctrl = ctrl;
|
||||
break;
|
||||
}
|
||||
|
||||
handle = calloc(1, sizeof(snd_pcm_t));
|
||||
if (!handle) {
|
||||
free(client);
|
||||
result = -ENOMEM;
|
||||
goto _err;
|
||||
}
|
||||
handle->type = SND_PCM_TYPE_CLIENT;
|
||||
handle->stream = stream;
|
||||
handle->ops = &snd_pcm_client_ops;
|
||||
|
|
@ -857,6 +762,11 @@ int snd_pcm_client_create(snd_pcm_t **handlep, char *host, int port, int transpo
|
|||
handle->fast_op_arg = handle;
|
||||
handle->mode = mode;
|
||||
handle->private = client;
|
||||
err = snd_pcm_init(handle);
|
||||
if (err < 0) {
|
||||
snd_pcm_close(handle);
|
||||
return err;
|
||||
}
|
||||
*handlep = handle;
|
||||
return 0;
|
||||
|
||||
|
|
@ -874,3 +784,59 @@ int snd_pcm_client_create(snd_pcm_t **handlep, char *host, int port, int transpo
|
|||
return result;
|
||||
}
|
||||
|
||||
int _snd_pcm_client_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf,
|
||||
int stream, int mode)
|
||||
{
|
||||
snd_config_iterator_t i;
|
||||
char *socket = NULL;
|
||||
char *sname = NULL;
|
||||
char *host = NULL;
|
||||
long port = -1;
|
||||
int err;
|
||||
snd_config_foreach(i, conf) {
|
||||
snd_config_t *n = snd_config_entry(i);
|
||||
if (strcmp(n->id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "type") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "stream") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "socket") == 0) {
|
||||
err = snd_config_string_get(n, &socket);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(n->id, "host") == 0) {
|
||||
err = snd_config_string_get(n, &host);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(n->id, "port") == 0) {
|
||||
err = snd_config_integer_get(n, &port);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(n->id, "sname") == 0) {
|
||||
err = snd_config_string_get(n, &sname);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!sname)
|
||||
return -EINVAL;
|
||||
if (socket) {
|
||||
if (port >= 0 || host)
|
||||
return -EINVAL;
|
||||
return snd_pcm_client_create(pcmp, socket, -1, SND_TRANSPORT_TYPE_SHM, sname, stream, mode);
|
||||
} else {
|
||||
if (port < 0 || !name)
|
||||
return -EINVAL;
|
||||
return snd_pcm_client_create(pcmp, host, port, SND_TRANSPORT_TYPE_TCP, sname, stream, mode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
1274
src/pcm/pcm_common.c
1274
src/pcm/pcm_common.c
File diff suppressed because it is too large
Load diff
447
src/pcm/pcm_file.c
Normal file
447
src/pcm/pcm_file.c
Normal file
|
|
@ -0,0 +1,447 @@
|
|||
/*
|
||||
* PCM - File plugin
|
||||
* 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 <byteswap.h>
|
||||
#include "pcm_local.h"
|
||||
#include "pcm_plugin.h"
|
||||
|
||||
typedef struct {
|
||||
snd_pcm_t *slave;
|
||||
int close_slave;
|
||||
char *fname;
|
||||
int fd;
|
||||
} snd_pcm_file_t;
|
||||
|
||||
static int snd_pcm_file_close(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
int err = 0;
|
||||
if (file->close_slave)
|
||||
err = snd_pcm_close(file->slave);
|
||||
if (file->fname) {
|
||||
free(file->fname);
|
||||
close(file->fd);
|
||||
}
|
||||
free(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_file_nonblock(snd_pcm_t *pcm, int nonblock)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_nonblock(file->slave, nonblock);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_info(file->slave, info);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_channel_info(file->slave, info);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_channel_params(file->slave, params);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_channel_setup(file->slave, setup);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_status(file->slave, status);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_state(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_state(file->slave);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_delay(snd_pcm_t *pcm, ssize_t *delayp)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_delay(file->slave, delayp);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_prepare(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_prepare(file->slave);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_start(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_start(file->slave);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_stop(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_stop(file->slave);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_flush(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_flush(file->slave);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_pause(snd_pcm_t *pcm, int enable)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_pause(file->slave, enable);
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_file_appl_ptr(snd_pcm_t *pcm, off_t offset)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_appl_ptr(file->slave, offset);
|
||||
}
|
||||
|
||||
static void snd_pcm_file_write_areas(snd_pcm_t *pcm,
|
||||
snd_pcm_channel_area_t *areas,
|
||||
size_t offset, size_t frames)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
size_t bytes = snd_pcm_frames_to_bytes(pcm, frames);
|
||||
char buf[bytes];
|
||||
size_t channels = pcm->setup.format.channels;
|
||||
snd_pcm_channel_area_t buf_areas[channels];
|
||||
size_t channel;
|
||||
ssize_t r;
|
||||
for (channel = 0; channel < channels; ++channel) {
|
||||
snd_pcm_channel_area_t *a = &buf_areas[channel];
|
||||
a->addr = buf;
|
||||
a->first = pcm->bits_per_sample * channel;
|
||||
a->step = pcm->bits_per_frame;
|
||||
}
|
||||
snd_pcm_areas_copy(areas, offset, buf_areas, 0,
|
||||
channels, frames, pcm->setup.format.sfmt);
|
||||
r = write(file->fd, buf, bytes);
|
||||
assert(r == (ssize_t)bytes);
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_file_writei(snd_pcm_t *pcm, const void *buffer, size_t size)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
ssize_t n = snd_pcm_writei(file->slave, buffer, size);
|
||||
if (n > 0) {
|
||||
size_t bytes = snd_pcm_frames_to_bytes(pcm, n);
|
||||
ssize_t r = write(file->fd, buffer, bytes);
|
||||
assert(r == (ssize_t)bytes);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_file_writen(snd_pcm_t *pcm, void **bufs, size_t size)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
snd_pcm_channel_area_t areas[pcm->setup.format.channels];
|
||||
ssize_t n = snd_pcm_writen(file->slave, bufs, size);
|
||||
if (n > 0) {
|
||||
snd_pcm_areas_from_bufs(pcm, areas, bufs);
|
||||
snd_pcm_file_write_areas(pcm, areas, 0, n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_file_readi(snd_pcm_t *pcm, void *buffer, size_t size)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
ssize_t n = snd_pcm_readi(file->slave, buffer, size);
|
||||
if (n > 0) {
|
||||
size_t bytes = snd_pcm_frames_to_bytes(pcm, n);
|
||||
ssize_t r = write(file->fd, buffer, bytes);
|
||||
assert(r == (ssize_t)bytes);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_file_readn(snd_pcm_t *pcm, void **bufs, size_t size)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
snd_pcm_channel_area_t areas[pcm->setup.format.channels];
|
||||
ssize_t n = snd_pcm_writen(file->slave, bufs, size);
|
||||
if (n > 0) {
|
||||
snd_pcm_areas_from_bufs(pcm, areas, bufs);
|
||||
snd_pcm_file_write_areas(pcm, areas, 0, n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_file_mmap_forward(snd_pcm_t *pcm, size_t size)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
size_t ofs = pcm->mmap_control->appl_ptr % pcm->setup.buffer_size;
|
||||
ssize_t n = snd_pcm_mmap_forward(file->slave, size);
|
||||
size_t xfer = 0;
|
||||
if (n <= 0)
|
||||
return n;
|
||||
while (xfer < (size_t)n) {
|
||||
size_t frames = size - xfer;
|
||||
size_t cont = pcm->setup.buffer_size - ofs;
|
||||
if (cont < frames)
|
||||
frames = cont;
|
||||
snd_pcm_file_write_areas(pcm, pcm->mmap_areas, ofs, frames);
|
||||
ofs += frames;
|
||||
if (ofs == pcm->setup.buffer_size)
|
||||
ofs = 0;
|
||||
xfer += frames;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_file_avail_update(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_avail_update(file->slave);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_mmap_status(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_mmap_status(file->slave, &pcm->mmap_status);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_mmap_control(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_mmap_control(file->slave, &pcm->mmap_control);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_mmap_data(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_mmap_data(file->slave, &pcm->mmap_data);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_munmap_status(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_munmap_status(file->slave);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_munmap_control(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_munmap_control(file->slave);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_munmap_data(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_munmap_data(file->slave);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_poll_descriptor(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_poll_descriptor(file->slave);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_channels_mask(snd_pcm_t *pcm, bitset_t *cmask)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_channels_mask(file->slave, cmask);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_params_info(file->slave, info);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_params(file->slave, params);
|
||||
}
|
||||
|
||||
static int snd_pcm_file_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
return snd_pcm_setup(file->slave, setup);
|
||||
}
|
||||
|
||||
static void snd_pcm_file_dump(snd_pcm_t *pcm, FILE *fp)
|
||||
{
|
||||
snd_pcm_file_t *file = pcm->private;
|
||||
if (file->fname)
|
||||
fprintf(fp, "File PCM (file=%s)\n", file->fname);
|
||||
else
|
||||
fprintf(fp, "File PCM (fd=%d)\n", file->fd);
|
||||
if (pcm->valid_setup) {
|
||||
fprintf(fp, "Its setup is:\n");
|
||||
snd_pcm_dump_setup(pcm, fp);
|
||||
}
|
||||
fprintf(fp, "Slave: ");
|
||||
snd_pcm_dump(file->slave, fp);
|
||||
}
|
||||
|
||||
struct snd_pcm_ops snd_pcm_file_ops = {
|
||||
close: snd_pcm_file_close,
|
||||
info: snd_pcm_file_info,
|
||||
params_info: snd_pcm_file_params_info,
|
||||
params: snd_pcm_file_params,
|
||||
setup: snd_pcm_file_setup,
|
||||
channel_info: snd_pcm_file_channel_info,
|
||||
channel_params: snd_pcm_file_channel_params,
|
||||
channel_setup: snd_pcm_file_channel_setup,
|
||||
dump: snd_pcm_file_dump,
|
||||
nonblock: snd_pcm_file_nonblock,
|
||||
mmap_status: snd_pcm_file_mmap_status,
|
||||
mmap_control: snd_pcm_file_mmap_control,
|
||||
mmap_data: snd_pcm_file_mmap_data,
|
||||
munmap_status: snd_pcm_file_munmap_status,
|
||||
munmap_control: snd_pcm_file_munmap_control,
|
||||
munmap_data: snd_pcm_file_munmap_data,
|
||||
};
|
||||
|
||||
struct snd_pcm_fast_ops snd_pcm_file_fast_ops = {
|
||||
status: snd_pcm_file_status,
|
||||
state: snd_pcm_file_state,
|
||||
delay: snd_pcm_file_delay,
|
||||
prepare: snd_pcm_file_prepare,
|
||||
start: snd_pcm_file_start,
|
||||
stop: snd_pcm_file_stop,
|
||||
flush: snd_pcm_file_flush,
|
||||
pause: snd_pcm_file_pause,
|
||||
appl_ptr: snd_pcm_file_appl_ptr,
|
||||
writei: snd_pcm_file_writei,
|
||||
writen: snd_pcm_file_writen,
|
||||
readi: snd_pcm_file_readi,
|
||||
readn: snd_pcm_file_readn,
|
||||
poll_descriptor: snd_pcm_file_poll_descriptor,
|
||||
channels_mask: snd_pcm_file_channels_mask,
|
||||
avail_update: snd_pcm_file_avail_update,
|
||||
mmap_forward: snd_pcm_file_mmap_forward,
|
||||
};
|
||||
|
||||
int snd_pcm_file_open(snd_pcm_t **handlep, char *fname, int fd, snd_pcm_t *slave, int close_slave)
|
||||
{
|
||||
snd_pcm_t *handle;
|
||||
snd_pcm_file_t *file;
|
||||
int err;
|
||||
assert(handlep && slave);
|
||||
if (fname) {
|
||||
fd = open(fname, O_WRONLY|O_CREAT, 0666);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
}
|
||||
file = calloc(1, sizeof(snd_pcm_file_t));
|
||||
if (!file) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
file->fname = fname;
|
||||
file->fd = fd;
|
||||
file->slave = slave;
|
||||
file->close_slave = close_slave;
|
||||
|
||||
handle = calloc(1, sizeof(snd_pcm_t));
|
||||
if (!handle) {
|
||||
free(file);
|
||||
return -ENOMEM;
|
||||
}
|
||||
handle->type = SND_PCM_TYPE_FILE;
|
||||
handle->stream = slave->stream;
|
||||
handle->ops = &snd_pcm_file_ops;
|
||||
handle->op_arg = handle;
|
||||
handle->fast_ops = &snd_pcm_file_fast_ops;
|
||||
handle->fast_op_arg = handle;
|
||||
handle->mode = slave->mode;
|
||||
handle->private = file;
|
||||
err = snd_pcm_init(handle);
|
||||
if (err < 0) {
|
||||
snd_pcm_close(handle);
|
||||
return err;
|
||||
}
|
||||
*handlep = handle;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _snd_pcm_file_open(snd_pcm_t **pcmp, char *name,
|
||||
snd_config_t *conf,
|
||||
int stream, int mode)
|
||||
{
|
||||
snd_config_iterator_t i;
|
||||
char *sname = NULL;
|
||||
int err;
|
||||
snd_pcm_t *spcm;
|
||||
char *fname = NULL;
|
||||
long fd = -1;
|
||||
snd_config_foreach(i, conf) {
|
||||
snd_config_t *n = snd_config_entry(i);
|
||||
if (strcmp(n->id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "type") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "stream") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "sname") == 0) {
|
||||
err = snd_config_string_get(n, &sname);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(n->id, "file") == 0) {
|
||||
err = snd_config_string_get(n, &fname);
|
||||
if (err < 0) {
|
||||
err = snd_config_integer_get(n, &fd);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!sname || (!fname && fd < 0))
|
||||
return -EINVAL;
|
||||
if (fname) {
|
||||
fname = strdup(fname);
|
||||
if (!fname)
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* This is needed cause snd_config_update may destroy config */
|
||||
sname = strdup(sname);
|
||||
if (!sname)
|
||||
return -ENOMEM;
|
||||
err = snd_pcm_open(&spcm, sname, stream, mode);
|
||||
free(sname);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_pcm_file_open(pcmp, fname, fd, spcm, 1);
|
||||
if (err < 0)
|
||||
snd_pcm_close(spcm);
|
||||
return err;
|
||||
}
|
||||
298
src/pcm/pcm_hw.c
298
src/pcm/pcm_hw.c
|
|
@ -32,7 +32,7 @@
|
|||
typedef struct {
|
||||
int fd;
|
||||
int card, device, subdevice;
|
||||
void *mmap_data_ptr;
|
||||
int mmap_emulation;
|
||||
} snd_pcm_hw_t;
|
||||
|
||||
#define SND_FILE_PCM_STREAM_PLAYBACK "/dev/snd/pcmC%iD%ip"
|
||||
|
|
@ -100,6 +100,14 @@ static int snd_pcm_hw_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
|
|||
int fd = hw->fd;
|
||||
if (ioctl(fd, SND_PCM_IOCTL_SETUP, setup) < 0)
|
||||
return -errno;
|
||||
if (setup->mmap_shape == SND_PCM_MMAP_UNSPECIFIED) {
|
||||
if (setup->xfer_mode == SND_PCM_XFER_INTERLEAVED)
|
||||
setup->mmap_shape = SND_PCM_MMAP_INTERLEAVED;
|
||||
else
|
||||
setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED;
|
||||
hw->mmap_emulation = 1;
|
||||
} else
|
||||
hw->mmap_emulation = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -127,7 +135,18 @@ static int snd_pcm_hw_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * se
|
|||
int fd = hw->fd;
|
||||
if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_SETUP, setup) < 0)
|
||||
return -errno;
|
||||
setup->area.addr = (char *)hw->mmap_data_ptr + (long)setup->area.addr;
|
||||
if (hw->mmap_emulation) {
|
||||
if (pcm->setup.mmap_shape == SND_PCM_MMAP_INTERLEAVED) {
|
||||
setup->area.addr = pcm->mmap_data;
|
||||
setup->area.first = setup->channel * pcm->bits_per_sample;
|
||||
setup->area.step = pcm->bits_per_frame;
|
||||
} else {
|
||||
setup->area.addr = pcm->mmap_data + setup->channel * pcm->setup.buffer_size * pcm->bits_per_sample / 8;
|
||||
setup->area.first = 0;
|
||||
setup->area.step = pcm->bits_per_sample;
|
||||
}
|
||||
} else
|
||||
setup->area.addr = (char *)pcm->mmap_data + (long)setup->area.addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -145,19 +164,20 @@ static int snd_pcm_hw_state(snd_pcm_t *pcm)
|
|||
snd_pcm_hw_t *hw = pcm->private;
|
||||
int fd = hw->fd;
|
||||
snd_pcm_status_t status;
|
||||
if (ioctl(fd, SND_PCM_IOCTL_STATUS, status) < 0)
|
||||
if (pcm->mmap_status)
|
||||
return pcm->mmap_status->state;
|
||||
if (ioctl(fd, SND_PCM_IOCTL_STATUS, &status) < 0)
|
||||
return -errno;
|
||||
return status.state;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_hw_hw_ptr(snd_pcm_t *pcm, int update ATTRIBUTE_UNUSED)
|
||||
static int snd_pcm_hw_delay(snd_pcm_t *pcm, ssize_t *delayp)
|
||||
{
|
||||
snd_pcm_hw_t *hw = pcm->private;
|
||||
int fd = hw->fd;
|
||||
ssize_t pos = ioctl(fd, SND_PCM_IOCTL_HW_PTR);
|
||||
if (pos < 0)
|
||||
if (ioctl(fd, SND_PCM_IOCTL_DELAY, delayp) < 0)
|
||||
return -errno;
|
||||
return pos;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_hw_prepare(snd_pcm_t *pcm)
|
||||
|
|
@ -169,20 +189,20 @@ static int snd_pcm_hw_prepare(snd_pcm_t *pcm)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_hw_go(snd_pcm_t *pcm)
|
||||
static int snd_pcm_hw_start(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_hw_t *hw = pcm->private;
|
||||
int fd = hw->fd;
|
||||
if (ioctl(fd, SND_PCM_IOCTL_GO) < 0)
|
||||
if (ioctl(fd, SND_PCM_IOCTL_START) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_hw_drain(snd_pcm_t *pcm)
|
||||
static int snd_pcm_hw_stop(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_hw_t *hw = pcm->private;
|
||||
int fd = hw->fd;
|
||||
if (ioctl(fd, SND_PCM_IOCTL_DRAIN) < 0)
|
||||
if (ioctl(fd, SND_PCM_IOCTL_STOP) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -218,79 +238,63 @@ static ssize_t snd_pcm_hw_appl_ptr(snd_pcm_t *pcm, off_t offset)
|
|||
return result;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_hw_write(snd_pcm_t *pcm, snd_timestamp_t *tstamp, const void *buffer, size_t size)
|
||||
static ssize_t snd_pcm_hw_writei(snd_pcm_t *pcm, const void *buffer, size_t size)
|
||||
{
|
||||
ssize_t result;
|
||||
snd_pcm_hw_t *hw = pcm->private;
|
||||
int fd = hw->fd;
|
||||
snd_xfer_t xfer;
|
||||
if (tstamp)
|
||||
xfer.tstamp = *tstamp;
|
||||
else
|
||||
xfer.tstamp.tv_sec = xfer.tstamp.tv_usec = 0;
|
||||
xfer.buf = (char*) buffer;
|
||||
xfer.count = size;
|
||||
result = ioctl(fd, SND_PCM_IOCTL_WRITE_FRAMES, &xfer);
|
||||
snd_xferi_t xferi;
|
||||
xferi.buf = (char*) buffer;
|
||||
xferi.frames = size;
|
||||
result = ioctl(fd, SND_PCM_IOCTL_WRITEI_FRAMES, &xferi);
|
||||
if (result < 0)
|
||||
return -errno;
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_hw_writev(snd_pcm_t *pcm, snd_timestamp_t *tstamp, const struct iovec *vector, unsigned long count)
|
||||
static ssize_t snd_pcm_hw_writen(snd_pcm_t *pcm, void **bufs, size_t size)
|
||||
{
|
||||
ssize_t result;
|
||||
snd_pcm_hw_t *hw = pcm->private;
|
||||
int fd = hw->fd;
|
||||
snd_xferv_t xferv;
|
||||
if (tstamp)
|
||||
xferv.tstamp = *tstamp;
|
||||
else
|
||||
xferv.tstamp.tv_sec = xferv.tstamp.tv_usec = 0;
|
||||
xferv.vector = vector;
|
||||
xferv.count = count;
|
||||
result = ioctl(fd, SND_PCM_IOCTL_WRITEV_FRAMES, &xferv);
|
||||
snd_xfern_t xfern;
|
||||
xfern.bufs = bufs;
|
||||
xfern.frames = size;
|
||||
result = ioctl(fd, SND_PCM_IOCTL_WRITEN_FRAMES, &xfern);
|
||||
if (result < 0)
|
||||
return -errno;
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_hw_read(snd_pcm_t *pcm, snd_timestamp_t *tstamp, void *buffer, size_t size)
|
||||
static ssize_t snd_pcm_hw_readi(snd_pcm_t *pcm, void *buffer, size_t size)
|
||||
{
|
||||
ssize_t result;
|
||||
snd_pcm_hw_t *hw = pcm->private;
|
||||
int fd = hw->fd;
|
||||
snd_xfer_t xfer;
|
||||
if (tstamp)
|
||||
xfer.tstamp = *tstamp;
|
||||
else
|
||||
xfer.tstamp.tv_sec = xfer.tstamp.tv_usec = 0;
|
||||
xfer.buf = buffer;
|
||||
xfer.count = size;
|
||||
result = ioctl(fd, SND_PCM_IOCTL_READ_FRAMES, &xfer);
|
||||
snd_xferi_t xferi;
|
||||
xferi.buf = buffer;
|
||||
xferi.frames = size;
|
||||
result = ioctl(fd, SND_PCM_IOCTL_READI_FRAMES, &xferi);
|
||||
if (result < 0)
|
||||
return -errno;
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_hw_readv(snd_pcm_t *pcm, snd_timestamp_t *tstamp, const struct iovec *vector, unsigned long count)
|
||||
ssize_t snd_pcm_hw_readn(snd_pcm_t *pcm, void **bufs, size_t size)
|
||||
{
|
||||
ssize_t result;
|
||||
snd_pcm_hw_t *hw = pcm->private;
|
||||
int fd = hw->fd;
|
||||
snd_xferv_t xferv;
|
||||
if (tstamp)
|
||||
xferv.tstamp = *tstamp;
|
||||
else
|
||||
xferv.tstamp.tv_sec = xferv.tstamp.tv_usec = 0;
|
||||
xferv.vector = vector;
|
||||
xferv.count = count;
|
||||
result = ioctl(fd, SND_PCM_IOCTL_READV_FRAMES, &xferv);
|
||||
snd_xfern_t xfern;
|
||||
xfern.bufs = bufs;
|
||||
xfern.frames = size;
|
||||
result = ioctl(fd, SND_PCM_IOCTL_READN_FRAMES, &xfern);
|
||||
if (result < 0)
|
||||
return -errno;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int snd_pcm_hw_mmap_status(snd_pcm_t *pcm, snd_pcm_mmap_status_t **status)
|
||||
static int snd_pcm_hw_mmap_status(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_hw_t *hw = pcm->private;
|
||||
void *ptr;
|
||||
|
|
@ -298,11 +302,11 @@ static int snd_pcm_hw_mmap_status(snd_pcm_t *pcm, snd_pcm_mmap_status_t **status
|
|||
hw->fd, SND_PCM_MMAP_OFFSET_STATUS);
|
||||
if (ptr == MAP_FAILED || ptr == NULL)
|
||||
return -errno;
|
||||
*status = ptr;
|
||||
pcm->mmap_status = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_hw_mmap_control(snd_pcm_t *pcm, snd_pcm_mmap_control_t **control)
|
||||
static int snd_pcm_hw_mmap_control(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_hw_t *hw = pcm->private;
|
||||
void *ptr;
|
||||
|
|
@ -310,59 +314,97 @@ static int snd_pcm_hw_mmap_control(snd_pcm_t *pcm, snd_pcm_mmap_control_t **cont
|
|||
hw->fd, SND_PCM_MMAP_OFFSET_CONTROL);
|
||||
if (ptr == MAP_FAILED || ptr == NULL)
|
||||
return -errno;
|
||||
*control = ptr;
|
||||
pcm->mmap_control = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_hw_mmap_data(snd_pcm_t *pcm, void **buffer, size_t bsize)
|
||||
static int snd_pcm_hw_mmap_data(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_hw_t *hw = pcm->private;
|
||||
void *ptr;
|
||||
int prot;
|
||||
#if 0
|
||||
prot = pcm->stream == SND_PCM_STREAM_PLAYBACK ? PROT_WRITE : PROT_READ;
|
||||
#else
|
||||
prot = PROT_WRITE | PROT_READ;
|
||||
#endif
|
||||
ptr = mmap(NULL, bsize, prot, MAP_FILE|MAP_SHARED,
|
||||
hw->fd, SND_PCM_MMAP_OFFSET_DATA);
|
||||
if (ptr == MAP_FAILED || ptr == NULL)
|
||||
return -errno;
|
||||
*buffer = hw->mmap_data_ptr = ptr;
|
||||
if (hw->mmap_emulation) {
|
||||
ptr = malloc(snd_pcm_frames_to_bytes(pcm, pcm->setup.buffer_size));
|
||||
if (!ptr)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
int prot;
|
||||
prot = PROT_WRITE | PROT_READ;
|
||||
ptr = mmap(NULL, pcm->setup.mmap_bytes,
|
||||
prot, MAP_FILE|MAP_SHARED,
|
||||
hw->fd, SND_PCM_MMAP_OFFSET_DATA);
|
||||
if (ptr == MAP_FAILED || ptr == NULL)
|
||||
return -errno;
|
||||
}
|
||||
pcm->mmap_data = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_hw_munmap_status(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_mmap_status_t *status)
|
||||
static int snd_pcm_hw_munmap_status(snd_pcm_t *pcm)
|
||||
{
|
||||
if (munmap(status, sizeof(*status)) < 0)
|
||||
if (munmap(pcm->mmap_status, sizeof(*pcm->mmap_status)) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_hw_munmap_control(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_mmap_control_t *control)
|
||||
static int snd_pcm_hw_munmap_control(snd_pcm_t *pcm)
|
||||
{
|
||||
if (munmap(control, sizeof(*control)) < 0)
|
||||
if (munmap(pcm->mmap_control, sizeof(*pcm->mmap_control)) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_hw_munmap_data(snd_pcm_t *pcm, void *buffer, size_t bsize)
|
||||
static int snd_pcm_hw_munmap_data(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_hw_t *hw = pcm->private;
|
||||
if (munmap(buffer, bsize) < 0)
|
||||
return -errno;
|
||||
hw->mmap_data_ptr = NULL;
|
||||
if (hw->mmap_emulation)
|
||||
free(pcm->mmap_data);
|
||||
else
|
||||
if (munmap(pcm->mmap_data, pcm->setup.mmap_bytes) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_hw_file_descriptor(snd_pcm_t *pcm)
|
||||
static ssize_t snd_pcm_hw_mmap_forward(snd_pcm_t *pcm, size_t size)
|
||||
{
|
||||
snd_pcm_hw_t *hw = pcm->private;
|
||||
if (hw->mmap_emulation && pcm->stream == SND_PCM_STREAM_PLAYBACK)
|
||||
return snd_pcm_write_mmap(pcm, size);
|
||||
snd_pcm_mmap_appl_forward(pcm, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_hw_avail_update(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_hw_t *hw = pcm->private;
|
||||
int fd = hw->fd;
|
||||
size_t avail;
|
||||
ssize_t err;
|
||||
if (pcm->setup.ready_mode == SND_PCM_READY_ASAP) {
|
||||
ssize_t d;
|
||||
int err = ioctl(fd, SND_PCM_IOCTL_DELAY, &d);
|
||||
if (err < 0)
|
||||
return -errno;
|
||||
}
|
||||
avail = snd_pcm_mmap_avail(pcm);
|
||||
if (avail > 0 && hw->mmap_emulation &&
|
||||
pcm->stream == SND_PCM_STREAM_CAPTURE) {
|
||||
err = snd_pcm_read_mmap(pcm, avail);
|
||||
if (err < 0)
|
||||
return err;
|
||||
assert((size_t)err == avail);
|
||||
return err;
|
||||
}
|
||||
return avail;
|
||||
}
|
||||
|
||||
static int snd_pcm_hw_poll_descriptor(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_hw_t *hw = pcm->private;
|
||||
return hw->fd;
|
||||
}
|
||||
|
||||
static int snd_pcm_hw_channels_mask(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
|
||||
bitset_t *client_vmask ATTRIBUTE_UNUSED)
|
||||
bitset_t *cmask ATTRIBUTE_UNUSED)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -387,35 +429,37 @@ struct snd_pcm_ops snd_pcm_hw_ops = {
|
|||
params_info: snd_pcm_hw_params_info,
|
||||
params: snd_pcm_hw_params,
|
||||
setup: snd_pcm_hw_setup,
|
||||
dump: snd_pcm_hw_dump,
|
||||
};
|
||||
|
||||
struct snd_pcm_fast_ops snd_pcm_hw_fast_ops = {
|
||||
nonblock: snd_pcm_hw_nonblock,
|
||||
channel_info: snd_pcm_hw_channel_info,
|
||||
channel_params: snd_pcm_hw_channel_params,
|
||||
channel_setup: snd_pcm_hw_channel_setup,
|
||||
status: snd_pcm_hw_status,
|
||||
hw_ptr: snd_pcm_hw_hw_ptr,
|
||||
state: snd_pcm_hw_state,
|
||||
prepare: snd_pcm_hw_prepare,
|
||||
go: snd_pcm_hw_go,
|
||||
drain: snd_pcm_hw_drain,
|
||||
flush: snd_pcm_hw_flush,
|
||||
pause: snd_pcm_hw_pause,
|
||||
appl_ptr: snd_pcm_hw_appl_ptr,
|
||||
write: snd_pcm_hw_write,
|
||||
writev: snd_pcm_hw_writev,
|
||||
read: snd_pcm_hw_read,
|
||||
readv: snd_pcm_hw_readv,
|
||||
dump: snd_pcm_hw_dump,
|
||||
nonblock: snd_pcm_hw_nonblock,
|
||||
mmap_status: snd_pcm_hw_mmap_status,
|
||||
mmap_control: snd_pcm_hw_mmap_control,
|
||||
mmap_data: snd_pcm_hw_mmap_data,
|
||||
munmap_status: snd_pcm_hw_munmap_status,
|
||||
munmap_control: snd_pcm_hw_munmap_control,
|
||||
munmap_data: snd_pcm_hw_munmap_data,
|
||||
file_descriptor: snd_pcm_hw_file_descriptor,
|
||||
};
|
||||
|
||||
struct snd_pcm_fast_ops snd_pcm_hw_fast_ops = {
|
||||
status: snd_pcm_hw_status,
|
||||
state: snd_pcm_hw_state,
|
||||
delay: snd_pcm_hw_delay,
|
||||
prepare: snd_pcm_hw_prepare,
|
||||
start: snd_pcm_hw_start,
|
||||
stop: snd_pcm_hw_stop,
|
||||
flush: snd_pcm_hw_flush,
|
||||
pause: snd_pcm_hw_pause,
|
||||
appl_ptr: snd_pcm_hw_appl_ptr,
|
||||
writei: snd_pcm_hw_writei,
|
||||
writen: snd_pcm_hw_writen,
|
||||
readi: snd_pcm_hw_readi,
|
||||
readn: snd_pcm_hw_readn,
|
||||
poll_descriptor: snd_pcm_hw_poll_descriptor,
|
||||
channels_mask: snd_pcm_hw_channels_mask,
|
||||
avail_update: snd_pcm_hw_avail_update,
|
||||
mmap_forward: snd_pcm_hw_mmap_forward,
|
||||
};
|
||||
|
||||
int snd_pcm_hw_open_subdevice(snd_pcm_t **handlep, int card, int device, int subdevice, int stream, int mode)
|
||||
|
|
@ -432,7 +476,6 @@ int snd_pcm_hw_open_subdevice(snd_pcm_t **handlep, int card, int device, int sub
|
|||
snd_pcm_hw_t *hw;
|
||||
|
||||
assert(handlep);
|
||||
*handlep = 0;
|
||||
|
||||
if ((ret = snd_ctl_hw_open(&ctl, card)) < 0)
|
||||
return ret;
|
||||
|
|
@ -482,14 +525,8 @@ int snd_pcm_hw_open_subdevice(snd_pcm_t **handlep, int card, int device, int sub
|
|||
goto __again;
|
||||
}
|
||||
}
|
||||
handle = calloc(1, sizeof(snd_pcm_t));
|
||||
if (!handle) {
|
||||
ret = -ENOMEM;
|
||||
goto __end;
|
||||
}
|
||||
hw = calloc(1, sizeof(snd_pcm_hw_t));
|
||||
if (!handle) {
|
||||
free(handle);
|
||||
if (!hw) {
|
||||
ret = -ENOMEM;
|
||||
goto __end;
|
||||
}
|
||||
|
|
@ -497,6 +534,13 @@ int snd_pcm_hw_open_subdevice(snd_pcm_t **handlep, int card, int device, int sub
|
|||
hw->device = device;
|
||||
hw->subdevice = subdevice;
|
||||
hw->fd = fd;
|
||||
|
||||
handle = calloc(1, sizeof(snd_pcm_t));
|
||||
if (!handle) {
|
||||
free(hw);
|
||||
ret = -ENOMEM;
|
||||
goto __end;
|
||||
}
|
||||
handle->type = SND_PCM_TYPE_HW;
|
||||
handle->stream = stream;
|
||||
handle->ops = &snd_pcm_hw_ops;
|
||||
|
|
@ -505,6 +549,12 @@ int snd_pcm_hw_open_subdevice(snd_pcm_t **handlep, int card, int device, int sub
|
|||
handle->fast_op_arg = handle;
|
||||
handle->mode = mode;
|
||||
handle->private = hw;
|
||||
ret = snd_pcm_init(handle);
|
||||
if (ret < 0) {
|
||||
snd_pcm_close(handle);
|
||||
snd_ctl_close(ctl);
|
||||
return ret;
|
||||
}
|
||||
*handlep = handle;
|
||||
|
||||
__end:
|
||||
|
|
@ -514,8 +564,54 @@ int snd_pcm_hw_open_subdevice(snd_pcm_t **handlep, int card, int device, int sub
|
|||
return ret;
|
||||
}
|
||||
|
||||
int snd_pcm_hw_open(snd_pcm_t **handlep, int card, int device, int stream, int mode)
|
||||
int snd_pcm_hw_open_device(snd_pcm_t **handlep, int card, int device, int stream, int mode)
|
||||
{
|
||||
return snd_pcm_hw_open_subdevice(handlep, card, device, -1, stream, mode);
|
||||
}
|
||||
|
||||
int _snd_pcm_hw_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf,
|
||||
int stream, int mode)
|
||||
{
|
||||
snd_config_iterator_t i;
|
||||
long card = -1, device = 0, subdevice = -1;
|
||||
char *str;
|
||||
int err;
|
||||
snd_config_foreach(i, conf) {
|
||||
snd_config_t *n = snd_config_entry(i);
|
||||
if (strcmp(n->id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "type") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "stream") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "card") == 0) {
|
||||
err = snd_config_integer_get(n, &card);
|
||||
if (err < 0) {
|
||||
err = snd_config_string_get(n, &str);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
card = snd_card_get_index(str);
|
||||
if (card < 0)
|
||||
return card;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (strcmp(n->id, "device") == 0) {
|
||||
err = snd_config_integer_get(n, &device);
|
||||
if (err < 0)
|
||||
return err;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(n->id, "subdevice") == 0) {
|
||||
err = snd_config_integer_get(n, &subdevice);
|
||||
if (err < 0)
|
||||
return err;
|
||||
continue;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
if (card < 0)
|
||||
return -EINVAL;
|
||||
return snd_pcm_hw_open_subdevice(pcmp, card, device, subdevice, stream, mode);
|
||||
}
|
||||
|
||||
|
|
|
|||
360
src/pcm/pcm_linear.c
Normal file
360
src/pcm/pcm_linear.c
Normal file
|
|
@ -0,0 +1,360 @@
|
|||
/*
|
||||
* PCM - Linear conversion
|
||||
* 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 <byteswap.h>
|
||||
#include "pcm_local.h"
|
||||
#include "pcm_plugin.h"
|
||||
|
||||
typedef struct {
|
||||
/* This field need to be the first */
|
||||
snd_pcm_plugin_t plug;
|
||||
int conv_idx;
|
||||
int sformat;
|
||||
int cformat;
|
||||
int cxfer_mode, cmmap_shape;
|
||||
} snd_pcm_linear_t;
|
||||
|
||||
static void linear_transfer(snd_pcm_channel_area_t *src_areas, size_t src_offset,
|
||||
snd_pcm_channel_area_t *dst_areas, size_t dst_offset,
|
||||
size_t frames, size_t channels, int convidx)
|
||||
{
|
||||
#define CONV_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef CONV_LABELS
|
||||
void *conv = conv_labels[convidx];
|
||||
unsigned int channel;
|
||||
for (channel = 0; channel < channels; ++channel) {
|
||||
char *src;
|
||||
char *dst;
|
||||
int src_step, dst_step;
|
||||
size_t frames1;
|
||||
snd_pcm_channel_area_t *src_area = &src_areas[channel];
|
||||
snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
|
||||
#if 0
|
||||
if (!src_area->enabled) {
|
||||
if (dst_area->wanted)
|
||||
snd_pcm_area_silence(dst_area, dst_offset, frames, dst_sfmt);
|
||||
dst_area->enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_area->enabled = 1;
|
||||
#endif
|
||||
src = snd_pcm_channel_area_addr(src_area, src_offset);
|
||||
dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
|
||||
src_step = snd_pcm_channel_area_step(src_area);
|
||||
dst_step = snd_pcm_channel_area_step(dst_area);
|
||||
frames1 = frames;
|
||||
while (frames1-- > 0) {
|
||||
goto *conv;
|
||||
#define CONV_END after
|
||||
#include "plugin_ops.h"
|
||||
#undef CONV_END
|
||||
after:
|
||||
src += src_step;
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int snd_pcm_linear_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info)
|
||||
{
|
||||
snd_pcm_linear_t *linear = pcm->private;
|
||||
unsigned int req_mask = info->req_mask;
|
||||
unsigned int sfmt = info->req.format.sfmt;
|
||||
int err;
|
||||
if (req_mask & SND_PCM_PARAMS_SFMT &&
|
||||
!snd_pcm_format_linear(sfmt)) {
|
||||
info->req.fail_mask = SND_PCM_PARAMS_SFMT;
|
||||
info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
|
||||
return -EINVAL;
|
||||
}
|
||||
info->req_mask |= SND_PCM_PARAMS_SFMT;
|
||||
info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE |
|
||||
SND_PCM_PARAMS_XFER_MODE);
|
||||
info->req.format.sfmt = linear->sformat;
|
||||
err = snd_pcm_params_info(linear->plug.slave, info);
|
||||
info->req_mask = req_mask;
|
||||
info->req.format.sfmt = sfmt;
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (req_mask & SND_PCM_PARAMS_SFMT)
|
||||
info->formats = 1 << sfmt;
|
||||
else
|
||||
info->formats = SND_PCM_LINEAR_FORMATS;
|
||||
info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
|
||||
info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int snd_pcm_linear_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
|
||||
{
|
||||
snd_pcm_linear_t *linear = pcm->private;
|
||||
snd_pcm_t *slave = linear->plug.slave;
|
||||
int err;
|
||||
if (!snd_pcm_format_linear(params->format.sfmt)) {
|
||||
params->fail_mask = SND_PCM_PARAMS_SFMT;
|
||||
params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
|
||||
return -EINVAL;
|
||||
}
|
||||
if (slave->mmap_data) {
|
||||
err = snd_pcm_munmap_data(slave);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
linear->cformat = params->format.sfmt;
|
||||
linear->cxfer_mode = params->xfer_mode;
|
||||
linear->cmmap_shape = params->mmap_shape;
|
||||
params->format.sfmt = linear->sformat;
|
||||
params->xfer_mode = SND_PCM_XFER_UNSPECIFIED;
|
||||
params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;
|
||||
err = snd_pcm_params(slave, params);
|
||||
params->format.sfmt = linear->cformat;
|
||||
params->xfer_mode = linear->cxfer_mode;
|
||||
params->mmap_shape = linear->cmmap_shape;
|
||||
if (slave->valid_setup) {
|
||||
int r = snd_pcm_mmap_data(slave, NULL);
|
||||
assert(r >= 0);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int snd_pcm_linear_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
|
||||
{
|
||||
snd_pcm_linear_t *linear = pcm->private;
|
||||
int err = snd_pcm_setup(linear->plug.slave, setup);
|
||||
if (err < 0)
|
||||
return err;
|
||||
assert(linear->sformat == setup->format.sfmt);
|
||||
|
||||
if (linear->cxfer_mode == SND_PCM_XFER_UNSPECIFIED)
|
||||
setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED;
|
||||
else
|
||||
setup->xfer_mode = linear->cxfer_mode;
|
||||
if (linear->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED)
|
||||
setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED;
|
||||
else
|
||||
setup->mmap_shape = linear->cmmap_shape;
|
||||
setup->format.sfmt = linear->cformat;
|
||||
setup->mmap_bytes = 0;
|
||||
if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
|
||||
linear->conv_idx = conv_index(linear->cformat,
|
||||
linear->sformat);
|
||||
else
|
||||
linear->conv_idx = conv_index(linear->sformat,
|
||||
linear->cformat);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_linear_write_areas(snd_pcm_t *pcm,
|
||||
snd_pcm_channel_area_t *areas,
|
||||
size_t offset,
|
||||
size_t size,
|
||||
size_t *slave_sizep)
|
||||
{
|
||||
snd_pcm_linear_t *linear = pcm->private;
|
||||
snd_pcm_t *slave = linear->plug.slave;
|
||||
size_t xfer = 0;
|
||||
ssize_t err = 0;
|
||||
if (slave_sizep && *slave_sizep < size)
|
||||
size = *slave_sizep;
|
||||
assert(size > 0);
|
||||
while (xfer < size) {
|
||||
size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer);
|
||||
linear_transfer(areas, offset,
|
||||
slave->mmap_areas, snd_pcm_mmap_offset(slave),
|
||||
frames, pcm->setup.format.channels, linear->conv_idx);
|
||||
err = snd_pcm_mmap_forward(slave, frames);
|
||||
if (err < 0)
|
||||
break;
|
||||
assert((size_t)err == frames);
|
||||
offset += err;
|
||||
xfer += err;
|
||||
snd_pcm_mmap_hw_forward(pcm, err);
|
||||
}
|
||||
if (xfer > 0) {
|
||||
if (slave_sizep)
|
||||
*slave_sizep = xfer;
|
||||
return xfer;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_linear_read_areas(snd_pcm_t *pcm,
|
||||
snd_pcm_channel_area_t *areas,
|
||||
size_t offset,
|
||||
size_t size,
|
||||
size_t *slave_sizep)
|
||||
{
|
||||
snd_pcm_linear_t *linear = pcm->private;
|
||||
snd_pcm_t *slave = linear->plug.slave;
|
||||
size_t xfer = 0;
|
||||
ssize_t err = 0;
|
||||
if (slave_sizep && *slave_sizep < size)
|
||||
size = *slave_sizep;
|
||||
assert(size > 0);
|
||||
while (xfer < size) {
|
||||
size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer);
|
||||
linear_transfer(slave->mmap_areas, snd_pcm_mmap_offset(slave),
|
||||
areas, offset,
|
||||
frames, pcm->setup.format.channels, linear->conv_idx);
|
||||
err = snd_pcm_mmap_forward(slave, frames);
|
||||
if (err < 0)
|
||||
break;
|
||||
assert((size_t)err == frames);
|
||||
offset += err;
|
||||
xfer += err;
|
||||
snd_pcm_mmap_hw_forward(pcm, err);
|
||||
}
|
||||
if (xfer > 0) {
|
||||
if (slave_sizep)
|
||||
*slave_sizep = xfer;
|
||||
return xfer;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void snd_pcm_linear_dump(snd_pcm_t *pcm, FILE *fp)
|
||||
{
|
||||
snd_pcm_linear_t *linear = pcm->private;
|
||||
fprintf(fp, "Linear conversion PCM (%s)\n",
|
||||
snd_pcm_format_name(linear->sformat));
|
||||
if (pcm->valid_setup) {
|
||||
fprintf(fp, "Its setup is:\n");
|
||||
snd_pcm_dump_setup(pcm, fp);
|
||||
}
|
||||
fprintf(fp, "Slave: ");
|
||||
snd_pcm_dump(linear->plug.slave, fp);
|
||||
}
|
||||
|
||||
struct snd_pcm_ops snd_pcm_linear_ops = {
|
||||
close: snd_pcm_plugin_close,
|
||||
info: snd_pcm_plugin_info,
|
||||
params_info: snd_pcm_linear_params_info,
|
||||
params: snd_pcm_linear_params,
|
||||
setup: snd_pcm_linear_setup,
|
||||
channel_info: snd_pcm_plugin_channel_info,
|
||||
channel_params: snd_pcm_plugin_channel_params,
|
||||
channel_setup: snd_pcm_plugin_channel_setup,
|
||||
dump: snd_pcm_linear_dump,
|
||||
nonblock: snd_pcm_plugin_nonblock,
|
||||
mmap_status: snd_pcm_plugin_mmap_status,
|
||||
mmap_control: snd_pcm_plugin_mmap_control,
|
||||
mmap_data: snd_pcm_plugin_mmap_data,
|
||||
munmap_status: snd_pcm_plugin_munmap_status,
|
||||
munmap_control: snd_pcm_plugin_munmap_control,
|
||||
munmap_data: snd_pcm_plugin_munmap_data,
|
||||
};
|
||||
|
||||
int snd_pcm_linear_open(snd_pcm_t **handlep, int sformat, snd_pcm_t *slave, int close_slave)
|
||||
{
|
||||
snd_pcm_t *handle;
|
||||
snd_pcm_linear_t *linear;
|
||||
int err;
|
||||
assert(handlep && slave);
|
||||
if (snd_pcm_format_linear(sformat) != 1)
|
||||
return -EINVAL;
|
||||
linear = calloc(1, sizeof(snd_pcm_linear_t));
|
||||
if (!linear) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
linear->sformat = sformat;
|
||||
linear->plug.read = snd_pcm_linear_read_areas;
|
||||
linear->plug.write = snd_pcm_linear_write_areas;
|
||||
linear->plug.slave = slave;
|
||||
linear->plug.close_slave = close_slave;
|
||||
|
||||
handle = calloc(1, sizeof(snd_pcm_t));
|
||||
if (!handle) {
|
||||
free(linear);
|
||||
return -ENOMEM;
|
||||
}
|
||||
handle->type = SND_PCM_TYPE_LINEAR;
|
||||
handle->stream = slave->stream;
|
||||
handle->ops = &snd_pcm_linear_ops;
|
||||
handle->op_arg = handle;
|
||||
handle->fast_ops = &snd_pcm_plugin_fast_ops;
|
||||
handle->fast_op_arg = handle;
|
||||
handle->mode = slave->mode;
|
||||
handle->private = linear;
|
||||
err = snd_pcm_init(handle);
|
||||
if (err < 0) {
|
||||
snd_pcm_close(handle);
|
||||
return err;
|
||||
}
|
||||
*handlep = handle;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _snd_pcm_linear_open(snd_pcm_t **pcmp, char *name,
|
||||
snd_config_t *conf,
|
||||
int stream, int mode)
|
||||
{
|
||||
snd_config_iterator_t i;
|
||||
char *sname = NULL;
|
||||
int err;
|
||||
snd_pcm_t *spcm;
|
||||
int sformat = -1;
|
||||
snd_config_foreach(i, conf) {
|
||||
snd_config_t *n = snd_config_entry(i);
|
||||
if (strcmp(n->id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "type") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "stream") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "sname") == 0) {
|
||||
err = snd_config_string_get(n, &sname);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(n->id, "sformat") == 0) {
|
||||
char *f;
|
||||
err = snd_config_string_get(n, &f);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
sformat = snd_pcm_format_value(f);
|
||||
if (sformat < 0)
|
||||
return -EINVAL;
|
||||
if (snd_pcm_format_linear(sformat) != 1)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!sname || !sformat)
|
||||
return -EINVAL;
|
||||
/* This is needed cause snd_config_update may destroy config */
|
||||
sname = strdup(sname);
|
||||
if (!sname)
|
||||
return -ENOMEM;
|
||||
err = snd_pcm_open(&spcm, sname, stream, mode);
|
||||
free(sname);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_pcm_linear_open(pcmp, sformat, spcm, 1);
|
||||
if (err < 0)
|
||||
snd_pcm_close(spcm);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -20,43 +20,49 @@
|
|||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/uio.h>
|
||||
#include <errno.h>
|
||||
#include "asoundlib.h"
|
||||
|
||||
struct snd_pcm_ops {
|
||||
int (*close)(snd_pcm_t *pcm);
|
||||
int (*nonblock)(snd_pcm_t *pcm, int nonblock);
|
||||
int (*info)(snd_pcm_t *pcm, snd_pcm_info_t *info);
|
||||
int (*params_info)(snd_pcm_t *pcm, snd_pcm_params_info_t *info);
|
||||
int (*params)(snd_pcm_t *pcm, snd_pcm_params_t *params);
|
||||
int (*setup)(snd_pcm_t *pcm, snd_pcm_setup_t *setup);
|
||||
void (*dump)(snd_pcm_t *pcm, FILE *fp);
|
||||
};
|
||||
|
||||
struct snd_pcm_fast_ops {
|
||||
int (*nonblock)(snd_pcm_t *pcm, int nonblock);
|
||||
int (*status)(snd_pcm_t *pcm, snd_pcm_status_t *status);
|
||||
int (*channel_info)(snd_pcm_t *pcm, snd_pcm_channel_info_t *info);
|
||||
int (*channel_params)(snd_pcm_t *pcm, snd_pcm_channel_params_t *params);
|
||||
int (*channel_setup)(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup);
|
||||
void (*dump)(snd_pcm_t *pcm, FILE *fp);
|
||||
int (*mmap_status)(snd_pcm_t *pcm);
|
||||
int (*mmap_control)(snd_pcm_t *pcm);
|
||||
int (*mmap_data)(snd_pcm_t *pcm);
|
||||
int (*munmap_status)(snd_pcm_t *pcm);
|
||||
int (*munmap_control)(snd_pcm_t *pcm);
|
||||
int (*munmap_data)(snd_pcm_t *pcm);
|
||||
};
|
||||
|
||||
struct snd_pcm_fast_ops {
|
||||
int (*status)(snd_pcm_t *pcm, snd_pcm_status_t *status);
|
||||
int (*prepare)(snd_pcm_t *pcm);
|
||||
int (*go)(snd_pcm_t *pcm);
|
||||
int (*drain)(snd_pcm_t *pcm);
|
||||
int (*start)(snd_pcm_t *pcm);
|
||||
int (*stop)(snd_pcm_t *pcm);
|
||||
int (*flush)(snd_pcm_t *pcm);
|
||||
int (*pause)(snd_pcm_t *pcm, int enable);
|
||||
int (*state)(snd_pcm_t *pcm);
|
||||
ssize_t (*hw_ptr)(snd_pcm_t *pcm, int update);
|
||||
int (*delay)(snd_pcm_t *pcm, ssize_t *delayp);
|
||||
ssize_t (*appl_ptr)(snd_pcm_t *pcm, off_t offset);
|
||||
ssize_t (*write)(snd_pcm_t *pcm, snd_timestamp_t *tstamp, const void *buffer, size_t size);
|
||||
ssize_t (*writev)(snd_pcm_t *pcm, snd_timestamp_t *tstamp, const struct iovec *vector, unsigned long count);
|
||||
ssize_t (*read)(snd_pcm_t *pcm, snd_timestamp_t *tstamp, void *buffer, size_t size);
|
||||
ssize_t (*readv)(snd_pcm_t *pcm, snd_timestamp_t *tstamp, const struct iovec *vector, unsigned long count);
|
||||
int (*file_descriptor)(snd_pcm_t *pcm);
|
||||
int (*channels_mask)(snd_pcm_t *pcm, bitset_t *client_vmask);
|
||||
int (*mmap_status)(snd_pcm_t *pcm, snd_pcm_mmap_status_t **status);
|
||||
int (*mmap_control)(snd_pcm_t *pcm, snd_pcm_mmap_control_t **control);
|
||||
int (*mmap_data)(snd_pcm_t *pcm, void **buffer, size_t bsize);
|
||||
int (*munmap_status)(snd_pcm_t *pcm, snd_pcm_mmap_status_t *status);
|
||||
int (*munmap_control)(snd_pcm_t *pcm, snd_pcm_mmap_control_t *control);
|
||||
int (*munmap_data)(snd_pcm_t *pcm, void *buffer, size_t bsize);
|
||||
ssize_t (*writei)(snd_pcm_t *pcm, const void *buffer, size_t size);
|
||||
ssize_t (*writen)(snd_pcm_t *pcm, void **bufs, size_t size);
|
||||
ssize_t (*readi)(snd_pcm_t *pcm, void *buffer, size_t size);
|
||||
ssize_t (*readn)(snd_pcm_t *pcm, void **bufs, size_t size);
|
||||
int (*poll_descriptor)(snd_pcm_t *pcm);
|
||||
int (*channels_mask)(snd_pcm_t *pcm, bitset_t *cmask);
|
||||
ssize_t (*avail_update)(snd_pcm_t *pcm);
|
||||
ssize_t (*mmap_forward)(snd_pcm_t *pcm, size_t size);
|
||||
};
|
||||
|
||||
struct snd_pcm {
|
||||
|
|
@ -65,13 +71,12 @@ struct snd_pcm {
|
|||
int mode;
|
||||
int valid_setup;
|
||||
snd_pcm_setup_t setup;
|
||||
snd_pcm_channel_area_t *channels;
|
||||
size_t bits_per_sample;
|
||||
size_t bits_per_frame;
|
||||
snd_pcm_mmap_status_t *mmap_status;
|
||||
snd_pcm_mmap_control_t *mmap_control;
|
||||
char *mmap_data;
|
||||
enum { _INTERLEAVED, _NONINTERLEAVED, _COMPLEX } mmap_type;
|
||||
void *mmap_data;
|
||||
snd_pcm_channel_area_t *mmap_areas;
|
||||
struct snd_pcm_ops *ops;
|
||||
struct snd_pcm_fast_ops *fast_ops;
|
||||
snd_pcm_t *op_arg;
|
||||
|
|
@ -79,74 +84,67 @@ struct snd_pcm {
|
|||
void *private;
|
||||
};
|
||||
|
||||
#undef snd_pcm_plug_t
|
||||
typedef struct snd_pcm_plug {
|
||||
int close_slave;
|
||||
snd_pcm_t *handle;
|
||||
snd_pcm_t *slave;
|
||||
snd_pcm_plugin_t *first;
|
||||
snd_pcm_plugin_t *last;
|
||||
size_t frames_alloc;
|
||||
} snd_pcm_plug_t;
|
||||
int snd_pcm_init(snd_pcm_t *pcm);
|
||||
void snd_pcm_areas_from_buf(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, void *buf);
|
||||
void snd_pcm_areas_from_bufs(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, void **bufs);
|
||||
|
||||
unsigned int snd_pcm_plug_formats(unsigned int slave_formats);
|
||||
int snd_pcm_plug_slave_fmt(int format, snd_pcm_params_info_t *slave_info);
|
||||
int snd_pcm_plug_slave_rate(unsigned int rate, snd_pcm_params_info_t *slave_info);
|
||||
int snd_pcm_plug_slave_format(snd_pcm_format_t *format,
|
||||
snd_pcm_info_t *slave_info,
|
||||
snd_pcm_params_info_t *slave_params_info,
|
||||
snd_pcm_format_t *slave_format);
|
||||
int snd_pcm_plug_format_plugins(snd_pcm_plug_t *plug,
|
||||
snd_pcm_format_t *format,
|
||||
snd_pcm_format_t *slave_format);
|
||||
int snd_pcm_mmap_status(snd_pcm_t *pcm, snd_pcm_mmap_status_t **status);
|
||||
int snd_pcm_mmap_control(snd_pcm_t *pcm, snd_pcm_mmap_control_t **control);
|
||||
int snd_pcm_mmap_data(snd_pcm_t *pcm, void **buffer);
|
||||
int snd_pcm_munmap_status(snd_pcm_t *pcm);
|
||||
int snd_pcm_munmap_control(snd_pcm_t *pcm);
|
||||
int snd_pcm_munmap_data(snd_pcm_t *pcm);
|
||||
int snd_pcm_mmap_ready(snd_pcm_t *pcm);
|
||||
ssize_t snd_pcm_mmap_appl_ptr(snd_pcm_t *pcm, off_t offset);
|
||||
void snd_pcm_mmap_appl_forward(snd_pcm_t *pcm, size_t frames);
|
||||
void snd_pcm_mmap_hw_forward(snd_pcm_t *pcm, size_t frames);
|
||||
size_t snd_pcm_mmap_hw_offset(snd_pcm_t *pcm);
|
||||
size_t snd_pcm_mmap_avail(snd_pcm_t *pcm);
|
||||
size_t snd_pcm_mmap_playback_xfer(snd_pcm_t *pcm, size_t frames);
|
||||
size_t snd_pcm_mmap_capture_xfer(snd_pcm_t *pcm, size_t frames);
|
||||
|
||||
ssize_t snd_pcm_plug_write_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *src_channels, size_t size);
|
||||
ssize_t snd_pcm_plug_read_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *dst_channels_final, size_t size);
|
||||
ssize_t snd_pcm_plug_client_channels_iovec(snd_pcm_plug_t *plug,
|
||||
const struct iovec *vector, unsigned long count,
|
||||
snd_pcm_plugin_channel_t **channels);
|
||||
ssize_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *plug,
|
||||
char *buf, size_t count,
|
||||
snd_pcm_plugin_channel_t **channels);
|
||||
typedef ssize_t (*snd_pcm_xfer_areas_func_t)(snd_pcm_t *pcm,
|
||||
snd_pcm_channel_area_t *areas,
|
||||
size_t offset, size_t size,
|
||||
size_t *slave_sizep);
|
||||
|
||||
int snd_pcm_plug_playback_channels_mask(snd_pcm_plug_t *plug,
|
||||
bitset_t *client_vmask);
|
||||
int snd_pcm_plug_capture_channels_mask(snd_pcm_plug_t *plug,
|
||||
bitset_t *client_vmask);
|
||||
ssize_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin,
|
||||
size_t frames,
|
||||
snd_pcm_plugin_channel_t **channels);
|
||||
ssize_t snd_pcm_read_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas,
|
||||
size_t offset, size_t size,
|
||||
snd_pcm_xfer_areas_func_t func);
|
||||
ssize_t snd_pcm_write_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas,
|
||||
size_t offset, size_t size,
|
||||
snd_pcm_xfer_areas_func_t func);
|
||||
ssize_t snd_pcm_read_mmap(snd_pcm_t *pcm, size_t size);
|
||||
ssize_t snd_pcm_write_mmap(snd_pcm_t *pcm, size_t size);
|
||||
|
||||
void *snd_pcm_plug_buf_alloc(snd_pcm_plug_t *plug, size_t size);
|
||||
void snd_pcm_plug_buf_unlock(snd_pcm_plug_t *pcm, void *ptr);
|
||||
|
||||
#define ROUTE_PLUGIN_RESOLUTION 16
|
||||
|
||||
int getput_index(int format);
|
||||
int conv_index(int src_format, int dst_format);
|
||||
|
||||
#ifdef PLUGIN_DEBUG
|
||||
#define pdprintf( args... ) fprintf(stderr, "plugin: " ##args)
|
||||
#else
|
||||
#define pdprintf( args... ) { ; }
|
||||
#endif
|
||||
|
||||
static inline size_t snd_pcm_mmap_playback_avail(snd_pcm_t *str)
|
||||
static inline size_t snd_pcm_mmap_playback_avail(snd_pcm_t *pcm)
|
||||
{
|
||||
ssize_t avail;
|
||||
avail = str->mmap_status->hw_ptr + str->setup.buffer_size - str->mmap_control->appl_ptr;
|
||||
avail = pcm->mmap_status->hw_ptr + pcm->setup.buffer_size - pcm->mmap_control->appl_ptr;
|
||||
if (avail < 0)
|
||||
avail += str->setup.boundary;
|
||||
avail += pcm->setup.boundary;
|
||||
return avail;
|
||||
}
|
||||
|
||||
static inline size_t snd_pcm_mmap_capture_avail(snd_pcm_t *str)
|
||||
static inline size_t snd_pcm_mmap_capture_avail(snd_pcm_t *pcm)
|
||||
{
|
||||
ssize_t avail;
|
||||
avail = str->mmap_status->hw_ptr - str->mmap_control->appl_ptr;
|
||||
avail = pcm->mmap_status->hw_ptr - pcm->mmap_control->appl_ptr;
|
||||
if (avail < 0)
|
||||
avail += str->setup.boundary;
|
||||
avail += pcm->setup.boundary;
|
||||
return avail;
|
||||
}
|
||||
|
||||
#define snd_pcm_plug_stream(plug) ((plug)->handle->stream)
|
||||
static inline void *snd_pcm_channel_area_addr(snd_pcm_channel_area_t *area, size_t offset)
|
||||
{
|
||||
size_t bitofs = area->first + area->step * offset;
|
||||
assert(bitofs % 8 == 0);
|
||||
return area->addr + bitofs / 8;
|
||||
}
|
||||
|
||||
static inline size_t snd_pcm_channel_area_step(snd_pcm_channel_area_t *area)
|
||||
{
|
||||
assert(area->step % 8 == 0);
|
||||
return area->step / 8;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@
|
|||
#include "../include/driver.h"
|
||||
#include "../include/pcm.h"
|
||||
#include "../include/pcm_plugin.h"
|
||||
#define bswap_16 swab16
|
||||
#define bswap_32 swab32
|
||||
#define bswap_64 swab64
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -26,461 +26,313 @@
|
|||
#include <sys/uio.h>
|
||||
#include "pcm_local.h"
|
||||
|
||||
int snd_pcm_avail(snd_pcm_t *handle, ssize_t *frames)
|
||||
size_t snd_pcm_mmap_avail(snd_pcm_t *pcm)
|
||||
{
|
||||
assert(handle);
|
||||
assert(handle->mmap_status && handle->mmap_control);
|
||||
if (handle->stream == SND_PCM_STREAM_PLAYBACK)
|
||||
*frames = snd_pcm_mmap_playback_avail(handle);
|
||||
assert(pcm);
|
||||
assert(pcm->mmap_status && pcm->mmap_control);
|
||||
if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
|
||||
return snd_pcm_mmap_playback_avail(pcm);
|
||||
else
|
||||
*frames = snd_pcm_mmap_capture_avail(handle);
|
||||
return snd_pcm_mmap_capture_avail(pcm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_mmap_playback_ready(snd_pcm_t *handle)
|
||||
static int snd_pcm_mmap_playback_ready(snd_pcm_t *pcm)
|
||||
{
|
||||
if (handle->mmap_status->state == SND_PCM_STATE_XRUN)
|
||||
if (pcm->mmap_status->state == SND_PCM_STATE_XRUN)
|
||||
return -EPIPE;
|
||||
return snd_pcm_mmap_playback_avail(handle) >= handle->setup.avail_min;
|
||||
return snd_pcm_mmap_playback_avail(pcm) >= pcm->setup.avail_min;
|
||||
}
|
||||
|
||||
static int snd_pcm_mmap_capture_ready(snd_pcm_t *handle)
|
||||
static int snd_pcm_mmap_capture_ready(snd_pcm_t *pcm)
|
||||
{
|
||||
int ret = 0;
|
||||
if (handle->mmap_status->state == SND_PCM_STATE_XRUN) {
|
||||
if (pcm->mmap_status->state == SND_PCM_STATE_XRUN) {
|
||||
ret = -EPIPE;
|
||||
if (handle->setup.xrun_mode == SND_PCM_XRUN_DRAIN)
|
||||
if (pcm->setup.xrun_act == SND_PCM_XRUN_ACT_DRAIN)
|
||||
return -EPIPE;
|
||||
}
|
||||
if (snd_pcm_mmap_capture_avail(handle) >= handle->setup.avail_min)
|
||||
if (snd_pcm_mmap_capture_avail(pcm) >= pcm->setup.avail_min)
|
||||
return 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int snd_pcm_mmap_ready(snd_pcm_t *handle)
|
||||
int snd_pcm_mmap_ready(snd_pcm_t *pcm)
|
||||
{
|
||||
assert(handle);
|
||||
assert(handle->mmap_status && handle->mmap_control);
|
||||
assert(handle->mmap_status->state >= SND_PCM_STATE_PREPARED);
|
||||
if (handle->stream == SND_PCM_STREAM_PLAYBACK) {
|
||||
return snd_pcm_mmap_playback_ready(handle);
|
||||
assert(pcm);
|
||||
assert(pcm->mmap_status && pcm->mmap_control);
|
||||
assert(pcm->mmap_status->state >= SND_PCM_STATE_PREPARED);
|
||||
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
|
||||
return snd_pcm_mmap_playback_ready(pcm);
|
||||
} else {
|
||||
return snd_pcm_mmap_capture_ready(handle);
|
||||
return snd_pcm_mmap_capture_ready(pcm);
|
||||
}
|
||||
}
|
||||
|
||||
static size_t snd_pcm_mmap_playback_xfer(snd_pcm_t *handle, size_t frames)
|
||||
size_t snd_pcm_mmap_playback_xfer(snd_pcm_t *pcm, size_t frames)
|
||||
{
|
||||
snd_pcm_mmap_control_t *control = handle->mmap_control;
|
||||
snd_pcm_mmap_control_t *control = pcm->mmap_control;
|
||||
size_t cont;
|
||||
size_t avail = snd_pcm_mmap_playback_avail(handle);
|
||||
size_t avail = snd_pcm_mmap_playback_avail(pcm);
|
||||
if (avail < frames)
|
||||
frames = avail;
|
||||
cont = handle->setup.buffer_size - control->appl_ptr % handle->setup.buffer_size;
|
||||
cont = pcm->setup.buffer_size - control->appl_ptr % pcm->setup.buffer_size;
|
||||
if (cont < frames)
|
||||
frames = cont;
|
||||
return frames;
|
||||
}
|
||||
|
||||
static size_t snd_pcm_mmap_capture_xfer(snd_pcm_t *handle, size_t frames)
|
||||
size_t snd_pcm_mmap_capture_xfer(snd_pcm_t *pcm, size_t frames)
|
||||
{
|
||||
snd_pcm_mmap_control_t *control = handle->mmap_control;
|
||||
snd_pcm_mmap_control_t *control = pcm->mmap_control;
|
||||
size_t cont;
|
||||
size_t avail = snd_pcm_mmap_capture_avail(handle);
|
||||
size_t avail = snd_pcm_mmap_capture_avail(pcm);
|
||||
if (avail < frames)
|
||||
frames = avail;
|
||||
cont = handle->setup.buffer_size - control->appl_ptr % handle->setup.buffer_size;
|
||||
cont = pcm->setup.buffer_size - control->appl_ptr % pcm->setup.buffer_size;
|
||||
if (cont < frames)
|
||||
frames = cont;
|
||||
return frames;
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_mmap_xfer(snd_pcm_t *handle, size_t frames)
|
||||
size_t snd_pcm_mmap_xfer(snd_pcm_t *pcm, size_t frames)
|
||||
{
|
||||
assert(handle);
|
||||
assert(handle->mmap_status && handle->mmap_control);
|
||||
if (handle->stream == SND_PCM_STREAM_PLAYBACK)
|
||||
return snd_pcm_mmap_playback_xfer(handle, frames);
|
||||
assert(pcm);
|
||||
assert(pcm->mmap_status && pcm->mmap_control);
|
||||
if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
|
||||
return snd_pcm_mmap_playback_xfer(pcm, frames);
|
||||
else
|
||||
return snd_pcm_mmap_capture_xfer(handle, frames);
|
||||
return snd_pcm_mmap_capture_xfer(pcm, frames);
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_mmap_offset(snd_pcm_t *handle)
|
||||
size_t snd_pcm_mmap_offset(snd_pcm_t *pcm)
|
||||
{
|
||||
assert(handle);
|
||||
assert(handle->mmap_control);
|
||||
return handle->mmap_control->appl_ptr % handle->setup.buffer_size;
|
||||
assert(pcm);
|
||||
assert(pcm->mmap_control);
|
||||
return pcm->mmap_control->appl_ptr % pcm->setup.buffer_size;
|
||||
}
|
||||
|
||||
int snd_pcm_mmap_state(snd_pcm_t *handle)
|
||||
size_t snd_pcm_mmap_hw_offset(snd_pcm_t *pcm)
|
||||
{
|
||||
assert(handle);
|
||||
assert(handle->mmap_status);
|
||||
return handle->mmap_status->state;
|
||||
assert(pcm);
|
||||
assert(pcm->mmap_status);
|
||||
return pcm->mmap_status->hw_ptr % pcm->setup.buffer_size;
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_mmap_hw_ptr(snd_pcm_t *handle)
|
||||
int snd_pcm_mmap_state(snd_pcm_t *pcm)
|
||||
{
|
||||
assert(handle);
|
||||
assert(handle->mmap_status);
|
||||
return handle->mmap_status->hw_ptr;
|
||||
assert(pcm);
|
||||
assert(pcm->mmap_status);
|
||||
return pcm->mmap_status->state;
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_mmap_appl_ptr(snd_pcm_t *handle, off_t offset)
|
||||
ssize_t snd_pcm_mmap_hw_ptr(snd_pcm_t *pcm)
|
||||
{
|
||||
assert(pcm);
|
||||
assert(pcm->mmap_status);
|
||||
return pcm->mmap_status->hw_ptr;
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_mmap_appl_ptr(snd_pcm_t *pcm, off_t offset)
|
||||
{
|
||||
ssize_t appl_ptr;
|
||||
assert(handle);
|
||||
assert(handle->mmap_status && handle->mmap_control);
|
||||
assert(offset == 0 || handle->type == SND_PCM_TYPE_HW);
|
||||
appl_ptr = handle->mmap_control->appl_ptr;
|
||||
assert(pcm);
|
||||
assert(pcm->mmap_status && pcm->mmap_control);
|
||||
assert(offset == 0 || pcm->type == SND_PCM_TYPE_HW);
|
||||
appl_ptr = pcm->mmap_control->appl_ptr;
|
||||
if (offset == 0)
|
||||
return appl_ptr;
|
||||
switch (handle->mmap_status->state) {
|
||||
switch (pcm->mmap_status->state) {
|
||||
case SND_PCM_STATE_RUNNING:
|
||||
if (handle->setup.mode == SND_PCM_MODE_FRAME)
|
||||
snd_pcm_hw_ptr(handle, 1);
|
||||
if (pcm->setup.xrun_mode == SND_PCM_XRUN_ASAP)
|
||||
snd_pcm_avail_update(pcm);
|
||||
break;
|
||||
case SND_PCM_STATE_READY:
|
||||
case SND_PCM_STATE_NOTREADY:
|
||||
return -EBADFD;
|
||||
}
|
||||
if (offset < 0) {
|
||||
if (offset < -(ssize_t)handle->setup.buffer_size)
|
||||
offset = -(ssize_t)handle->setup.buffer_size;
|
||||
else
|
||||
offset -= offset % handle->setup.align;
|
||||
if (offset < -(ssize_t)pcm->setup.buffer_size)
|
||||
offset = -(ssize_t)pcm->setup.buffer_size;
|
||||
appl_ptr += offset;
|
||||
if (appl_ptr < 0)
|
||||
appl_ptr += handle->setup.boundary;
|
||||
appl_ptr += pcm->setup.boundary;
|
||||
} else {
|
||||
size_t avail;
|
||||
if (handle->stream == SND_PCM_STREAM_PLAYBACK)
|
||||
avail = snd_pcm_mmap_playback_avail(handle);
|
||||
if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
|
||||
avail = snd_pcm_mmap_playback_avail(pcm);
|
||||
else
|
||||
avail = snd_pcm_mmap_capture_avail(handle);
|
||||
avail = snd_pcm_mmap_capture_avail(pcm);
|
||||
if ((size_t)offset > avail)
|
||||
offset = avail;
|
||||
offset -= offset % handle->setup.align;
|
||||
appl_ptr += offset;
|
||||
if ((size_t)appl_ptr >= handle->setup.boundary)
|
||||
appl_ptr -= handle->setup.boundary;
|
||||
if ((size_t)appl_ptr >= pcm->setup.boundary)
|
||||
appl_ptr -= pcm->setup.boundary;
|
||||
}
|
||||
handle->mmap_control->appl_ptr = appl_ptr;
|
||||
pcm->mmap_control->appl_ptr = appl_ptr;
|
||||
return appl_ptr;
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_mmap_write_areas(snd_pcm_t *handle, snd_pcm_channel_area_t *channels, size_t frames)
|
||||
void snd_pcm_mmap_appl_forward(snd_pcm_t *pcm, size_t frames)
|
||||
{
|
||||
snd_pcm_mmap_status_t *status;
|
||||
size_t offset = 0;
|
||||
size_t result = 0;
|
||||
int err;
|
||||
|
||||
assert(handle->mmap_data && handle->mmap_status && handle->mmap_control);
|
||||
status = handle->mmap_status;
|
||||
assert(status->state >= SND_PCM_STATE_PREPARED);
|
||||
if (handle->setup.mode == SND_PCM_MODE_FRAGMENT) {
|
||||
assert(frames % handle->setup.frag_size == 0);
|
||||
} else {
|
||||
if (status->state == SND_PCM_STATE_RUNNING &&
|
||||
handle->mode & SND_PCM_NONBLOCK)
|
||||
snd_pcm_hw_ptr(handle, 1);
|
||||
}
|
||||
while (frames > 0) {
|
||||
ssize_t mmap_offset;
|
||||
size_t frames1;
|
||||
int ready = snd_pcm_mmap_playback_ready(handle);
|
||||
if (ready < 0)
|
||||
return ready;
|
||||
if (!ready) {
|
||||
struct pollfd pfd;
|
||||
if (status->state != SND_PCM_STATE_RUNNING)
|
||||
return result > 0 ? result : -EPIPE;
|
||||
if (handle->mode & SND_PCM_NONBLOCK)
|
||||
return result > 0 ? result : -EAGAIN;
|
||||
pfd.fd = snd_pcm_file_descriptor(handle);
|
||||
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(handle));
|
||||
}
|
||||
frames1 = snd_pcm_mmap_playback_xfer(handle, frames);
|
||||
assert(frames1 > 0);
|
||||
mmap_offset = snd_pcm_mmap_offset(handle);
|
||||
snd_pcm_areas_copy(channels, offset, handle->channels, mmap_offset, handle->setup.format.channels, frames1, handle->setup.format.format);
|
||||
if (status->state == SND_PCM_STATE_XRUN)
|
||||
return result > 0 ? result : -EPIPE;
|
||||
snd_pcm_appl_ptr(handle, frames1);
|
||||
frames -= frames1;
|
||||
offset += frames1;
|
||||
result += frames1;
|
||||
if (status->state == SND_PCM_STATE_PREPARED &&
|
||||
(handle->setup.start_mode == SND_PCM_START_DATA ||
|
||||
(handle->setup.start_mode == SND_PCM_START_FULL &&
|
||||
!snd_pcm_mmap_playback_ready(handle)))) {
|
||||
err = snd_pcm_go(handle);
|
||||
if (err < 0)
|
||||
return result > 0 ? result : err;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
size_t appl_ptr = pcm->mmap_control->appl_ptr;
|
||||
appl_ptr += frames;
|
||||
if (appl_ptr >= pcm->setup.boundary)
|
||||
appl_ptr -= pcm->setup.boundary;
|
||||
pcm->mmap_control->appl_ptr = appl_ptr;
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_mmap_write(snd_pcm_t *handle, const void *buffer, size_t frames)
|
||||
void snd_pcm_mmap_hw_forward(snd_pcm_t *pcm, size_t frames)
|
||||
{
|
||||
unsigned int nchannels;
|
||||
assert(handle);
|
||||
assert(handle->mmap_data && handle->mmap_status && handle->mmap_control);
|
||||
assert(frames == 0 || buffer);
|
||||
nchannels = handle->setup.format.channels;
|
||||
assert(handle->setup.format.interleave || nchannels == 1);
|
||||
{
|
||||
snd_pcm_channel_area_t channels[nchannels];
|
||||
unsigned int channel;
|
||||
for (channel = 0; channel < nchannels; ++channel) {
|
||||
channels[channel].addr = (char*)buffer;
|
||||
channels[channel].first = handle->bits_per_sample * channel;
|
||||
channels[channel].step = handle->bits_per_frame;
|
||||
}
|
||||
return snd_pcm_mmap_write_areas(handle, channels, frames);
|
||||
}
|
||||
size_t hw_ptr = pcm->mmap_status->hw_ptr;
|
||||
hw_ptr += frames;
|
||||
if (hw_ptr >= pcm->setup.boundary)
|
||||
hw_ptr -= pcm->setup.boundary;
|
||||
pcm->mmap_status->hw_ptr = hw_ptr;
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_mmap_writev(snd_pcm_t *handle, const struct iovec *vector, unsigned long vcount)
|
||||
ssize_t snd_pcm_mmap_write_areas(snd_pcm_t *pcm,
|
||||
snd_pcm_channel_area_t *areas,
|
||||
size_t offset,
|
||||
size_t size,
|
||||
size_t *slave_sizep)
|
||||
{
|
||||
size_t result = 0;
|
||||
unsigned int nchannels;
|
||||
assert(handle);
|
||||
assert(handle->mmap_data && handle->mmap_status && handle->mmap_control);
|
||||
assert(vcount == 0 || vector);
|
||||
nchannels = handle->setup.format.channels;
|
||||
if (handle->setup.format.interleave) {
|
||||
unsigned int b;
|
||||
for (b = 0; b < vcount; b++) {
|
||||
ssize_t ret;
|
||||
size_t frames = vector[b].iov_len;
|
||||
ret = snd_pcm_mmap_write(handle, vector[b].iov_base, frames);
|
||||
if (ret < 0) {
|
||||
if (result <= 0)
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
result += ret;
|
||||
}
|
||||
} else {
|
||||
snd_pcm_channel_area_t channels[nchannels];
|
||||
unsigned long bcount;
|
||||
unsigned int b;
|
||||
assert(vcount % nchannels == 0);
|
||||
bcount = vcount / nchannels;
|
||||
for (b = 0; b < bcount; b++) {
|
||||
unsigned int v;
|
||||
ssize_t ret;
|
||||
size_t frames = vector[0].iov_len;
|
||||
for (v = 0; v < nchannels; ++v) {
|
||||
assert(vector[v].iov_len == frames);
|
||||
channels[v].addr = vector[v].iov_base;
|
||||
channels[v].first = 0;
|
||||
channels[v].step = handle->bits_per_sample;
|
||||
}
|
||||
ret = snd_pcm_mmap_write_areas(handle, channels, frames);
|
||||
if (ret < 0) {
|
||||
if (result <= 0)
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
result += ret;
|
||||
if ((size_t)ret != frames)
|
||||
break;
|
||||
vector += nchannels;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_mmap_read_areas(snd_pcm_t *handle, snd_pcm_channel_area_t *channels, size_t frames)
|
||||
{
|
||||
snd_pcm_mmap_status_t *status;
|
||||
size_t offset = 0;
|
||||
size_t result = 0;
|
||||
int err;
|
||||
|
||||
assert(handle->mmap_data && handle->mmap_status && handle->mmap_control);
|
||||
status = handle->mmap_status;
|
||||
assert(status->state >= SND_PCM_STATE_PREPARED);
|
||||
if (handle->setup.mode == SND_PCM_MODE_FRAGMENT) {
|
||||
assert(frames % handle->setup.frag_size == 0);
|
||||
} else {
|
||||
if (status->state == SND_PCM_STATE_RUNNING &&
|
||||
handle->mode & SND_PCM_NONBLOCK)
|
||||
snd_pcm_hw_ptr(handle, 1);
|
||||
}
|
||||
if (status->state == SND_PCM_STATE_PREPARED &&
|
||||
handle->setup.start_mode == SND_PCM_START_DATA) {
|
||||
err = snd_pcm_go(handle);
|
||||
size_t xfer;
|
||||
ssize_t err = 0;
|
||||
if (slave_sizep && *slave_sizep < size)
|
||||
size = *slave_sizep;
|
||||
xfer = 0;
|
||||
while (xfer < size) {
|
||||
size_t frames = snd_pcm_mmap_playback_xfer(pcm, size - xfer);
|
||||
snd_pcm_areas_copy(areas, offset,
|
||||
pcm->mmap_areas, snd_pcm_mmap_offset(pcm),
|
||||
pcm->setup.format.channels,
|
||||
frames, pcm->setup.format.sfmt);
|
||||
err = snd_pcm_mmap_forward(pcm, frames);
|
||||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
assert((size_t)err == frames);
|
||||
offset += err;
|
||||
xfer += err;
|
||||
}
|
||||
while (frames > 0) {
|
||||
ssize_t mmap_offset;
|
||||
size_t frames1;
|
||||
int ready = snd_pcm_mmap_capture_ready(handle);
|
||||
if (ready < 0)
|
||||
return ready;
|
||||
if (!ready) {
|
||||
struct pollfd pfd;
|
||||
if (status->state != SND_PCM_STATE_RUNNING)
|
||||
return result > 0 ? result : -EPIPE;
|
||||
if (handle->mode & SND_PCM_NONBLOCK)
|
||||
return result > 0 ? result : -EAGAIN;
|
||||
pfd.fd = snd_pcm_file_descriptor(handle);
|
||||
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(handle));
|
||||
}
|
||||
frames1 = snd_pcm_mmap_capture_xfer(handle, frames);
|
||||
assert(frames1 > 0);
|
||||
mmap_offset = snd_pcm_mmap_offset(handle);
|
||||
snd_pcm_areas_copy(handle->channels, mmap_offset, channels, offset, handle->setup.format.channels, frames1, handle->setup.format.format);
|
||||
if (status->state == SND_PCM_STATE_XRUN &&
|
||||
handle->setup.xrun_mode == SND_PCM_XRUN_DRAIN)
|
||||
return result > 0 ? result : -EPIPE;
|
||||
snd_pcm_appl_ptr(handle, frames1);
|
||||
frames -= frames1;
|
||||
offset += frames1;
|
||||
result += frames1;
|
||||
if (xfer > 0) {
|
||||
if (slave_sizep)
|
||||
*slave_sizep = xfer;
|
||||
return xfer;
|
||||
}
|
||||
return result;
|
||||
return err;
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_mmap_read(snd_pcm_t *handle, void *buffer, size_t frames)
|
||||
ssize_t snd_pcm_mmap_read_areas(snd_pcm_t *pcm,
|
||||
snd_pcm_channel_area_t *areas,
|
||||
size_t offset,
|
||||
size_t size,
|
||||
size_t *slave_sizep)
|
||||
{
|
||||
unsigned int nchannels;
|
||||
assert(handle);
|
||||
assert(handle->mmap_data && handle->mmap_status && handle->mmap_control);
|
||||
assert(frames == 0 || buffer);
|
||||
nchannels = handle->setup.format.channels;
|
||||
assert(handle->setup.format.interleave || nchannels == 1);
|
||||
{
|
||||
snd_pcm_channel_area_t channels[nchannels];
|
||||
unsigned int channel;
|
||||
for (channel = 0; channel < nchannels; ++channel) {
|
||||
channels[channel].addr = (char*)buffer;
|
||||
channels[channel].first = handle->bits_per_sample * channel;
|
||||
channels[channel].step = handle->bits_per_frame;
|
||||
}
|
||||
return snd_pcm_mmap_read_areas(handle, channels, frames);
|
||||
size_t xfer;
|
||||
ssize_t err = 0;
|
||||
if (slave_sizep && *slave_sizep < size)
|
||||
size = *slave_sizep;
|
||||
xfer = 0;
|
||||
while (xfer < size) {
|
||||
size_t frames = snd_pcm_mmap_capture_xfer(pcm, size - xfer);
|
||||
snd_pcm_areas_copy(pcm->mmap_areas, snd_pcm_mmap_offset(pcm),
|
||||
areas, offset,
|
||||
pcm->setup.format.channels,
|
||||
frames, pcm->setup.format.sfmt);
|
||||
err = snd_pcm_mmap_forward(pcm, frames);
|
||||
if (err < 0)
|
||||
break;
|
||||
assert((size_t)err == frames);
|
||||
offset += err;
|
||||
xfer += err;
|
||||
}
|
||||
if (xfer > 0) {
|
||||
if (slave_sizep)
|
||||
*slave_sizep = xfer;
|
||||
return xfer;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_mmap_readv(snd_pcm_t *handle, const struct iovec *vector, unsigned long vcount)
|
||||
ssize_t snd_pcm_mmap_writei(snd_pcm_t *pcm, const void *buffer, size_t size)
|
||||
{
|
||||
size_t result = 0;
|
||||
unsigned int nchannels;
|
||||
assert(handle);
|
||||
assert(handle->mmap_data && handle->mmap_status && handle->mmap_control);
|
||||
assert(vcount == 0 || vector);
|
||||
nchannels = handle->setup.format.channels;
|
||||
if (handle->setup.format.interleave) {
|
||||
unsigned int b;
|
||||
for (b = 0; b < vcount; b++) {
|
||||
ssize_t ret;
|
||||
size_t frames = vector[b].iov_len;
|
||||
ret = snd_pcm_mmap_read(handle, vector[b].iov_base, frames);
|
||||
if (ret < 0) {
|
||||
if (result <= 0)
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
result += ret;
|
||||
}
|
||||
} else {
|
||||
snd_pcm_channel_area_t channels[nchannels];
|
||||
unsigned long bcount;
|
||||
unsigned int b;
|
||||
assert(vcount % nchannels == 0);
|
||||
bcount = vcount / nchannels;
|
||||
for (b = 0; b < bcount; b++) {
|
||||
unsigned int v;
|
||||
ssize_t ret;
|
||||
size_t frames = vector[0].iov_len;
|
||||
for (v = 0; v < nchannels; ++v) {
|
||||
assert(vector[v].iov_len == frames);
|
||||
channels[v].addr = vector[v].iov_base;
|
||||
channels[v].first = 0;
|
||||
channels[v].step = handle->bits_per_sample;
|
||||
}
|
||||
ret = snd_pcm_mmap_read_areas(handle, channels, frames);
|
||||
if (ret < 0) {
|
||||
if (result <= 0)
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
result += ret;
|
||||
if ((size_t)ret != frames)
|
||||
break;
|
||||
vector += nchannels;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
snd_pcm_channel_area_t areas[pcm->setup.format.channels];
|
||||
snd_pcm_areas_from_buf(pcm, areas, (void*)buffer);
|
||||
return snd_pcm_write_areas(pcm, areas, 0, size,
|
||||
snd_pcm_mmap_write_areas);
|
||||
}
|
||||
|
||||
int snd_pcm_mmap_status(snd_pcm_t *handle, snd_pcm_mmap_status_t **status)
|
||||
ssize_t snd_pcm_mmap_writen(snd_pcm_t *pcm, void **bufs, size_t size)
|
||||
{
|
||||
snd_pcm_channel_area_t areas[pcm->setup.format.channels];
|
||||
snd_pcm_areas_from_bufs(pcm, areas, bufs);
|
||||
return snd_pcm_write_areas(pcm, areas, 0, size,
|
||||
snd_pcm_mmap_write_areas);
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_mmap_readi(snd_pcm_t *pcm, void *buffer, size_t size)
|
||||
{
|
||||
snd_pcm_channel_area_t areas[pcm->setup.format.channels];
|
||||
snd_pcm_areas_from_buf(pcm, areas, buffer);
|
||||
return snd_pcm_read_areas(pcm, areas, 0, size,
|
||||
snd_pcm_mmap_read_areas);
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_mmap_readn(snd_pcm_t *pcm, void **bufs, size_t size)
|
||||
{
|
||||
snd_pcm_channel_area_t areas[pcm->setup.format.channels];
|
||||
snd_pcm_areas_from_bufs(pcm, areas, bufs);
|
||||
return snd_pcm_read_areas(pcm, areas, 0, size,
|
||||
snd_pcm_mmap_read_areas);
|
||||
}
|
||||
|
||||
int snd_pcm_mmap_status(snd_pcm_t *pcm, snd_pcm_mmap_status_t **status)
|
||||
{
|
||||
int err;
|
||||
assert(handle);
|
||||
assert(handle->valid_setup);
|
||||
if (handle->mmap_status) {
|
||||
assert(pcm);
|
||||
if (pcm->mmap_status) {
|
||||
if (status)
|
||||
*status = handle->mmap_status;
|
||||
*status = pcm->mmap_status;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((err = handle->fast_ops->mmap_status(handle->fast_op_arg, &handle->mmap_status)) < 0)
|
||||
if ((err = pcm->ops->mmap_status(pcm->op_arg)) < 0)
|
||||
return err;
|
||||
if (status)
|
||||
*status = handle->mmap_status;
|
||||
*status = pcm->mmap_status;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_mmap_control(snd_pcm_t *handle, snd_pcm_mmap_control_t **control)
|
||||
int snd_pcm_mmap_control(snd_pcm_t *pcm, snd_pcm_mmap_control_t **control)
|
||||
{
|
||||
int err;
|
||||
assert(handle);
|
||||
assert(handle->valid_setup);
|
||||
if (handle->mmap_control) {
|
||||
assert(pcm);
|
||||
if (pcm->mmap_control) {
|
||||
if (control)
|
||||
*control = handle->mmap_control;
|
||||
*control = pcm->mmap_control;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((err = handle->fast_ops->mmap_control(handle->fast_op_arg, &handle->mmap_control)) < 0)
|
||||
if ((err = pcm->ops->mmap_control(pcm->op_arg)) < 0)
|
||||
return err;
|
||||
if (control)
|
||||
*control = handle->mmap_control;
|
||||
*control = pcm->mmap_control;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_mmap_get_areas(snd_pcm_t *handle, snd_pcm_channel_area_t *areas)
|
||||
int snd_pcm_mmap_get_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas)
|
||||
{
|
||||
snd_pcm_channel_setup_t s;
|
||||
snd_pcm_channel_area_t *a, *ap;
|
||||
unsigned int channel;
|
||||
int interleaved = 1, noninterleaved = 1;
|
||||
int err;
|
||||
assert(handle);
|
||||
assert(handle->mmap_data);
|
||||
a = calloc(handle->setup.format.channels, sizeof(*areas));
|
||||
for (channel = 0, ap = a; channel < handle->setup.format.channels; ++channel, ++ap) {
|
||||
assert(pcm);
|
||||
assert(pcm->mmap_data);
|
||||
a = calloc(pcm->setup.format.channels, sizeof(*areas));
|
||||
for (channel = 0, ap = a; channel < pcm->setup.format.channels; ++channel, ++ap) {
|
||||
s.channel = channel;
|
||||
err = snd_pcm_channel_setup(handle, &s);
|
||||
err = snd_pcm_channel_setup(pcm, &s);
|
||||
if (err < 0) {
|
||||
free(a);
|
||||
return err;
|
||||
|
|
@ -488,110 +340,146 @@ int snd_pcm_mmap_get_areas(snd_pcm_t *handle, snd_pcm_channel_area_t *areas)
|
|||
if (areas)
|
||||
areas[channel] = s.area;
|
||||
*ap = s.area;
|
||||
if (ap->step != handle->bits_per_sample || ap->first != 0)
|
||||
noninterleaved = 0;
|
||||
if (ap->addr != a[0].addr ||
|
||||
ap->step != handle->bits_per_frame ||
|
||||
ap->first != channel * handle->bits_per_sample)
|
||||
interleaved = 0;
|
||||
}
|
||||
if (noninterleaved)
|
||||
handle->mmap_type = _NONINTERLEAVED;
|
||||
else if (interleaved)
|
||||
handle->mmap_type = _INTERLEAVED;
|
||||
else
|
||||
handle->mmap_type = _COMPLEX;
|
||||
handle->channels = a;
|
||||
pcm->mmap_areas = a;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_mmap_data(snd_pcm_t *handle, void **data)
|
||||
int snd_pcm_mmap_data(snd_pcm_t *pcm, void **data)
|
||||
{
|
||||
int err;
|
||||
assert(handle);
|
||||
assert(handle->valid_setup);
|
||||
if (handle->mmap_data) {
|
||||
assert(pcm);
|
||||
assert(pcm->valid_setup);
|
||||
if (pcm->mmap_data) {
|
||||
if (data)
|
||||
*data = handle->mmap_data;
|
||||
*data = pcm->mmap_data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (handle->setup.mmap_bytes == 0)
|
||||
return -ENXIO;
|
||||
if ((err = handle->fast_ops->mmap_data(handle->fast_op_arg, (void**)&handle->mmap_data, handle->setup.mmap_bytes)) < 0)
|
||||
if ((err = pcm->ops->mmap_data(pcm->op_arg)) < 0)
|
||||
return err;
|
||||
if (data)
|
||||
*data = handle->mmap_data;
|
||||
err = snd_pcm_mmap_get_areas(handle, NULL);
|
||||
*data = pcm->mmap_data;
|
||||
err = snd_pcm_mmap_get_areas(pcm, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_mmap(snd_pcm_t *handle, snd_pcm_mmap_status_t **status, snd_pcm_mmap_control_t **control, void **data)
|
||||
int snd_pcm_munmap_status(snd_pcm_t *pcm)
|
||||
{
|
||||
int err;
|
||||
err = snd_pcm_mmap_status(handle, status);
|
||||
if (err < 0)
|
||||
assert(pcm);
|
||||
assert(pcm->mmap_status);
|
||||
if ((err = pcm->ops->munmap_status(pcm->op_arg)) < 0)
|
||||
return err;
|
||||
err = snd_pcm_mmap_control(handle, control);
|
||||
if (err < 0) {
|
||||
snd_pcm_munmap_status(handle);
|
||||
pcm->mmap_status = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_munmap_control(snd_pcm_t *pcm)
|
||||
{
|
||||
int err;
|
||||
assert(pcm);
|
||||
assert(pcm->mmap_control);
|
||||
if ((err = pcm->ops->munmap_control(pcm->op_arg)) < 0)
|
||||
return err;
|
||||
pcm->mmap_control = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_munmap_data(snd_pcm_t *pcm)
|
||||
{
|
||||
int err;
|
||||
assert(pcm);
|
||||
assert(pcm->mmap_data);
|
||||
if ((err = pcm->ops->munmap_data(pcm->op_arg)) < 0)
|
||||
return err;
|
||||
free(pcm->mmap_areas);
|
||||
pcm->mmap_areas = 0;
|
||||
pcm->mmap_data = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_mmap(snd_pcm_t *pcm, void **data)
|
||||
{
|
||||
return snd_pcm_mmap_data(pcm, data);
|
||||
}
|
||||
|
||||
int snd_pcm_munmap(snd_pcm_t *pcm)
|
||||
{
|
||||
return snd_pcm_munmap_data(pcm);
|
||||
}
|
||||
|
||||
|
||||
ssize_t snd_pcm_write_mmap(snd_pcm_t *pcm, size_t size)
|
||||
{
|
||||
size_t xfer = 0;
|
||||
ssize_t err = 0;
|
||||
assert(size > 0);
|
||||
while (xfer < size) {
|
||||
size_t frames = size - xfer;
|
||||
size_t offset = snd_pcm_mmap_hw_offset(pcm);
|
||||
size_t cont = pcm->setup.buffer_size - offset;
|
||||
if (cont < frames)
|
||||
frames = cont;
|
||||
if (pcm->setup.xfer_mode == SND_PCM_XFER_INTERLEAVED) {
|
||||
snd_pcm_channel_area_t *a = pcm->mmap_areas;
|
||||
char *buf = snd_pcm_channel_area_addr(a, offset);
|
||||
assert(pcm->setup.mmap_shape == SND_PCM_MMAP_INTERLEAVED);
|
||||
err = snd_pcm_writei(pcm, buf, frames);
|
||||
} else {
|
||||
size_t channels = pcm->setup.format.channels;
|
||||
unsigned int c;
|
||||
void *bufs[channels];
|
||||
assert(pcm->setup.mmap_shape == SND_PCM_MMAP_NONINTERLEAVED);
|
||||
for (c = 0; c < channels; ++c) {
|
||||
snd_pcm_channel_area_t *a = &pcm->mmap_areas[c];
|
||||
bufs[c] = snd_pcm_channel_area_addr(a, offset);
|
||||
}
|
||||
err = snd_pcm_writen(pcm, bufs, frames);
|
||||
}
|
||||
if (err < 0)
|
||||
break;
|
||||
xfer += frames;
|
||||
}
|
||||
err = snd_pcm_mmap_data(handle, data);
|
||||
if (err < 0) {
|
||||
snd_pcm_munmap_status(handle);
|
||||
snd_pcm_munmap_control(handle);
|
||||
return err;
|
||||
if (xfer > 0)
|
||||
return xfer;
|
||||
return err;
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_read_mmap(snd_pcm_t *pcm, size_t size)
|
||||
{
|
||||
size_t xfer = 0;
|
||||
ssize_t err = 0;
|
||||
assert(size > 0);
|
||||
while (xfer < size) {
|
||||
size_t frames = size - xfer;
|
||||
size_t offset = snd_pcm_mmap_hw_offset(pcm);
|
||||
size_t cont = pcm->setup.buffer_size - offset;
|
||||
if (cont < frames)
|
||||
frames = cont;
|
||||
if (pcm->setup.xfer_mode == SND_PCM_XFER_INTERLEAVED) {
|
||||
snd_pcm_channel_area_t *a = pcm->mmap_areas;
|
||||
char *buf = snd_pcm_channel_area_addr(a, offset);
|
||||
assert(pcm->setup.mmap_shape == SND_PCM_MMAP_INTERLEAVED);
|
||||
err = snd_pcm_readi(pcm, buf, frames);
|
||||
} else {
|
||||
size_t channels = pcm->setup.format.channels;
|
||||
unsigned int c;
|
||||
void *bufs[channels];
|
||||
assert(pcm->setup.mmap_shape == SND_PCM_MMAP_NONINTERLEAVED);
|
||||
for (c = 0; c < channels; ++c) {
|
||||
snd_pcm_channel_area_t *a = &pcm->mmap_areas[c];
|
||||
bufs[c] = snd_pcm_channel_area_addr(a, offset);
|
||||
}
|
||||
err = snd_pcm_readn(pcm, bufs, frames);
|
||||
}
|
||||
if (err < 0)
|
||||
break;
|
||||
xfer += frames;
|
||||
}
|
||||
return 0;
|
||||
if (xfer > 0)
|
||||
return xfer;
|
||||
return err;
|
||||
}
|
||||
|
||||
int snd_pcm_munmap_status(snd_pcm_t *handle)
|
||||
{
|
||||
int err;
|
||||
assert(handle);
|
||||
assert(handle->mmap_status);
|
||||
if ((err = handle->fast_ops->munmap_status(handle->fast_op_arg, handle->mmap_status)) < 0)
|
||||
return err;
|
||||
handle->mmap_status = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_munmap_control(snd_pcm_t *handle)
|
||||
{
|
||||
int err;
|
||||
assert(handle);
|
||||
assert(handle->mmap_control);
|
||||
if ((err = handle->fast_ops->munmap_control(handle->fast_op_arg, handle->mmap_control)) < 0)
|
||||
return err;
|
||||
handle->mmap_control = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_munmap_data(snd_pcm_t *handle)
|
||||
{
|
||||
int err;
|
||||
assert(handle);
|
||||
assert(handle->mmap_data);
|
||||
if ((err = handle->fast_ops->munmap_data(handle->fast_op_arg, handle->mmap_data, handle->setup.mmap_bytes)) < 0)
|
||||
return err;
|
||||
free(handle->channels);
|
||||
handle->channels = 0;
|
||||
handle->mmap_data = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_munmap(snd_pcm_t *handle)
|
||||
{
|
||||
int err;
|
||||
err = snd_pcm_munmap_status(handle);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_pcm_munmap_control(handle);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return snd_pcm_munmap_data(handle);
|
||||
}
|
||||
|
||||
|
|
|
|||
536
src/pcm/pcm_mulaw.c
Normal file
536
src/pcm/pcm_mulaw.c
Normal file
|
|
@ -0,0 +1,536 @@
|
|||
/*
|
||||
* PCM - Mu-Law conversion
|
||||
* 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 <byteswap.h>
|
||||
#include "pcm_local.h"
|
||||
#include "pcm_plugin.h"
|
||||
|
||||
typedef void (*mulaw_f)(snd_pcm_channel_area_t *src_areas,
|
||||
size_t src_offset,
|
||||
snd_pcm_channel_area_t *dst_areas,
|
||||
size_t dst_offset,
|
||||
size_t frames, size_t channels, int getputidx);
|
||||
|
||||
typedef struct {
|
||||
/* This field need to be the first */
|
||||
snd_pcm_plugin_t plug;
|
||||
int getput_idx;
|
||||
mulaw_f func;
|
||||
int sformat;
|
||||
int cformat;
|
||||
int cxfer_mode, cmmap_shape;
|
||||
} snd_pcm_mulaw_t;
|
||||
|
||||
static inline int val_seg(int val)
|
||||
{
|
||||
int r = 0;
|
||||
val >>= 7;
|
||||
if (val & 0xf0) {
|
||||
val >>= 4;
|
||||
r += 4;
|
||||
}
|
||||
if (val & 0x0c) {
|
||||
val >>= 2;
|
||||
r += 2;
|
||||
}
|
||||
if (val & 0x02)
|
||||
r += 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* s16_to_ulaw() - Convert a linear PCM value to u-law
|
||||
*
|
||||
* In order to simplify the encoding process, the original linear magnitude
|
||||
* is biased by adding 33 which shifts the encoding range from (0 - 8158) to
|
||||
* (33 - 8191). The result can be seen in the following encoding table:
|
||||
*
|
||||
* Biased Linear Input Code Compressed Code
|
||||
* ------------------------ ---------------
|
||||
* 00000001wxyza 000wxyz
|
||||
* 0000001wxyzab 001wxyz
|
||||
* 000001wxyzabc 010wxyz
|
||||
* 00001wxyzabcd 011wxyz
|
||||
* 0001wxyzabcde 100wxyz
|
||||
* 001wxyzabcdef 101wxyz
|
||||
* 01wxyzabcdefg 110wxyz
|
||||
* 1wxyzabcdefgh 111wxyz
|
||||
*
|
||||
* Each biased linear code has a leading 1 which identifies the segment
|
||||
* number. The value of the segment number is equal to 7 minus the number
|
||||
* of leading 0's. The quantization interval is directly available as the
|
||||
* four bits wxyz. * The trailing bits (a - h) are ignored.
|
||||
*
|
||||
* Ordinarily the complement of the resulting code word is used for
|
||||
* transmission, and so the code word is complemented before it is returned.
|
||||
*
|
||||
* For further information see John C. Bellamy's Digital Telephony, 1982,
|
||||
* John Wiley & Sons, pps 98-111 and 472-476.
|
||||
*/
|
||||
|
||||
static unsigned char s16_to_ulaw(int pcm_val) /* 2's complement (16-bit range) */
|
||||
{
|
||||
int mask;
|
||||
int seg;
|
||||
unsigned char uval;
|
||||
|
||||
if (pcm_val < 0) {
|
||||
pcm_val = -pcm_val + 0x84;
|
||||
mask = 0x7f;
|
||||
} else {
|
||||
pcm_val += 0x84;
|
||||
mask = 0xff;
|
||||
}
|
||||
if (pcm_val > 0x7fff)
|
||||
pcm_val = 0x7fff;
|
||||
|
||||
/* Convert the scaled magnitude to segment number. */
|
||||
seg = val_seg(pcm_val);
|
||||
|
||||
/*
|
||||
* Combine the sign, segment, quantization bits;
|
||||
* and complement the code word.
|
||||
*/
|
||||
uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
|
||||
return uval ^ mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* ulaw_to_s16() - Convert a u-law value to 16-bit linear PCM
|
||||
*
|
||||
* First, a biased linear code is derived from the code word. An unbiased
|
||||
* output can then be obtained by subtracting 33 from the biased code.
|
||||
*
|
||||
* Note that this function expects to be passed the complement of the
|
||||
* original code word. This is in keeping with ISDN conventions.
|
||||
*/
|
||||
static int ulaw_to_s16(unsigned char u_val)
|
||||
{
|
||||
int t;
|
||||
|
||||
/* Complement to obtain normal u-law value. */
|
||||
u_val = ~u_val;
|
||||
|
||||
/*
|
||||
* Extract and bias the quantization bits. Then
|
||||
* shift up by the segment number and subtract out the bias.
|
||||
*/
|
||||
t = ((u_val & 0x0f) << 3) + 0x84;
|
||||
t <<= (u_val & 0x70) >> 4;
|
||||
|
||||
return ((u_val & 0x80) ? (0x84 - t) : (t - 0x84));
|
||||
}
|
||||
|
||||
static void mulaw_decode(snd_pcm_channel_area_t *src_areas,
|
||||
size_t src_offset,
|
||||
snd_pcm_channel_area_t *dst_areas,
|
||||
size_t dst_offset,
|
||||
size_t frames, size_t channels, int putidx)
|
||||
{
|
||||
#define PUT_S16_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_S16_LABELS
|
||||
void *put = put_s16_labels[putidx];
|
||||
size_t channel;
|
||||
for (channel = 0; channel < channels; ++channel) {
|
||||
char *src;
|
||||
char *dst;
|
||||
int src_step, dst_step;
|
||||
size_t frames1;
|
||||
snd_pcm_channel_area_t *src_area = &src_areas[channel];
|
||||
snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
|
||||
#if 0
|
||||
if (!src_area->enabled) {
|
||||
if (dst_area->wanted)
|
||||
snd_pcm_area_silence(&dst_areas[channel], dst_offset, frames, dst_sfmt);
|
||||
dst_area->enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_area->enabled = 1;
|
||||
#endif
|
||||
src = snd_pcm_channel_area_addr(src_area, src_offset);
|
||||
dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
|
||||
src_step = snd_pcm_channel_area_step(src_area);
|
||||
dst_step = snd_pcm_channel_area_step(dst_area);
|
||||
frames1 = frames;
|
||||
while (frames1-- > 0) {
|
||||
int16_t sample = ulaw_to_s16(*src);
|
||||
goto *put;
|
||||
#define PUT_S16_END after
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_S16_END
|
||||
after:
|
||||
src += src_step;
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mulaw_encode(snd_pcm_channel_area_t *src_areas,
|
||||
size_t src_offset,
|
||||
snd_pcm_channel_area_t *dst_areas,
|
||||
size_t dst_offset,
|
||||
size_t frames, size_t channels, int getidx)
|
||||
{
|
||||
#define GET_S16_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_LABELS
|
||||
void *get = get_s16_labels[getidx];
|
||||
size_t channel;
|
||||
int16_t sample = 0;
|
||||
for (channel = 0; channel < channels; ++channel) {
|
||||
char *src;
|
||||
char *dst;
|
||||
int src_step, dst_step;
|
||||
size_t frames1;
|
||||
snd_pcm_channel_area_t *src_area = &src_areas[channel];
|
||||
snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
|
||||
#if 0
|
||||
if (!src_area->enabled) {
|
||||
if (dst_area->wanted)
|
||||
snd_pcm_area_silence(&dst_area->area, 0, frames, dst_sfmt);
|
||||
dst_area->enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_area->enabled = 1;
|
||||
#endif
|
||||
src = snd_pcm_channel_area_addr(src_area, src_offset);
|
||||
dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
|
||||
src_step = snd_pcm_channel_area_step(src_area);
|
||||
dst_step = snd_pcm_channel_area_step(dst_area);
|
||||
frames1 = frames;
|
||||
while (frames1-- > 0) {
|
||||
goto *get;
|
||||
#define GET_S16_END after
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_END
|
||||
after:
|
||||
*dst = s16_to_ulaw(sample);
|
||||
src += src_step;
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int snd_pcm_mulaw_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info)
|
||||
{
|
||||
snd_pcm_mulaw_t *mulaw = pcm->private;
|
||||
unsigned int req_mask = info->req_mask;
|
||||
unsigned int sfmt = info->req.format.sfmt;
|
||||
int err;
|
||||
if (req_mask & SND_PCM_PARAMS_SFMT) {
|
||||
if (mulaw->sformat == SND_PCM_SFMT_MU_LAW ?
|
||||
!snd_pcm_format_linear(sfmt) :
|
||||
sfmt != SND_PCM_SFMT_MU_LAW) {
|
||||
info->req.fail_mask = SND_PCM_PARAMS_SFMT;
|
||||
info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
info->req_mask |= SND_PCM_PARAMS_SFMT;
|
||||
info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE |
|
||||
SND_PCM_PARAMS_XFER_MODE);
|
||||
info->req.format.sfmt = mulaw->sformat;
|
||||
err = snd_pcm_params_info(mulaw->plug.slave, info);
|
||||
info->req_mask = req_mask;
|
||||
info->req.format.sfmt = sfmt;
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (req_mask & SND_PCM_PARAMS_SFMT)
|
||||
info->formats = 1 << sfmt;
|
||||
else
|
||||
info->formats = mulaw->sformat == SND_PCM_SFMT_MU_LAW ?
|
||||
SND_PCM_LINEAR_FORMATS : 1 << SND_PCM_SFMT_MU_LAW;
|
||||
info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
|
||||
info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int snd_pcm_mulaw_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
|
||||
{
|
||||
snd_pcm_mulaw_t *mulaw = pcm->private;
|
||||
snd_pcm_t *slave = mulaw->plug.slave;
|
||||
int err;
|
||||
if (mulaw->sformat == SND_PCM_SFMT_MU_LAW ?
|
||||
!snd_pcm_format_linear(params->format.sfmt) :
|
||||
params->format.sfmt != SND_PCM_SFMT_MU_LAW) {
|
||||
params->fail_mask = SND_PCM_PARAMS_SFMT;
|
||||
params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
|
||||
return -EINVAL;
|
||||
}
|
||||
if (slave->mmap_data) {
|
||||
err = snd_pcm_munmap_data(slave);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
mulaw->cformat = params->format.sfmt;
|
||||
mulaw->cxfer_mode = params->xfer_mode;
|
||||
mulaw->cmmap_shape = params->mmap_shape;
|
||||
params->format.sfmt = mulaw->sformat;
|
||||
params->xfer_mode = SND_PCM_XFER_UNSPECIFIED;
|
||||
params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;;
|
||||
err = snd_pcm_params(slave, params);
|
||||
params->format.sfmt = mulaw->cformat;
|
||||
params->xfer_mode = mulaw->cxfer_mode;
|
||||
params->mmap_shape = mulaw->cmmap_shape;
|
||||
if (slave->valid_setup) {
|
||||
int r = snd_pcm_mmap_data(slave, NULL);
|
||||
assert(r >= 0);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int snd_pcm_mulaw_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
|
||||
{
|
||||
snd_pcm_mulaw_t *mulaw = pcm->private;
|
||||
int err = snd_pcm_setup(mulaw->plug.slave, setup);
|
||||
if (err < 0)
|
||||
return err;
|
||||
assert(mulaw->sformat == setup->format.sfmt);
|
||||
if (mulaw->cxfer_mode == SND_PCM_XFER_UNSPECIFIED)
|
||||
setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED;
|
||||
else
|
||||
setup->xfer_mode = mulaw->cxfer_mode;
|
||||
if (mulaw->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED)
|
||||
setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED;
|
||||
else
|
||||
setup->mmap_shape = mulaw->cmmap_shape;
|
||||
setup->format.sfmt = mulaw->cformat;
|
||||
setup->mmap_bytes = 0;
|
||||
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
|
||||
if (mulaw->sformat == SND_PCM_SFMT_MU_LAW) {
|
||||
mulaw->getput_idx = getput_index(mulaw->cformat);
|
||||
mulaw->func = mulaw_encode;
|
||||
} else {
|
||||
mulaw->getput_idx = getput_index(mulaw->sformat);
|
||||
mulaw->func = mulaw_decode;
|
||||
}
|
||||
} else {
|
||||
if (mulaw->sformat == SND_PCM_SFMT_MU_LAW) {
|
||||
mulaw->getput_idx = getput_index(mulaw->cformat);
|
||||
mulaw->func = mulaw_decode;
|
||||
} else {
|
||||
mulaw->getput_idx = getput_index(mulaw->sformat);
|
||||
mulaw->func = mulaw_encode;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_mulaw_write_areas(snd_pcm_t *pcm,
|
||||
snd_pcm_channel_area_t *areas,
|
||||
size_t offset,
|
||||
size_t size,
|
||||
size_t *slave_sizep)
|
||||
{
|
||||
snd_pcm_mulaw_t *mulaw = pcm->private;
|
||||
snd_pcm_t *slave = mulaw->plug.slave;
|
||||
size_t xfer = 0;
|
||||
ssize_t err = 0;
|
||||
if (slave_sizep && *slave_sizep < size)
|
||||
size = *slave_sizep;
|
||||
assert(size > 0);
|
||||
while (xfer < size) {
|
||||
size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer);
|
||||
mulaw->func(areas, offset,
|
||||
slave->mmap_areas, snd_pcm_mmap_offset(slave),
|
||||
frames, pcm->setup.format.channels,
|
||||
mulaw->getput_idx);
|
||||
err = snd_pcm_mmap_forward(slave, frames);
|
||||
if (err < 0)
|
||||
break;
|
||||
assert((size_t)err == frames);
|
||||
offset += err;
|
||||
xfer += err;
|
||||
snd_pcm_mmap_hw_forward(pcm, err);
|
||||
}
|
||||
if (xfer > 0) {
|
||||
if (slave_sizep)
|
||||
*slave_sizep = xfer;
|
||||
return xfer;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_mulaw_read_areas(snd_pcm_t *pcm,
|
||||
snd_pcm_channel_area_t *areas,
|
||||
size_t offset,
|
||||
size_t size,
|
||||
size_t *slave_sizep)
|
||||
{
|
||||
snd_pcm_mulaw_t *mulaw = pcm->private;
|
||||
snd_pcm_t *slave = mulaw->plug.slave;
|
||||
size_t xfer = 0;
|
||||
ssize_t err = 0;
|
||||
if (slave_sizep && *slave_sizep < size)
|
||||
size = *slave_sizep;
|
||||
assert(size > 0);
|
||||
while (xfer < size) {
|
||||
size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer);
|
||||
mulaw->func(slave->mmap_areas, snd_pcm_mmap_offset(slave),
|
||||
areas, offset,
|
||||
frames, pcm->setup.format.channels,
|
||||
mulaw->getput_idx);
|
||||
err = snd_pcm_mmap_forward(slave, frames);
|
||||
if (err < 0)
|
||||
break;
|
||||
assert((size_t)err == frames);
|
||||
offset += err;
|
||||
xfer += err;
|
||||
snd_pcm_mmap_hw_forward(pcm, err);
|
||||
}
|
||||
if (xfer > 0) {
|
||||
if (slave_sizep)
|
||||
*slave_sizep = xfer;
|
||||
return xfer;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void snd_pcm_mulaw_dump(snd_pcm_t *pcm, FILE *fp)
|
||||
{
|
||||
snd_pcm_mulaw_t *mulaw = pcm->private;
|
||||
fprintf(fp, "Mu-Law conversion PCM (%s)\n",
|
||||
snd_pcm_format_name(mulaw->sformat));
|
||||
if (pcm->valid_setup) {
|
||||
fprintf(fp, "Its setup is:\n");
|
||||
snd_pcm_dump_setup(pcm, fp);
|
||||
}
|
||||
fprintf(fp, "Slave: ");
|
||||
snd_pcm_dump(mulaw->plug.slave, fp);
|
||||
}
|
||||
|
||||
struct snd_pcm_ops snd_pcm_mulaw_ops = {
|
||||
close: snd_pcm_plugin_close,
|
||||
info: snd_pcm_plugin_info,
|
||||
params_info: snd_pcm_mulaw_params_info,
|
||||
params: snd_pcm_mulaw_params,
|
||||
setup: snd_pcm_mulaw_setup,
|
||||
channel_info: snd_pcm_plugin_channel_info,
|
||||
channel_params: snd_pcm_plugin_channel_params,
|
||||
channel_setup: snd_pcm_plugin_channel_setup,
|
||||
dump: snd_pcm_mulaw_dump,
|
||||
nonblock: snd_pcm_plugin_nonblock,
|
||||
mmap_status: snd_pcm_plugin_mmap_status,
|
||||
mmap_control: snd_pcm_plugin_mmap_control,
|
||||
mmap_data: snd_pcm_plugin_mmap_data,
|
||||
munmap_status: snd_pcm_plugin_munmap_status,
|
||||
munmap_control: snd_pcm_plugin_munmap_control,
|
||||
munmap_data: snd_pcm_plugin_munmap_data,
|
||||
};
|
||||
|
||||
int snd_pcm_mulaw_open(snd_pcm_t **handlep, int sformat, snd_pcm_t *slave, int close_slave)
|
||||
{
|
||||
snd_pcm_t *handle;
|
||||
snd_pcm_mulaw_t *mulaw;
|
||||
int err;
|
||||
assert(handlep && slave);
|
||||
if (snd_pcm_format_linear(sformat) != 1 &&
|
||||
sformat != SND_PCM_SFMT_MU_LAW)
|
||||
return -EINVAL;
|
||||
mulaw = calloc(1, sizeof(snd_pcm_mulaw_t));
|
||||
if (!mulaw) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
mulaw->sformat = sformat;
|
||||
mulaw->plug.read = snd_pcm_mulaw_read_areas;
|
||||
mulaw->plug.write = snd_pcm_mulaw_write_areas;
|
||||
mulaw->plug.slave = slave;
|
||||
mulaw->plug.close_slave = close_slave;
|
||||
|
||||
handle = calloc(1, sizeof(snd_pcm_t));
|
||||
if (!handle) {
|
||||
free(mulaw);
|
||||
return -ENOMEM;
|
||||
}
|
||||
handle->type = SND_PCM_TYPE_MULAW;
|
||||
handle->stream = slave->stream;
|
||||
handle->ops = &snd_pcm_mulaw_ops;
|
||||
handle->op_arg = handle;
|
||||
handle->fast_ops = &snd_pcm_plugin_fast_ops;
|
||||
handle->fast_op_arg = handle;
|
||||
handle->mode = slave->mode;
|
||||
handle->private = mulaw;
|
||||
err = snd_pcm_init(handle);
|
||||
if (err < 0) {
|
||||
snd_pcm_close(handle);
|
||||
return err;
|
||||
}
|
||||
*handlep = handle;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _snd_pcm_mulaw_open(snd_pcm_t **pcmp, char *name,
|
||||
snd_config_t *conf,
|
||||
int stream, int mode)
|
||||
{
|
||||
snd_config_iterator_t i;
|
||||
char *sname = NULL;
|
||||
int err;
|
||||
snd_pcm_t *spcm;
|
||||
int sformat = -1;
|
||||
snd_config_foreach(i, conf) {
|
||||
snd_config_t *n = snd_config_entry(i);
|
||||
if (strcmp(n->id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "type") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "stream") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "sname") == 0) {
|
||||
err = snd_config_string_get(n, &sname);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(n->id, "sformat") == 0) {
|
||||
char *f;
|
||||
err = snd_config_string_get(n, &f);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
sformat = snd_pcm_format_value(f);
|
||||
if (sformat < 0)
|
||||
return -EINVAL;
|
||||
if (snd_pcm_format_linear(sformat) != 1 &&
|
||||
sformat != SND_PCM_SFMT_MU_LAW)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!sname || !sformat)
|
||||
return -EINVAL;
|
||||
/* This is needed cause snd_config_update may destroy config */
|
||||
sname = strdup(sname);
|
||||
if (!sname)
|
||||
return -ENOMEM;
|
||||
err = snd_pcm_open(&spcm, sname, stream, mode);
|
||||
free(sname);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_pcm_mulaw_open(pcmp, sformat, spcm, 1);
|
||||
if (err < 0)
|
||||
snd_pcm_close(spcm);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -32,9 +32,6 @@ typedef struct {
|
|||
snd_pcm_t *handle;
|
||||
unsigned int channels_total;
|
||||
int close_slave;
|
||||
char *buf;
|
||||
snd_pcm_channel_area_t *areas;
|
||||
struct iovec *iovec;
|
||||
} snd_pcm_multi_slave_t;
|
||||
|
||||
typedef struct {
|
||||
|
|
@ -49,9 +46,7 @@ typedef struct {
|
|||
size_t bindings_count;
|
||||
snd_pcm_multi_bind_t *bindings;
|
||||
size_t channels_count;
|
||||
size_t frames_alloc;
|
||||
int interleave;
|
||||
int one_to_many;
|
||||
int xfer_mode, mmap_shape;
|
||||
} snd_pcm_multi_t;
|
||||
|
||||
static int snd_pcm_multi_close(snd_pcm_t *pcm)
|
||||
|
|
@ -68,12 +63,6 @@ static int snd_pcm_multi_close(snd_pcm_t *pcm)
|
|||
ret = err;
|
||||
} else
|
||||
snd_pcm_unlink(slave->handle);
|
||||
if (slave->buf) {
|
||||
free(slave->buf);
|
||||
free(slave->areas);
|
||||
}
|
||||
if (slave->iovec)
|
||||
free(slave->iovec);
|
||||
}
|
||||
free(multi->slaves);
|
||||
free(multi->bindings);
|
||||
|
|
@ -91,23 +80,12 @@ static int snd_pcm_multi_nonblock(snd_pcm_t *pcm, int nonblock)
|
|||
static int snd_pcm_multi_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
|
||||
{
|
||||
snd_pcm_multi_t *multi = pcm->private;
|
||||
unsigned int i;
|
||||
int err;
|
||||
snd_pcm_t *handle_0 = multi->slaves[0].handle;
|
||||
/* FIXME */
|
||||
err = snd_pcm_info(handle_0, info);
|
||||
if (err < 0)
|
||||
return err;
|
||||
for (i = 1; i < multi->slaves_count; ++i) {
|
||||
snd_pcm_t *handle_i = multi->slaves[i].handle;
|
||||
snd_pcm_info_t info_i;
|
||||
memset(&info_i, 0, sizeof(info_i));
|
||||
err = snd_pcm_info(handle_i, &info_i);
|
||||
if (err < 0)
|
||||
return err;
|
||||
info->flags &= info_i.flags;
|
||||
}
|
||||
if (multi->one_to_many)
|
||||
info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -118,7 +96,9 @@ static int snd_pcm_multi_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info
|
|||
int err;
|
||||
snd_pcm_t *handle_0 = multi->slaves[0].handle;
|
||||
unsigned int old_mask = info->req_mask;
|
||||
info->req_mask &= ~SND_PCM_PARAMS_CHANNELS;
|
||||
info->req_mask &= ~(SND_PCM_PARAMS_CHANNELS |
|
||||
SND_PCM_PARAMS_MMAP_SHAPE |
|
||||
SND_PCM_PARAMS_XFER_MODE);
|
||||
err = snd_pcm_params_info(handle_0, info);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
|
@ -128,6 +108,8 @@ static int snd_pcm_multi_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info
|
|||
snd_pcm_t *handle_i = multi->slaves[i].handle;
|
||||
snd_pcm_params_info_t info_i;
|
||||
info_i = *info;
|
||||
info_i.req_mask |= SND_PCM_PARAMS_CHANNELS;
|
||||
info_i.req.format.channels = multi->slaves[i].channels_total;
|
||||
err = snd_pcm_params_info(handle_i, &info_i);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
|
@ -147,6 +129,7 @@ static int snd_pcm_multi_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info
|
|||
info->min_fragments = info_i.min_fragments;
|
||||
if (info_i.max_fragments < info->max_fragments)
|
||||
info->max_fragments = info_i.max_fragments;
|
||||
info->flags &= info_i.flags;
|
||||
}
|
||||
info->req_mask = old_mask;
|
||||
return 0;
|
||||
|
|
@ -159,35 +142,38 @@ static int snd_pcm_multi_params(snd_pcm_t *pcm, snd_pcm_params_t *params)
|
|||
snd_pcm_params_t p;
|
||||
if (params->format.channels != multi->channels_count)
|
||||
return -EINVAL;
|
||||
multi->xfer_mode = params->xfer_mode;
|
||||
multi->mmap_shape = params->mmap_shape;
|
||||
p = *params;
|
||||
multi->interleave = params->format.interleave;
|
||||
for (i = 0; i < multi->slaves_count; ++i) {
|
||||
int err;
|
||||
snd_pcm_t *handle = multi->slaves[i].handle;
|
||||
snd_pcm_info_t info;
|
||||
err = snd_pcm_info(handle, &info);
|
||||
if (err < 0)
|
||||
return err;
|
||||
p.format.interleave = params->format.interleave;
|
||||
if (!(info.flags & SND_PCM_INFO_INTERLEAVE))
|
||||
p.format.interleave = 0;
|
||||
else if (!(info.flags & SND_PCM_INFO_NONINTERLEAVE))
|
||||
p.format.interleave = 1;
|
||||
if (handle->mmap_data) {
|
||||
err = snd_pcm_munmap_data(handle);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
p.xfer_mode = SND_PCM_XFER_UNSPECIFIED;
|
||||
p.mmap_shape = SND_PCM_MMAP_UNSPECIFIED;
|
||||
p.format.channels = multi->slaves[i].channels_total;
|
||||
#if 1
|
||||
p.xrun_max = ~0;
|
||||
#endif
|
||||
err = snd_pcm_params(handle, &p);
|
||||
if (err < 0) {
|
||||
params->fail_mask = p.fail_mask;
|
||||
params->fail_reason = p.fail_reason;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < multi->slaves_count; ++i) {
|
||||
snd_pcm_t *handle = multi->slaves[i].handle;
|
||||
int err = snd_pcm_mmap_data(handle, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (i == 0 && params->mode == SND_PCM_MODE_FRAGMENT) {
|
||||
snd_pcm_setup_t s;
|
||||
err = snd_pcm_setup(handle, &s);
|
||||
if (err < 0)
|
||||
return err;
|
||||
p.frag_size = s.frag_size;
|
||||
p.buffer_size = s.buffer_size;
|
||||
}
|
||||
if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
|
||||
snd_pcm_areas_silence(handle->mmap_areas, 0, handle->setup.format.channels,
|
||||
handle->setup.buffer_size, handle->setup.format.sfmt);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -202,7 +188,6 @@ static int snd_pcm_multi_setup(snd_pcm_t *pcm, snd_pcm_setup_t *setup)
|
|||
if (err < 0)
|
||||
return err;
|
||||
frames_alloc = multi->slaves[0].handle->setup.frag_size;
|
||||
multi->frames_alloc = 0;
|
||||
for (i = 1; i < multi->slaves_count; ++i) {
|
||||
snd_pcm_setup_t s;
|
||||
snd_pcm_t *sh = multi->slaves[i].handle;
|
||||
|
|
@ -211,53 +196,21 @@ static int snd_pcm_multi_setup(snd_pcm_t *pcm, snd_pcm_setup_t *setup)
|
|||
return err;
|
||||
if (setup->format.rate != s.format.rate)
|
||||
return -EINVAL;
|
||||
if (setup->align % s.align != 0)
|
||||
/* mmap is not feasible */
|
||||
if (setup->buffer_size != s.buffer_size)
|
||||
return -EINVAL;
|
||||
if (setup->mmap_shape != SND_PCM_MMAP_NONINTERLEAVED ||
|
||||
s.mmap_shape != SND_PCM_MMAP_NONINTERLEAVED)
|
||||
setup->mmap_shape = SND_PCM_MMAP_COMPLEX;
|
||||
}
|
||||
setup->format.interleave = multi->interleave;
|
||||
setup->format.channels = multi->channels_count;
|
||||
for (i = 0; i < multi->slaves_count; ++i) {
|
||||
snd_pcm_multi_slave_t *s = &multi->slaves[i];
|
||||
snd_pcm_t *sh = s->handle;
|
||||
unsigned int c;
|
||||
if (s->buf) {
|
||||
free(s->buf);
|
||||
s->buf = 0;
|
||||
free(s->areas);
|
||||
s->areas = 0;
|
||||
}
|
||||
if (s->iovec)
|
||||
free(s->iovec);
|
||||
if (!sh->setup.format.interleave) {
|
||||
s->iovec = calloc(s->channels_total, sizeof(*s->iovec));
|
||||
if (!pcm->setup.format.interleave)
|
||||
continue;
|
||||
}
|
||||
s->buf = malloc(frames_alloc * sh->bits_per_frame / 8);
|
||||
if (!s->buf)
|
||||
return -ENOMEM;
|
||||
snd_pcm_format_set_silence(sh->setup.format.format, s->buf,
|
||||
sh->setup.frag_size * sh->setup.format.channels);
|
||||
s->areas = calloc(s->channels_total, sizeof(*s->areas));
|
||||
if (!s->areas)
|
||||
return -ENOMEM;
|
||||
for (c = 0; c < s->channels_total; ++c) {
|
||||
snd_pcm_channel_area_t *a = &s->areas[c];
|
||||
if (sh->setup.format.interleave) {
|
||||
a->addr = s->buf;
|
||||
a->first = c * sh->bits_per_sample;
|
||||
a->step = sh->bits_per_frame;
|
||||
} else {
|
||||
a->addr = s->buf + sh->setup.frag_size * sh->bits_per_sample / 8;
|
||||
a->first = 0;
|
||||
a->step = sh->bits_per_sample;
|
||||
s->iovec[c].iov_base = a->addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
multi->frames_alloc = frames_alloc;
|
||||
/* Loaded with a value != 0 if mmap is feasible */
|
||||
setup->mmap_bytes = !multi->one_to_many;
|
||||
if (multi->xfer_mode == SND_PCM_XFER_UNSPECIFIED)
|
||||
setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED;
|
||||
else
|
||||
setup->xfer_mode = multi->xfer_mode;
|
||||
if (multi->mmap_shape != SND_PCM_MMAP_UNSPECIFIED &&
|
||||
multi->mmap_shape != setup->mmap_shape)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -275,11 +228,18 @@ static int snd_pcm_multi_state(snd_pcm_t *pcm)
|
|||
return snd_pcm_state(handle);
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_multi_hw_ptr(snd_pcm_t *pcm, int update)
|
||||
static int snd_pcm_multi_delay(snd_pcm_t *pcm, ssize_t *delayp)
|
||||
{
|
||||
snd_pcm_multi_t *multi = pcm->private;
|
||||
snd_pcm_t *handle = multi->slaves[0].handle;
|
||||
return snd_pcm_hw_ptr(handle, update);
|
||||
return snd_pcm_delay(handle, delayp);
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_multi_avail_update(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_multi_t *multi = pcm->private;
|
||||
snd_pcm_t *handle = multi->slaves[0].handle;
|
||||
return snd_pcm_avail_update(handle);
|
||||
}
|
||||
|
||||
static int snd_pcm_multi_prepare(snd_pcm_t *pcm)
|
||||
|
|
@ -288,16 +248,16 @@ static int snd_pcm_multi_prepare(snd_pcm_t *pcm)
|
|||
return snd_pcm_prepare(multi->slaves[0].handle);
|
||||
}
|
||||
|
||||
static int snd_pcm_multi_go(snd_pcm_t *pcm)
|
||||
static int snd_pcm_multi_start(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_multi_t *multi = pcm->private;
|
||||
return snd_pcm_go(multi->slaves[0].handle);
|
||||
return snd_pcm_start(multi->slaves[0].handle);
|
||||
}
|
||||
|
||||
static int snd_pcm_multi_drain(snd_pcm_t *pcm)
|
||||
static int snd_pcm_multi_stop(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_multi_t *multi = pcm->private;
|
||||
return snd_pcm_drain(multi->slaves[0].handle);
|
||||
return snd_pcm_stop(multi->slaves[0].handle);
|
||||
}
|
||||
|
||||
static int snd_pcm_multi_flush(snd_pcm_t *pcm)
|
||||
|
|
@ -394,206 +354,21 @@ static ssize_t snd_pcm_multi_appl_ptr(snd_pcm_t *pcm, off_t offset)
|
|||
return newpos;
|
||||
}
|
||||
|
||||
static int snd_pcm_multi_write_copy(snd_pcm_t *pcm, const void *buf,
|
||||
size_t offset, size_t count)
|
||||
static int snd_pcm_multi_mmap_status(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_multi_t *multi = pcm->private;
|
||||
unsigned int i;
|
||||
snd_pcm_channel_area_t area;
|
||||
area.addr = (void *) buf + offset * pcm->bits_per_frame;
|
||||
area.step = pcm->bits_per_frame;
|
||||
for (i = 0; i < multi->bindings_count; ++i) {
|
||||
snd_pcm_multi_bind_t *bind = &multi->bindings[i];
|
||||
snd_pcm_multi_slave_t *slave = &multi->slaves[bind->slave];
|
||||
int err;
|
||||
assert(slave->buf);
|
||||
area.first = pcm->bits_per_sample * bind->client_channel;
|
||||
err = snd_pcm_area_copy(&area, 0, &slave->areas[bind->slave_channel], 0, count, pcm->setup.format.format);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (!slave->handle->setup.format.interleave) {
|
||||
struct iovec *vec = &slave->iovec[bind->slave_channel];
|
||||
vec->iov_len = count;
|
||||
}
|
||||
}
|
||||
pcm->mmap_status = multi->slaves[0].handle->mmap_status;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_multi_writev_copy(snd_pcm_t *pcm, const struct iovec *vec,
|
||||
size_t offset, size_t count)
|
||||
static int snd_pcm_multi_mmap_control(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_multi_t *multi = pcm->private;
|
||||
unsigned int i;
|
||||
snd_pcm_channel_area_t area;
|
||||
area.first = 0;
|
||||
area.step = pcm->bits_per_sample;
|
||||
for (i = 0; i < multi->bindings_count; ++i) {
|
||||
snd_pcm_multi_bind_t *bind = &multi->bindings[i];
|
||||
snd_pcm_multi_slave_t *slave = &multi->slaves[bind->slave];
|
||||
int err;
|
||||
area.addr = vec[bind->client_channel].iov_base +
|
||||
offset * pcm->bits_per_sample;
|
||||
if (slave->handle->setup.format.interleave) {
|
||||
assert(slave->buf);
|
||||
err = snd_pcm_area_copy(&area, 0, &slave->areas[bind->slave_channel], 0, count, pcm->setup.format.format);
|
||||
if (err < 0)
|
||||
return err;
|
||||
} else {
|
||||
struct iovec *vec = &slave->iovec[bind->slave_channel];
|
||||
vec->iov_base = area.addr;
|
||||
vec->iov_len = count;
|
||||
}
|
||||
}
|
||||
pcm->mmap_control = multi->slaves[0].handle->mmap_control;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_multi_write_io(snd_pcm_t *pcm, size_t count)
|
||||
{
|
||||
snd_pcm_multi_t *multi = pcm->private;
|
||||
unsigned int i;
|
||||
ssize_t frames = count;
|
||||
for (i = 0; i < multi->slaves_count; ++i) {
|
||||
snd_pcm_multi_slave_t *slave = &multi->slaves[i];
|
||||
snd_pcm_t *sh = slave->handle;
|
||||
if (sh->setup.format.interleave) {
|
||||
frames = snd_pcm_write(sh, slave->buf, frames);
|
||||
} else {
|
||||
int channels = sh->setup.format.channels;
|
||||
frames = snd_pcm_writev(sh, slave->iovec, channels);
|
||||
}
|
||||
if (frames <= 0)
|
||||
break;
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_multi_write(snd_pcm_t *pcm, snd_timestamp_t *timestamp ATTRIBUTE_UNUSED, const void *buf, size_t count)
|
||||
{
|
||||
snd_pcm_multi_t *multi = pcm->private;
|
||||
size_t result = 0;
|
||||
while (count > 0) {
|
||||
int err;
|
||||
ssize_t ret;
|
||||
size_t frames = count;
|
||||
if (frames > multi->frames_alloc)
|
||||
frames = multi->frames_alloc;
|
||||
err = snd_pcm_multi_write_copy(pcm, buf, result, frames);
|
||||
if (err < 0)
|
||||
return err;
|
||||
ret = snd_pcm_multi_write_io(pcm, frames);
|
||||
if (ret > 0)
|
||||
result += ret;
|
||||
if (ret != (ssize_t)frames) {
|
||||
if (result > 0)
|
||||
return result;
|
||||
return ret;
|
||||
}
|
||||
count -= ret;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_multi_writev1(snd_pcm_t *pcm, const struct iovec *vector, size_t count)
|
||||
{
|
||||
snd_pcm_multi_t *multi = pcm->private;
|
||||
size_t result = 0;
|
||||
while (count > 0) {
|
||||
int err;
|
||||
ssize_t ret;
|
||||
size_t frames = count;
|
||||
if (frames > multi->frames_alloc)
|
||||
frames = multi->frames_alloc;
|
||||
err = snd_pcm_multi_writev_copy(pcm, vector, result, frames);
|
||||
if (err < 0)
|
||||
return err;
|
||||
ret = snd_pcm_multi_write_io(pcm, frames);
|
||||
if (ret > 0)
|
||||
result += ret;
|
||||
if (ret != (ssize_t) frames) {
|
||||
if (result > 0)
|
||||
return result;
|
||||
return ret;
|
||||
}
|
||||
count -= ret;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_multi_writev(snd_pcm_t *pcm, snd_timestamp_t *timestamp ATTRIBUTE_UNUSED, const struct iovec *vector, unsigned long count)
|
||||
{
|
||||
unsigned int k, step;
|
||||
size_t result = 0;
|
||||
if (pcm->setup.format.interleave)
|
||||
step = 1;
|
||||
else
|
||||
step = pcm->setup.format.channels;
|
||||
for (k = 0; k < count; k += step) {
|
||||
ssize_t ret;
|
||||
if (pcm->setup.format.interleave)
|
||||
ret = snd_pcm_multi_write(pcm, timestamp, vector->iov_base, vector->iov_len);
|
||||
else
|
||||
ret = snd_pcm_multi_writev1(pcm, vector, vector->iov_len);
|
||||
if (ret > 0)
|
||||
result += ret;
|
||||
if (ret != (ssize_t) vector->iov_len) {
|
||||
if (result > 0)
|
||||
return result;
|
||||
return ret;
|
||||
}
|
||||
vector += step;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_multi_read(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_timestamp_t *timestamp ATTRIBUTE_UNUSED, void *buf ATTRIBUTE_UNUSED, size_t count ATTRIBUTE_UNUSED)
|
||||
{
|
||||
// snd_pcm_multi_t *multi = pcm->private;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_multi_readv(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_timestamp_t *timestamp ATTRIBUTE_UNUSED, const struct iovec *vector ATTRIBUTE_UNUSED, unsigned long count ATTRIBUTE_UNUSED)
|
||||
{
|
||||
// snd_pcm_multi_t *multi = pcm->private;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int snd_pcm_multi_mmap_status(snd_pcm_t *pcm, snd_pcm_mmap_status_t **status)
|
||||
{
|
||||
snd_pcm_multi_t *multi = pcm->private;
|
||||
unsigned int i;
|
||||
for (i = 0; i < multi->slaves_count; ++i) {
|
||||
snd_pcm_t *handle = multi->slaves[i].handle;
|
||||
int err = snd_pcm_mmap_status(handle, status);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
*status = multi->slaves[0].handle->mmap_status;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_multi_mmap_control(snd_pcm_t *pcm, snd_pcm_mmap_control_t **control)
|
||||
{
|
||||
snd_pcm_multi_t *multi = pcm->private;
|
||||
snd_pcm_setup_t *setup_0 = &multi->slaves[0].handle->setup;
|
||||
unsigned int i;
|
||||
for (i = 1; i < multi->slaves_count; ++i) {
|
||||
snd_pcm_setup_t *setup = &multi->slaves[i].handle->setup;
|
||||
/* Don't permit mmap if appl_ptr's have
|
||||
different ranges */
|
||||
if (setup->buffer_size != setup_0->buffer_size)
|
||||
return -EBADFD;
|
||||
}
|
||||
for (i = 0; i < multi->slaves_count; ++i) {
|
||||
snd_pcm_t *handle = multi->slaves[i].handle;
|
||||
int err = snd_pcm_mmap_control(handle, control);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
*control = multi->slaves[0].handle->mmap_control;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_multi_mmap_data(snd_pcm_t *pcm, void **buffer, size_t bsize ATTRIBUTE_UNUSED)
|
||||
static int snd_pcm_multi_mmap_data(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_multi_t *multi = pcm->private;
|
||||
unsigned int i;
|
||||
|
|
@ -609,44 +384,26 @@ static int snd_pcm_multi_mmap_data(snd_pcm_t *pcm, void **buffer, size_t bsize A
|
|||
err = snd_pcm_mmap_get_areas(handle, areas);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_pcm_areas_silence(areas, 0, setup->format.channels, setup->buffer_size, setup->format.format);
|
||||
err = snd_pcm_areas_silence(areas, 0, setup->format.channels, setup->buffer_size, setup->format.sfmt);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
*buffer = multi->slaves[0].handle->mmap_data;
|
||||
pcm->mmap_data = multi->slaves[0].handle->mmap_data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_multi_munmap_status(snd_pcm_t *pcm, snd_pcm_mmap_status_t *status ATTRIBUTE_UNUSED)
|
||||
static int snd_pcm_multi_munmap_status(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
|
||||
{
|
||||
snd_pcm_multi_t *multi = pcm->private;
|
||||
unsigned int i;
|
||||
int ret = 0;
|
||||
for (i = 0; i < multi->slaves_count; ++i) {
|
||||
snd_pcm_t *handle = multi->slaves[i].handle;
|
||||
int err = snd_pcm_munmap_status(handle);
|
||||
if (err < 0)
|
||||
ret = err;
|
||||
}
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_multi_munmap_control(snd_pcm_t *pcm, snd_pcm_mmap_control_t *control ATTRIBUTE_UNUSED)
|
||||
static int snd_pcm_multi_munmap_control(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
|
||||
{
|
||||
snd_pcm_multi_t *multi = pcm->private;
|
||||
unsigned int i;
|
||||
int ret = 0;
|
||||
for (i = 0; i < multi->slaves_count; ++i) {
|
||||
snd_pcm_t *handle = multi->slaves[i].handle;
|
||||
int err = snd_pcm_munmap_control(handle);
|
||||
if (err < 0)
|
||||
ret = err;
|
||||
}
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_multi_munmap_data(snd_pcm_t *pcm, void *buffer ATTRIBUTE_UNUSED, size_t size ATTRIBUTE_UNUSED)
|
||||
static int snd_pcm_multi_munmap_data(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_multi_t *multi = pcm->private;
|
||||
unsigned int i;
|
||||
|
|
@ -660,44 +417,64 @@ static int snd_pcm_multi_munmap_data(snd_pcm_t *pcm, void *buffer ATTRIBUTE_UNUS
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int snd_pcm_multi_channels_mask(snd_pcm_t *pcm, bitset_t *client_vmask)
|
||||
static ssize_t snd_pcm_multi_mmap_forward(snd_pcm_t *pcm, size_t size)
|
||||
{
|
||||
snd_pcm_multi_t *multi = pcm->private;
|
||||
unsigned int i;
|
||||
bitset_t *vmasks[multi->slaves_count];
|
||||
|
||||
for (i = 0; i < multi->slaves_count; ++i) {
|
||||
snd_pcm_t *handle = multi->slaves[i].handle;
|
||||
ssize_t frames = snd_pcm_mmap_forward(handle, size);
|
||||
if (frames < 0)
|
||||
return frames;
|
||||
if (i == 0) {
|
||||
size = frames;
|
||||
continue;
|
||||
}
|
||||
if ((size_t) frames != size)
|
||||
return -EBADFD;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
static int snd_pcm_multi_channels_mask(snd_pcm_t *pcm, bitset_t *cmask)
|
||||
{
|
||||
snd_pcm_multi_t *multi = pcm->private;
|
||||
unsigned int i;
|
||||
bitset_t *cmasks[multi->slaves_count];
|
||||
int err;
|
||||
for (i = 0; i < multi->slaves_count; ++i)
|
||||
vmasks[i] = bitset_alloc(multi->slaves[i].channels_total);
|
||||
cmasks[i] = bitset_alloc(multi->slaves[i].channels_total);
|
||||
for (i = 0; i < multi->bindings_count; ++i) {
|
||||
snd_pcm_multi_bind_t *b = &multi->bindings[i];
|
||||
if (bitset_get(client_vmask, b->client_channel))
|
||||
bitset_set(vmasks[b->slave], b->slave_channel);
|
||||
if (bitset_get(cmask, b->client_channel))
|
||||
bitset_set(cmasks[b->slave], b->slave_channel);
|
||||
}
|
||||
for (i = 0; i < multi->slaves_count; ++i) {
|
||||
snd_pcm_t *handle = multi->slaves[i].handle;
|
||||
err = snd_pcm_channels_mask(handle, vmasks[i]);
|
||||
err = snd_pcm_channels_mask(handle, cmasks[i]);
|
||||
if (err < 0) {
|
||||
for (i = 0; i <= multi->slaves_count; ++i)
|
||||
free(vmasks[i]);
|
||||
free(cmasks[i]);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
bitset_zero(client_vmask, pcm->setup.format.channels);
|
||||
bitset_zero(cmask, pcm->setup.format.channels);
|
||||
for (i = 0; i < multi->bindings_count; ++i) {
|
||||
snd_pcm_multi_bind_t *b = &multi->bindings[i];
|
||||
if (bitset_get(vmasks[b->slave], b->slave_channel))
|
||||
bitset_set(client_vmask, b->client_channel);
|
||||
if (bitset_get(cmasks[b->slave], b->slave_channel))
|
||||
bitset_set(cmask, b->client_channel);
|
||||
}
|
||||
for (i = 0; i < multi->slaves_count; ++i)
|
||||
free(vmasks[i]);
|
||||
free(cmasks[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_multi_file_descriptor(snd_pcm_t *pcm)
|
||||
int snd_pcm_multi_poll_descriptor(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_multi_t *multi = pcm->private;
|
||||
snd_pcm_t *handle = multi->slaves[0].handle;
|
||||
return snd_pcm_file_descriptor(handle);
|
||||
return snd_pcm_poll_descriptor(handle);
|
||||
}
|
||||
|
||||
static void snd_pcm_multi_dump(snd_pcm_t *pcm, FILE *fp)
|
||||
|
|
@ -728,35 +505,37 @@ struct snd_pcm_ops snd_pcm_multi_ops = {
|
|||
params_info: snd_pcm_multi_params_info,
|
||||
params: snd_pcm_multi_params,
|
||||
setup: snd_pcm_multi_setup,
|
||||
dump: snd_pcm_multi_dump,
|
||||
};
|
||||
|
||||
struct snd_pcm_fast_ops snd_pcm_multi_fast_ops = {
|
||||
nonblock: snd_pcm_multi_nonblock,
|
||||
channel_info: snd_pcm_multi_channel_info,
|
||||
channel_params: snd_pcm_multi_channel_params,
|
||||
channel_setup: snd_pcm_multi_channel_setup,
|
||||
status: snd_pcm_multi_status,
|
||||
hw_ptr: snd_pcm_multi_hw_ptr,
|
||||
state: snd_pcm_multi_state,
|
||||
prepare: snd_pcm_multi_prepare,
|
||||
go: snd_pcm_multi_go,
|
||||
drain: snd_pcm_multi_drain,
|
||||
flush: snd_pcm_multi_flush,
|
||||
pause: snd_pcm_multi_pause,
|
||||
write: snd_pcm_multi_write,
|
||||
writev: snd_pcm_multi_writev,
|
||||
read: snd_pcm_multi_read,
|
||||
readv: snd_pcm_multi_readv,
|
||||
appl_ptr: snd_pcm_multi_appl_ptr,
|
||||
dump: snd_pcm_multi_dump,
|
||||
nonblock: snd_pcm_multi_nonblock,
|
||||
mmap_status: snd_pcm_multi_mmap_status,
|
||||
mmap_control: snd_pcm_multi_mmap_control,
|
||||
mmap_data: snd_pcm_multi_mmap_data,
|
||||
munmap_status: snd_pcm_multi_munmap_status,
|
||||
munmap_control: snd_pcm_multi_munmap_control,
|
||||
munmap_data: snd_pcm_multi_munmap_data,
|
||||
file_descriptor: snd_pcm_multi_file_descriptor,
|
||||
};
|
||||
|
||||
struct snd_pcm_fast_ops snd_pcm_multi_fast_ops = {
|
||||
status: snd_pcm_multi_status,
|
||||
state: snd_pcm_multi_state,
|
||||
delay: snd_pcm_multi_delay,
|
||||
prepare: snd_pcm_multi_prepare,
|
||||
start: snd_pcm_multi_start,
|
||||
stop: snd_pcm_multi_stop,
|
||||
flush: snd_pcm_multi_flush,
|
||||
pause: snd_pcm_multi_pause,
|
||||
writei: snd_pcm_mmap_writei,
|
||||
writen: snd_pcm_mmap_writen,
|
||||
readi: snd_pcm_mmap_readi,
|
||||
readn: snd_pcm_mmap_readn,
|
||||
appl_ptr: snd_pcm_multi_appl_ptr,
|
||||
poll_descriptor: snd_pcm_multi_poll_descriptor,
|
||||
channels_mask: snd_pcm_multi_channels_mask,
|
||||
avail_update: snd_pcm_multi_avail_update,
|
||||
mmap_forward: snd_pcm_multi_mmap_forward,
|
||||
};
|
||||
|
||||
int snd_pcm_multi_create(snd_pcm_t **handlep, size_t slaves_count,
|
||||
|
|
@ -769,6 +548,7 @@ int snd_pcm_multi_create(snd_pcm_t **handlep, size_t slaves_count,
|
|||
snd_pcm_multi_t *multi;
|
||||
size_t channels = 0;
|
||||
unsigned int i;
|
||||
int err;
|
||||
int stream;
|
||||
char client_map[32] = { 0 };
|
||||
char slave_map[32][32] = { { 0 } };
|
||||
|
|
@ -777,12 +557,8 @@ int snd_pcm_multi_create(snd_pcm_t **handlep, size_t slaves_count,
|
|||
assert(slaves_count > 0 && slaves_handle && schannels_count);
|
||||
assert(bindings_count > 0 && bindings_slave && bindings_cchannel && bindings_schannel);
|
||||
|
||||
handle = calloc(1, sizeof(snd_pcm_t));
|
||||
if (!handle)
|
||||
return -ENOMEM;
|
||||
multi = calloc(1, sizeof(snd_pcm_multi_t));
|
||||
if (!multi) {
|
||||
free(handle);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
|
@ -808,21 +584,20 @@ int snd_pcm_multi_create(snd_pcm_t **handlep, size_t slaves_count,
|
|||
bind->client_channel = bindings_cchannel[i];
|
||||
bind->slave = bindings_slave[i];
|
||||
bind->slave_channel = bindings_schannel[i];
|
||||
if (slave_map[bindings_slave[i]][bindings_schannel[i]]) {
|
||||
assert(stream == SND_PCM_STREAM_CAPTURE);
|
||||
multi->one_to_many = 1;
|
||||
}
|
||||
assert(!slave_map[bindings_slave[i]][bindings_schannel[i]]);
|
||||
slave_map[bindings_slave[i]][bindings_schannel[i]] = 1;
|
||||
if (client_map[bindings_cchannel[i]]) {
|
||||
assert(stream == SND_PCM_STREAM_PLAYBACK);
|
||||
multi->one_to_many = 1;
|
||||
}
|
||||
assert(!client_map[bindings_cchannel[i]]);
|
||||
client_map[bindings_cchannel[i]] = 1;
|
||||
if (bindings_cchannel[i] >= channels)
|
||||
channels = bindings_cchannel[i] + 1;
|
||||
}
|
||||
multi->channels_count = channels;
|
||||
|
||||
handle = calloc(1, sizeof(snd_pcm_t));
|
||||
if (!handle) {
|
||||
free(multi);
|
||||
return -ENOMEM;
|
||||
}
|
||||
handle->type = SND_PCM_TYPE_MULTI;
|
||||
handle->stream = stream;
|
||||
handle->mode = multi->slaves[0].handle->mode;
|
||||
|
|
@ -831,6 +606,195 @@ int snd_pcm_multi_create(snd_pcm_t **handlep, size_t slaves_count,
|
|||
handle->fast_ops = &snd_pcm_multi_fast_ops;
|
||||
handle->fast_op_arg = handle;
|
||||
handle->private = multi;
|
||||
err = snd_pcm_init(handle);
|
||||
if (err < 0) {
|
||||
snd_pcm_close(handle);
|
||||
return err;
|
||||
}
|
||||
*handlep = handle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _snd_pcm_multi_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf,
|
||||
int stream, int mode)
|
||||
{
|
||||
snd_config_iterator_t i, j;
|
||||
snd_config_t *slave = NULL;
|
||||
snd_config_t *binding = NULL;
|
||||
int err;
|
||||
unsigned int idx;
|
||||
char **slaves_id = NULL;
|
||||
char **slaves_name = NULL;
|
||||
snd_pcm_t **slaves_pcm = NULL;
|
||||
size_t *slaves_channels = NULL;
|
||||
unsigned int *bindings_cchannel = NULL;
|
||||
unsigned int *bindings_slave = NULL;
|
||||
unsigned int *bindings_schannel = NULL;
|
||||
size_t slaves_count = 0;
|
||||
size_t bindings_count = 0;
|
||||
snd_config_foreach(i, conf) {
|
||||
snd_config_t *n = snd_config_entry(i);
|
||||
if (strcmp(n->id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "type") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "stream") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "slave") == 0) {
|
||||
if (snd_config_type(n) != SND_CONFIG_TYPE_COMPOUND)
|
||||
return -EINVAL;
|
||||
slave = n;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(n->id, "binding") == 0) {
|
||||
if (snd_config_type(n) != SND_CONFIG_TYPE_COMPOUND)
|
||||
return -EINVAL;
|
||||
binding = n;
|
||||
continue;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!slave || !binding)
|
||||
return -EINVAL;
|
||||
snd_config_foreach(i, slave) {
|
||||
++slaves_count;
|
||||
}
|
||||
snd_config_foreach(i, binding) {
|
||||
++bindings_count;
|
||||
}
|
||||
slaves_id = calloc(slaves_count, sizeof(*slaves_id));
|
||||
slaves_name = calloc(slaves_count, sizeof(*slaves_name));
|
||||
slaves_pcm = calloc(slaves_count, sizeof(*slaves_pcm));
|
||||
slaves_channels = calloc(slaves_count, sizeof(*slaves_channels));
|
||||
bindings_cchannel = calloc(bindings_count, sizeof(*bindings_cchannel));
|
||||
bindings_slave = calloc(bindings_count, sizeof(*bindings_slave));
|
||||
bindings_schannel = calloc(bindings_count, sizeof(*bindings_schannel));
|
||||
idx = 0;
|
||||
snd_config_foreach(i, slave) {
|
||||
snd_config_t *m = snd_config_entry(i);
|
||||
char *pcm = NULL;
|
||||
long channels = -1;
|
||||
slaves_id[idx] = snd_config_id(m);
|
||||
snd_config_foreach(j, m) {
|
||||
snd_config_t *n = snd_config_entry(j);
|
||||
if (strcmp(n->id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "pcm") == 0) {
|
||||
err = snd_config_string_get(n, &pcm);
|
||||
if (err < 0)
|
||||
goto _free;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(n->id, "channels") == 0) {
|
||||
err = snd_config_integer_get(n, &channels);
|
||||
if (err < 0)
|
||||
goto _free;
|
||||
continue;
|
||||
}
|
||||
err = -EINVAL;
|
||||
goto _free;
|
||||
}
|
||||
if (!pcm || channels < 0) {
|
||||
err = -EINVAL;
|
||||
goto _free;
|
||||
}
|
||||
slaves_name[idx] = strdup(pcm);
|
||||
slaves_channels[idx] = channels;
|
||||
++idx;
|
||||
}
|
||||
|
||||
idx = 0;
|
||||
snd_config_foreach(i, binding) {
|
||||
snd_config_t *m = snd_config_entry(i);
|
||||
long cchannel = -1, schannel = -1;
|
||||
int slave = -1;
|
||||
long val;
|
||||
char *str;
|
||||
snd_config_foreach(j, m) {
|
||||
snd_config_t *n = snd_config_entry(j);
|
||||
if (strcmp(n->id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "client_channel") == 0) {
|
||||
err = snd_config_integer_get(n, &cchannel);
|
||||
if (err < 0)
|
||||
goto _free;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(n->id, "slave") == 0) {
|
||||
char buf[32];
|
||||
unsigned int k;
|
||||
err = snd_config_string_get(n, &str);
|
||||
if (err < 0) {
|
||||
err = snd_config_integer_get(n, &val);
|
||||
if (err < 0)
|
||||
goto _free;
|
||||
sprintf(buf, "%ld", val);
|
||||
str = buf;
|
||||
}
|
||||
for (k = 0; k < slaves_count; ++k) {
|
||||
if (strcmp(slaves_id[k], str) == 0)
|
||||
slave = k;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (strcmp(n->id, "slave_channel") == 0) {
|
||||
err = snd_config_integer_get(n, &schannel);
|
||||
if (err < 0)
|
||||
goto _free;
|
||||
continue;
|
||||
}
|
||||
err = -EINVAL;
|
||||
goto _free;
|
||||
}
|
||||
if (cchannel < 0 || slave < 0 || schannel < 0) {
|
||||
err = -EINVAL;
|
||||
goto _free;
|
||||
}
|
||||
if ((size_t)slave >= slaves_count) {
|
||||
err = -EINVAL;
|
||||
goto _free;
|
||||
}
|
||||
if ((unsigned int) schannel >= slaves_channels[slave]) {
|
||||
err = -EINVAL;
|
||||
goto _free;
|
||||
}
|
||||
bindings_cchannel[idx] = cchannel;
|
||||
bindings_slave[idx] = slave;
|
||||
bindings_schannel[idx] = schannel;
|
||||
++idx;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < slaves_count; ++idx) {
|
||||
err = snd_pcm_open(&slaves_pcm[idx], slaves_name[idx], stream, mode);
|
||||
if (err < 0)
|
||||
goto _free;
|
||||
}
|
||||
err = snd_pcm_multi_create(pcmp, slaves_count, slaves_pcm,
|
||||
slaves_channels,
|
||||
bindings_count, bindings_cchannel,
|
||||
bindings_slave, bindings_schannel,
|
||||
1);
|
||||
_free:
|
||||
if (err < 0) {
|
||||
for (idx = 0; idx < slaves_count; ++idx) {
|
||||
if (slaves_pcm[idx])
|
||||
snd_pcm_close(slaves_pcm[idx]);
|
||||
if (slaves_name[idx])
|
||||
free(slaves_name[idx]);
|
||||
}
|
||||
}
|
||||
if (slaves_name)
|
||||
free(slaves_name);
|
||||
if (slaves_pcm)
|
||||
free(slaves_pcm);
|
||||
if (slaves_channels)
|
||||
free(slaves_channels);
|
||||
if (bindings_cchannel)
|
||||
free(bindings_cchannel);
|
||||
if (bindings_slave)
|
||||
free(bindings_slave);
|
||||
if (bindings_schannel)
|
||||
free(bindings_schannel);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
|||
1191
src/pcm/pcm_plug.c
1191
src/pcm/pcm_plug.c
File diff suppressed because it is too large
Load diff
394
src/pcm/pcm_plugin.c
Normal file
394
src/pcm/pcm_plugin.c
Normal file
|
|
@ -0,0 +1,394 @@
|
|||
/*
|
||||
* PCM - Common plugin code
|
||||
* 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 "pcm_local.h"
|
||||
#include "pcm_plugin.h"
|
||||
#include <limits.h>
|
||||
|
||||
int snd_pcm_plugin_close(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
int err = 0;
|
||||
if (plugin->close_slave)
|
||||
err = snd_pcm_close(plugin->slave);
|
||||
free(plugin);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_nonblock(snd_pcm_t *pcm, int nonblock)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
return snd_pcm_nonblock(plugin->slave, nonblock);
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
return snd_pcm_info(plugin->slave, info);
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
return snd_pcm_channel_info(plugin->slave, info);
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
return snd_pcm_channel_params(plugin->slave, params);
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
int err;
|
||||
err = snd_pcm_channel_setup(plugin->slave, setup);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (pcm->setup.mmap_shape == SND_PCM_MMAP_INTERLEAVED) {
|
||||
setup->area.addr = pcm->mmap_data;
|
||||
setup->area.first = setup->channel * pcm->bits_per_sample;
|
||||
setup->area.step = pcm->bits_per_frame;
|
||||
} else {
|
||||
setup->area.addr = pcm->mmap_data + setup->channel * pcm->setup.buffer_size * pcm->bits_per_sample / 8;
|
||||
setup->area.first = 0;
|
||||
setup->area.step = pcm->bits_per_sample;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
int err = snd_pcm_status(plugin->slave, status);
|
||||
if (err < 0)
|
||||
return err;
|
||||
status->hw_ptr = plugin->mmap_status.hw_ptr;
|
||||
status->appl_ptr = plugin->mmap_control.appl_ptr;
|
||||
status->avail = (pcm->stream == SND_PCM_STREAM_PLAYBACK ?
|
||||
snd_pcm_mmap_playback_avail(pcm) :
|
||||
snd_pcm_mmap_capture_avail(pcm));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_state(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
return snd_pcm_state(plugin->slave);
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_delay(snd_pcm_t *pcm, ssize_t *delayp)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
ssize_t sd;
|
||||
int err = snd_pcm_delay(plugin->slave, &sd);
|
||||
int d;
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (plugin->client_frames)
|
||||
sd = plugin->client_frames(pcm, sd);
|
||||
if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
|
||||
d = pcm->setup.buffer_size - snd_pcm_mmap_playback_avail(pcm);
|
||||
else
|
||||
d = snd_pcm_mmap_capture_avail(pcm);
|
||||
*delayp = sd + d;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_prepare(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
int err = snd_pcm_prepare(plugin->slave);
|
||||
if (err < 0)
|
||||
return err;
|
||||
plugin->mmap_status.hw_ptr = 0;
|
||||
plugin->mmap_control.appl_ptr = 0;
|
||||
if (plugin->init) {
|
||||
err = plugin->init(pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_start(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
return snd_pcm_start(plugin->slave);
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_stop(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
return snd_pcm_stop(plugin->slave);
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_flush(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
return snd_pcm_flush(plugin->slave);
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_pause(snd_pcm_t *pcm, int enable)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
return snd_pcm_pause(plugin->slave, enable);
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_plugin_appl_ptr(snd_pcm_t *pcm, off_t offset)
|
||||
{
|
||||
/* FIXME */
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_plugin_writei(snd_pcm_t *pcm, const void *buffer, size_t size)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
snd_pcm_channel_area_t areas[pcm->setup.format.channels];
|
||||
ssize_t frames;
|
||||
snd_pcm_areas_from_buf(pcm, areas, (void*)buffer);
|
||||
frames = snd_pcm_write_areas(pcm, areas, 0, size, plugin->write);
|
||||
if (frames > 0)
|
||||
snd_pcm_mmap_appl_forward(pcm, frames);
|
||||
return frames;
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_plugin_writen(snd_pcm_t *pcm, void **bufs, size_t size)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
snd_pcm_channel_area_t areas[pcm->setup.format.channels];
|
||||
ssize_t frames;
|
||||
snd_pcm_areas_from_bufs(pcm, areas, bufs);
|
||||
frames = snd_pcm_write_areas(pcm, areas, 0, size, plugin->write);
|
||||
if (frames > 0)
|
||||
snd_pcm_mmap_appl_forward(pcm, frames);
|
||||
return frames;
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_plugin_readi(snd_pcm_t *pcm, void *buffer, size_t size)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
snd_pcm_channel_area_t areas[pcm->setup.format.channels];
|
||||
ssize_t frames;
|
||||
snd_pcm_areas_from_buf(pcm, areas, buffer);
|
||||
frames = snd_pcm_read_areas(pcm, areas, 0, size, plugin->read);
|
||||
if (frames > 0)
|
||||
snd_pcm_mmap_appl_forward(pcm, frames);
|
||||
return frames;
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_plugin_readn(snd_pcm_t *pcm, void **bufs, size_t size)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
snd_pcm_channel_area_t areas[pcm->setup.format.channels];
|
||||
ssize_t frames;
|
||||
snd_pcm_areas_from_bufs(pcm, areas, bufs);
|
||||
frames = snd_pcm_read_areas(pcm, areas, 0, size, plugin->read);
|
||||
if (frames > 0)
|
||||
snd_pcm_mmap_appl_forward(pcm, frames);
|
||||
return frames;
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_plugin_mmap_forward(snd_pcm_t *pcm, size_t client_size)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
snd_pcm_t *slave = plugin->slave;
|
||||
size_t client_xfer = 0;
|
||||
size_t slave_xfer = 0;
|
||||
ssize_t err = 0;
|
||||
ssize_t slave_size;
|
||||
if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
|
||||
snd_pcm_mmap_appl_forward(pcm, client_size);
|
||||
return client_size;
|
||||
}
|
||||
slave_size = snd_pcm_avail_update(slave);
|
||||
if (slave_size <= 0)
|
||||
return slave_size;
|
||||
while (client_xfer < client_size &&
|
||||
slave_xfer < (size_t)slave_size) {
|
||||
size_t slave_frames = slave_size - slave_xfer;
|
||||
size_t client_frames = client_size - client_xfer;
|
||||
size_t cont = pcm->setup.buffer_size - snd_pcm_mmap_hw_offset(pcm);
|
||||
if (cont < client_frames)
|
||||
client_frames = cont;
|
||||
err = plugin->write(pcm, pcm->mmap_areas,
|
||||
snd_pcm_mmap_hw_offset(pcm),
|
||||
client_frames, &slave_frames);
|
||||
if (err < 0)
|
||||
break;
|
||||
snd_pcm_mmap_appl_forward(pcm, err);
|
||||
client_xfer += err;
|
||||
slave_xfer += slave_frames;
|
||||
}
|
||||
if (client_xfer > 0)
|
||||
return client_xfer;
|
||||
return err;
|
||||
}
|
||||
|
||||
ssize_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
snd_pcm_t *slave = plugin->slave;
|
||||
size_t client_xfer;
|
||||
size_t slave_xfer = 0;
|
||||
ssize_t err = 0;
|
||||
size_t client_size;
|
||||
ssize_t slave_size = snd_pcm_avail_update(slave);
|
||||
if (slave_size <= 0)
|
||||
return slave_size;
|
||||
if (pcm->stream == SND_PCM_STREAM_PLAYBACK ||
|
||||
!pcm->mmap_data)
|
||||
return plugin->client_frames ?
|
||||
plugin->client_frames(pcm, slave_size) : slave_size;
|
||||
client_xfer = snd_pcm_mmap_capture_avail(pcm);
|
||||
client_size = pcm->setup.buffer_size;
|
||||
while (slave_xfer < (size_t)slave_size &&
|
||||
client_xfer < client_size) {
|
||||
size_t slave_frames = slave_size - slave_xfer;
|
||||
size_t client_frames = client_size - client_xfer;
|
||||
size_t cont = pcm->setup.buffer_size - snd_pcm_mmap_hw_offset(pcm);
|
||||
if (cont < client_frames)
|
||||
client_frames = cont;
|
||||
err = plugin->read(pcm, pcm->mmap_areas,
|
||||
snd_pcm_mmap_hw_offset(pcm),
|
||||
client_frames, &slave_frames);
|
||||
if (err < 0)
|
||||
break;
|
||||
client_xfer += err;
|
||||
slave_xfer += slave_frames;
|
||||
}
|
||||
if (client_xfer > 0)
|
||||
return client_xfer;
|
||||
return err;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_mmap_status(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
pcm->mmap_status = &plugin->mmap_status;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_mmap_control(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
pcm->mmap_control = &plugin->mmap_control;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_mmap_data(snd_pcm_t *pcm)
|
||||
{
|
||||
void *ptr = malloc(snd_pcm_frames_to_bytes(pcm, pcm->setup.buffer_size));
|
||||
if (!ptr)
|
||||
return -ENOMEM;
|
||||
pcm->mmap_data = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_munmap_status(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_munmap_control(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_munmap_data(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
|
||||
{
|
||||
free(pcm->mmap_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_poll_descriptor(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
return snd_pcm_poll_descriptor(plugin->slave);
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_channels_mask(snd_pcm_t *pcm, bitset_t *cmask)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
return snd_pcm_channels_mask(plugin->slave, cmask);
|
||||
}
|
||||
|
||||
int conv_index(int src_format, int dst_format)
|
||||
{
|
||||
int src_endian, dst_endian, sign, src_width, dst_width;
|
||||
|
||||
sign = (snd_pcm_format_signed(src_format) !=
|
||||
snd_pcm_format_signed(dst_format));
|
||||
#ifdef SND_LITTLE_ENDIAN
|
||||
src_endian = snd_pcm_format_big_endian(src_format);
|
||||
dst_endian = snd_pcm_format_big_endian(dst_format);
|
||||
#else
|
||||
src_endian = snd_pcm_format_little_endian(src_format);
|
||||
dst_endian = snd_pcm_format_little_endian(dst_format);
|
||||
#endif
|
||||
|
||||
if (src_endian < 0)
|
||||
src_endian = 0;
|
||||
if (dst_endian < 0)
|
||||
dst_endian = 0;
|
||||
|
||||
src_width = snd_pcm_format_width(src_format) / 8 - 1;
|
||||
dst_width = snd_pcm_format_width(dst_format) / 8 - 1;
|
||||
|
||||
return src_width * 32 + src_endian * 16 + sign * 8 + dst_width * 2 + dst_endian;
|
||||
}
|
||||
|
||||
int getput_index(int format)
|
||||
{
|
||||
int sign, width, endian;
|
||||
sign = !snd_pcm_format_signed(format);
|
||||
width = snd_pcm_format_width(format) / 8 - 1;
|
||||
#ifdef SND_LITTLE_ENDIAN
|
||||
endian = snd_pcm_format_big_endian(format);
|
||||
#else
|
||||
endian = snd_pcm_format_little_endian(format);
|
||||
#endif
|
||||
if (endian < 0)
|
||||
endian = 0;
|
||||
return width * 4 + endian * 2 + sign;
|
||||
}
|
||||
|
||||
struct snd_pcm_fast_ops snd_pcm_plugin_fast_ops = {
|
||||
status: snd_pcm_plugin_status,
|
||||
state: snd_pcm_plugin_state,
|
||||
delay: snd_pcm_plugin_delay,
|
||||
prepare: snd_pcm_plugin_prepare,
|
||||
start: snd_pcm_plugin_start,
|
||||
stop: snd_pcm_plugin_stop,
|
||||
flush: snd_pcm_plugin_flush,
|
||||
pause: snd_pcm_plugin_pause,
|
||||
appl_ptr: snd_pcm_plugin_appl_ptr,
|
||||
writei: snd_pcm_plugin_writei,
|
||||
writen: snd_pcm_plugin_writen,
|
||||
readi: snd_pcm_plugin_readi,
|
||||
readn: snd_pcm_plugin_readn,
|
||||
poll_descriptor: snd_pcm_plugin_poll_descriptor,
|
||||
channels_mask: snd_pcm_plugin_channels_mask,
|
||||
avail_update: snd_pcm_plugin_avail_update,
|
||||
mmap_forward: snd_pcm_plugin_mmap_forward,
|
||||
};
|
||||
|
||||
105
src/pcm/pcm_plugin.h
Normal file
105
src/pcm/pcm_plugin.h
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* PCM - Common plugin code
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
snd_pcm_t *slave;
|
||||
int close_slave;
|
||||
snd_pcm_xfer_areas_func_t read;
|
||||
snd_pcm_xfer_areas_func_t write;
|
||||
size_t (*client_frames)(snd_pcm_t *pcm, size_t frames);
|
||||
int (*init)(snd_pcm_t *pcm);
|
||||
snd_pcm_mmap_control_t mmap_control;
|
||||
snd_pcm_mmap_status_t mmap_status;
|
||||
} snd_pcm_plugin_t;
|
||||
|
||||
int snd_pcm_plugin_close(snd_pcm_t *pcm);
|
||||
int snd_pcm_plugin_nonblock(snd_pcm_t *pcm, int nonblock);
|
||||
int snd_pcm_plugin_info(snd_pcm_t *pcm, snd_pcm_info_t * info);
|
||||
int snd_pcm_plugin_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info);
|
||||
int snd_pcm_plugin_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params);
|
||||
int snd_pcm_plugin_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup);
|
||||
int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_status_t * status);
|
||||
int snd_pcm_plugin_state(snd_pcm_t *pcm);
|
||||
int snd_pcm_plugin_delay(snd_pcm_t *pcm, ssize_t *delayp);
|
||||
int snd_pcm_plugin_prepare(snd_pcm_t *pcm);
|
||||
int snd_pcm_plugin_start(snd_pcm_t *pcm);
|
||||
int snd_pcm_plugin_stop(snd_pcm_t *pcm);
|
||||
int snd_pcm_plugin_flush(snd_pcm_t *pcm);
|
||||
int snd_pcm_plugin_pause(snd_pcm_t *pcm, int enable);
|
||||
ssize_t snd_pcm_plugin_appl_ptr(snd_pcm_t *pcm, off_t offset);
|
||||
ssize_t snd_pcm_plugin_writei(snd_pcm_t *pcm, const void *buffer, size_t size);
|
||||
ssize_t snd_pcm_plugin_writen(snd_pcm_t *pcm, void **bufs, size_t size);
|
||||
ssize_t snd_pcm_plugin_readi(snd_pcm_t *pcm, void *buffer, size_t size);
|
||||
ssize_t snd_pcm_plugin_readn(snd_pcm_t *pcm, void **bufs, size_t size);
|
||||
ssize_t snd_pcm_plugin_mmap_forward(snd_pcm_t *pcm, size_t size);
|
||||
ssize_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm);
|
||||
int snd_pcm_plugin_mmap_status(snd_pcm_t *pcm);
|
||||
int snd_pcm_plugin_mmap_control(snd_pcm_t *pcm);
|
||||
int snd_pcm_plugin_mmap_data(snd_pcm_t *pcm);
|
||||
int snd_pcm_plugin_munmap_status(snd_pcm_t *pcm);
|
||||
int snd_pcm_plugin_munmap_control(snd_pcm_t *pcm);
|
||||
int snd_pcm_plugin_munmap_data(snd_pcm_t *pcm);
|
||||
int snd_pcm_plugin_poll_descriptor(snd_pcm_t *pcm);
|
||||
int snd_pcm_plugin_channels_mask(snd_pcm_t *pcm, bitset_t *cmask);
|
||||
int getput_index(int format);
|
||||
int conv_index(int src_format, int dst_format);
|
||||
|
||||
#define SND_PCM_LINEAR_FORMATS (SND_PCM_FMT_S8 | SND_PCM_FMT_U8 | \
|
||||
SND_PCM_FMT_S16_LE | SND_PCM_FMT_S16_BE | \
|
||||
SND_PCM_FMT_U16_LE | SND_PCM_FMT_U16_BE | \
|
||||
SND_PCM_FMT_S24_LE | SND_PCM_FMT_S24_BE | \
|
||||
SND_PCM_FMT_U24_LE | SND_PCM_FMT_U24_BE | \
|
||||
SND_PCM_FMT_S32_LE | SND_PCM_FMT_S32_BE | \
|
||||
SND_PCM_FMT_U32_LE | SND_PCM_FMT_U32_BE)
|
||||
|
||||
extern struct snd_pcm_fast_ops snd_pcm_plugin_fast_ops;
|
||||
|
||||
#define muldiv64(a,b,d) (((int64_t)(a) * (b) + (b) / 2) / (d))
|
||||
|
||||
#define ROUTE_PLUGIN_FLOAT 1
|
||||
#define ROUTE_PLUGIN_RESOLUTION 16
|
||||
|
||||
#if ROUTE_PLUGIN_FLOAT
|
||||
typedef float ttable_entry_t;
|
||||
#define HALF 0.5
|
||||
#define FULL 1.0
|
||||
#else
|
||||
typedef int ttable_entry_t;
|
||||
#define HALF (ROUTE_PLUGIN_RESOLUTION / 2)
|
||||
#define FULL ROUTE_PLUGIN_RESOLUTION
|
||||
#endif
|
||||
|
||||
int snd_pcm_linear_open(snd_pcm_t **handlep, int sformat, snd_pcm_t *slave, int close_slave);
|
||||
int snd_pcm_mulaw_open(snd_pcm_t **handlep, int sformat, snd_pcm_t *slave, int close_slave);
|
||||
int snd_pcm_alaw_open(snd_pcm_t **handlep, int sformat, snd_pcm_t *slave, int close_slave);
|
||||
int snd_pcm_adpcm_open(snd_pcm_t **handlep, int sformat, snd_pcm_t *slave, int close_slave);
|
||||
int snd_pcm_route_load_ttable(snd_config_t *tt, ttable_entry_t *ttable,
|
||||
unsigned int tt_csize, unsigned int tt_ssize,
|
||||
unsigned int *tt_cused, unsigned int *tt_sused,
|
||||
int schannels);
|
||||
int snd_pcm_route_open(snd_pcm_t **handlep,
|
||||
int sformat, unsigned int schannels,
|
||||
ttable_entry_t *ttable,
|
||||
unsigned int tt_ssize,
|
||||
unsigned int tt_cused, unsigned int tt_sused,
|
||||
snd_pcm_t *slave, int close_slave);
|
||||
int snd_pcm_rate_open(snd_pcm_t **handlep, int sformat, int srate, snd_pcm_t *slave, int close_slave);
|
||||
|
||||
693
src/pcm/pcm_rate.c
Normal file
693
src/pcm/pcm_rate.c
Normal file
|
|
@ -0,0 +1,693 @@
|
|||
/*
|
||||
* PCM - Rate conversion
|
||||
* 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 <limits.h>
|
||||
#include <byteswap.h>
|
||||
#include "pcm_local.h"
|
||||
#include "pcm_plugin.h"
|
||||
|
||||
#define DIV (1<<16)
|
||||
|
||||
typedef struct {
|
||||
int16_t sample;
|
||||
int sum;
|
||||
unsigned int pos;
|
||||
} rate_state_t;
|
||||
|
||||
typedef size_t (*rate_f)(snd_pcm_channel_area_t *src_areas,
|
||||
size_t src_offset, size_t src_frames,
|
||||
snd_pcm_channel_area_t *dst_areas,
|
||||
size_t dst_offset, size_t *dst_framesp,
|
||||
size_t channels,
|
||||
int getidx, int putidx,
|
||||
unsigned int arg,
|
||||
rate_state_t *states);
|
||||
|
||||
typedef struct {
|
||||
/* This field need to be the first */
|
||||
snd_pcm_plugin_t plug;
|
||||
int get_idx;
|
||||
int put_idx;
|
||||
unsigned int pitch;
|
||||
rate_f func;
|
||||
int req_sformat;
|
||||
int req_srate;
|
||||
int sformat;
|
||||
int cformat;
|
||||
int srate;
|
||||
int crate;
|
||||
int cxfer_mode, cmmap_shape;
|
||||
rate_state_t *states;
|
||||
} snd_pcm_rate_t;
|
||||
|
||||
static size_t resample_expand(snd_pcm_channel_area_t *src_areas,
|
||||
size_t src_offset, size_t src_frames,
|
||||
snd_pcm_channel_area_t *dst_areas,
|
||||
size_t dst_offset, size_t *dst_framesp,
|
||||
size_t channels,
|
||||
int getidx, int putidx,
|
||||
unsigned int get_threshold,
|
||||
rate_state_t *states)
|
||||
{
|
||||
#define GET_S16_LABELS
|
||||
#define PUT_S16_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_LABELS
|
||||
#undef PUT_S16_LABELS
|
||||
void *get = get_s16_labels[getidx];
|
||||
void *put = put_s16_labels[putidx];
|
||||
unsigned int channel;
|
||||
size_t src_frames1 = 0;
|
||||
size_t dst_frames1 = 0;
|
||||
size_t dst_frames = *dst_framesp;
|
||||
int16_t sample = 0;
|
||||
|
||||
if (src_frames == 0 ||
|
||||
dst_frames == 0)
|
||||
return 0;
|
||||
for (channel = 0; channel < channels; ++channel) {
|
||||
snd_pcm_channel_area_t *src_area = &src_areas[channel];
|
||||
snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
|
||||
char *src, *dst;
|
||||
int src_step, dst_step;
|
||||
int16_t old_sample = states->sample;
|
||||
unsigned int pos = states->pos;
|
||||
#if 0
|
||||
if (!src_area->enabled) {
|
||||
if (dst_area->wanted)
|
||||
snd_pcm_area_silence(&dst_area->area, 0, dst_frames, plugin->dst_format.sfmt);
|
||||
dst_area->enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_area->enabled = 1;
|
||||
#endif
|
||||
src = snd_pcm_channel_area_addr(src_area, src_offset);
|
||||
dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
|
||||
src_step = snd_pcm_channel_area_step(src_area);
|
||||
dst_step = snd_pcm_channel_area_step(dst_area);
|
||||
src_frames1 = 0;
|
||||
dst_frames1 = 0;
|
||||
while (dst_frames1 < dst_frames) {
|
||||
if (pos >= get_threshold) {
|
||||
int16_t new_sample;
|
||||
if (src_frames1 == src_frames)
|
||||
break;
|
||||
pos -= get_threshold;
|
||||
goto *get;
|
||||
#define GET_S16_END after_get
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_END
|
||||
after_get:
|
||||
src += src_step;
|
||||
src_frames1++;
|
||||
new_sample = sample;
|
||||
sample = (old_sample * (DIV - pos) + new_sample * pos) / DIV;
|
||||
old_sample = new_sample;
|
||||
} else
|
||||
sample = old_sample;
|
||||
goto *put;
|
||||
#define PUT_S16_END after_put
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_S16_END
|
||||
after_put:
|
||||
dst += dst_step;
|
||||
dst_frames1++;
|
||||
pos += DIV;
|
||||
}
|
||||
states->sample = old_sample;
|
||||
states->pos = pos;
|
||||
states++;
|
||||
}
|
||||
*dst_framesp = dst_frames1;
|
||||
return src_frames1;
|
||||
}
|
||||
|
||||
static size_t resample_shrink(snd_pcm_channel_area_t *src_areas,
|
||||
size_t src_offset, size_t src_frames,
|
||||
snd_pcm_channel_area_t *dst_areas,
|
||||
size_t dst_offset, size_t *dst_framesp,
|
||||
size_t channels,
|
||||
int getidx, int putidx,
|
||||
unsigned int get_increment,
|
||||
rate_state_t *states)
|
||||
{
|
||||
#define GET_S16_LABELS
|
||||
#define PUT_S16_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_LABELS
|
||||
#undef PUT_S16_LABELS
|
||||
void *get = get_s16_labels[getidx];
|
||||
void *put = put_s16_labels[putidx];
|
||||
unsigned int channel;
|
||||
size_t src_frames1 = 0;
|
||||
size_t dst_frames1 = 0;
|
||||
size_t dst_frames = *dst_framesp;
|
||||
int16_t sample = 0;
|
||||
|
||||
if (src_frames == 0 ||
|
||||
dst_frames == 0)
|
||||
return 0;
|
||||
for (channel = 0; channel < channels; ++channel) {
|
||||
snd_pcm_channel_area_t *src_area = &src_areas[channel];
|
||||
snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
|
||||
unsigned int pos;
|
||||
int sum;
|
||||
char *src, *dst;
|
||||
int src_step, dst_step;
|
||||
sum = states->sum;
|
||||
pos = states->pos;
|
||||
#if 0
|
||||
if (!src_area->enabled) {
|
||||
if (dst_area->wanted)
|
||||
snd_pcm_area_silence(&dst_area->area, 0, dst_frames, plugin->dst_format.sfmt);
|
||||
dst_area->enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_area->enabled = 1;
|
||||
#endif
|
||||
src = snd_pcm_channel_area_addr(src_area, src_offset);
|
||||
dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
|
||||
src_step = snd_pcm_channel_area_step(src_area);
|
||||
dst_step = snd_pcm_channel_area_step(dst_area);
|
||||
src_frames1 = 0;
|
||||
dst_frames1 = 0;
|
||||
while (src_frames1 < src_frames) {
|
||||
|
||||
goto *get;
|
||||
#define GET_S16_END after_get
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_END
|
||||
after_get:
|
||||
src += src_step;
|
||||
src_frames1++;
|
||||
pos += get_increment;
|
||||
if (pos >= DIV) {
|
||||
int s = sample;
|
||||
pos -= DIV;
|
||||
sum += s * (get_increment - pos);
|
||||
sum /= DIV;
|
||||
sample = sum;
|
||||
goto *put;
|
||||
#define PUT_S16_END after_put
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_S16_END
|
||||
after_put:
|
||||
dst += dst_step;
|
||||
sum = s * pos;
|
||||
dst_frames1++;
|
||||
if (dst_frames1 == dst_frames)
|
||||
break;
|
||||
} else
|
||||
sum += sample * get_increment;
|
||||
}
|
||||
states->sum = sum;
|
||||
states->pos = pos;
|
||||
states++;
|
||||
}
|
||||
*dst_framesp = dst_frames1;
|
||||
return src_frames1;
|
||||
}
|
||||
|
||||
static int snd_pcm_rate_close(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_rate_t *rate = pcm->private;
|
||||
int err = 0;
|
||||
if (rate->plug.close_slave)
|
||||
err = snd_pcm_close(rate->plug.slave);
|
||||
if (rate->states)
|
||||
free(rate->states);
|
||||
free(rate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_rate_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info)
|
||||
{
|
||||
snd_pcm_rate_t *rate = pcm->private;
|
||||
unsigned int req_mask = info->req_mask;
|
||||
unsigned int sfmt = info->req.format.sfmt;
|
||||
unsigned int crate = info->req.format.rate;
|
||||
unsigned int srate;
|
||||
int err;
|
||||
if (req_mask & SND_PCM_PARAMS_SFMT &&
|
||||
!snd_pcm_format_linear(sfmt)) {
|
||||
info->req.fail_mask = SND_PCM_PARAMS_SFMT;
|
||||
info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
|
||||
return -EINVAL;
|
||||
}
|
||||
if (rate->req_sformat >= 0) {
|
||||
info->req_mask |= SND_PCM_PARAMS_SFMT;
|
||||
info->req.format.sfmt = rate->req_sformat;
|
||||
}
|
||||
info->req_mask |= SND_PCM_PARAMS_RATE;
|
||||
info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE |
|
||||
SND_PCM_PARAMS_XFER_MODE);
|
||||
info->req.format.rate = rate->req_srate;
|
||||
err = snd_pcm_params_info(rate->plug.slave, info);
|
||||
info->req_mask = req_mask;
|
||||
info->req.format.sfmt = sfmt;
|
||||
info->req.format.rate = crate;
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (req_mask & SND_PCM_PARAMS_SFMT)
|
||||
info->formats = 1 << sfmt;
|
||||
else
|
||||
info->formats = SND_PCM_LINEAR_FORMATS;
|
||||
if (!(req_mask & SND_PCM_PARAMS_RATE)) {
|
||||
info->min_rate = 4000;
|
||||
info->max_rate = 192000;
|
||||
return 0;
|
||||
}
|
||||
if (rate->req_srate - info->min_rate < info->max_rate - rate->req_srate)
|
||||
srate = info->min_rate;
|
||||
else
|
||||
srate = info->max_rate;
|
||||
info->min_rate = crate;
|
||||
info->max_rate = crate;
|
||||
if (info->buffer_size)
|
||||
info->buffer_size = muldiv64(info->buffer_size, crate, srate);
|
||||
if (info->min_fragment_size)
|
||||
info->min_fragment_size = muldiv64(info->min_fragment_size, crate, srate);
|
||||
if (info->max_fragment_size)
|
||||
info->max_fragment_size = muldiv64(info->max_fragment_size, crate, srate);
|
||||
if (info->fragment_align)
|
||||
info->fragment_align = muldiv64(info->fragment_align, crate, srate);
|
||||
info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
|
||||
info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_rate_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
|
||||
{
|
||||
snd_pcm_rate_t *rate = pcm->private;
|
||||
snd_pcm_t *slave = rate->plug.slave;
|
||||
snd_pcm_params_t slave_params;
|
||||
snd_pcm_params_info_t slave_info;
|
||||
int srate, crate;
|
||||
int err;
|
||||
if (!snd_pcm_format_linear(params->format.sfmt)) {
|
||||
params->fail_mask = SND_PCM_PARAMS_SFMT;
|
||||
params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
|
||||
return -EINVAL;
|
||||
}
|
||||
slave_params = *params;
|
||||
rate->cformat = params->format.sfmt;
|
||||
rate->crate = crate = params->format.rate;
|
||||
rate->cxfer_mode = params->xfer_mode;
|
||||
rate->cmmap_shape = params->mmap_shape;
|
||||
|
||||
memset(&slave_info, 0, sizeof(slave_info));
|
||||
slave_info.req = *params;
|
||||
if (rate->req_sformat >= 0)
|
||||
slave_info.req.format.sfmt = rate->req_sformat;
|
||||
slave_info.req.format.rate = rate->req_srate;
|
||||
slave_info.req_mask = ~0;
|
||||
err = snd_pcm_params_info(slave, &slave_info);
|
||||
if (err < 0) {
|
||||
params->fail_mask = slave_info.req.fail_mask;
|
||||
params->fail_reason = slave_info.req.fail_reason;
|
||||
return err;
|
||||
}
|
||||
|
||||
if (slave->mmap_data) {
|
||||
err = snd_pcm_munmap_data(slave);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (rate->req_srate - slave_info.min_rate < slave_info.max_rate - rate->req_srate)
|
||||
srate = slave_info.min_rate;
|
||||
else
|
||||
srate = slave_info.max_rate;
|
||||
|
||||
slave_params.format.rate = srate;
|
||||
slave_params.avail_min = muldiv64(params->avail_min, srate, crate);
|
||||
slave_params.xfer_min = muldiv64(params->xfer_min, srate, crate);
|
||||
slave_params.buffer_size = muldiv64(params->buffer_size, srate, crate);
|
||||
slave_params.frag_size = muldiv64(params->frag_size, srate, crate);
|
||||
slave_params.xfer_align = muldiv64(params->xfer_align, srate, crate);
|
||||
slave_params.xrun_max = muldiv64(params->xrun_max, srate, crate);
|
||||
/* FIXME: boundary? */
|
||||
slave_params.xfer_mode = SND_PCM_XFER_UNSPECIFIED;
|
||||
slave_params.mmap_shape = SND_PCM_MMAP_UNSPECIFIED;;
|
||||
err = snd_pcm_params(slave, &slave_params);
|
||||
params->fail_mask = slave_params.fail_mask;
|
||||
params->fail_reason = slave_params.fail_reason;
|
||||
if (slave->valid_setup) {
|
||||
int r = snd_pcm_mmap_data(slave, NULL);
|
||||
assert(r >= 0);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int snd_pcm_rate_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
|
||||
{
|
||||
snd_pcm_rate_t *rate = pcm->private;
|
||||
int src_format, dst_format;
|
||||
int src_rate, dst_rate;
|
||||
int mul, div;
|
||||
int err = snd_pcm_setup(rate->plug.slave, setup);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (rate->req_sformat >= 0)
|
||||
assert(rate->req_sformat == setup->format.sfmt);
|
||||
rate->sformat = setup->format.sfmt;
|
||||
rate->srate = setup->format.rate;
|
||||
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
|
||||
src_format = rate->cformat;
|
||||
dst_format = rate->sformat;
|
||||
src_rate = rate->crate;
|
||||
dst_rate = rate->srate;
|
||||
} else {
|
||||
src_format = rate->sformat;
|
||||
dst_format = rate->cformat;
|
||||
src_rate = rate->srate;
|
||||
dst_rate = rate->crate;
|
||||
}
|
||||
rate->get_idx = getput_index(src_format);
|
||||
rate->put_idx = getput_index(dst_format);
|
||||
if (src_rate < dst_rate) {
|
||||
rate->func = resample_expand;
|
||||
/* pitch is get_threshold */
|
||||
} else {
|
||||
rate->func = resample_shrink;
|
||||
/* pitch is get_increment */
|
||||
}
|
||||
rate->pitch = (((u_int64_t)dst_rate * DIV) + src_rate / 2) / src_rate;
|
||||
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
|
||||
mul = DIV;
|
||||
div = rate->pitch;
|
||||
} else {
|
||||
mul = rate->pitch;
|
||||
div = DIV;
|
||||
}
|
||||
rate->crate = muldiv64(rate->srate, mul, div);
|
||||
if (rate->cxfer_mode == SND_PCM_XFER_UNSPECIFIED)
|
||||
setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED;
|
||||
else
|
||||
setup->xfer_mode = rate->cxfer_mode;
|
||||
if (rate->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED)
|
||||
setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED;
|
||||
else
|
||||
setup->mmap_shape = rate->cmmap_shape;
|
||||
setup->format.sfmt = rate->cformat;
|
||||
setup->format.rate = rate->crate;
|
||||
/* FIXME */
|
||||
setup->rate_master = rate->crate;
|
||||
setup->rate_divisor = 1;
|
||||
setup->mmap_bytes = 0;
|
||||
setup->avail_min = muldiv64(setup->avail_min, mul, div);
|
||||
setup->xfer_min = muldiv64(setup->xfer_min, mul, div);
|
||||
setup->xrun_max = muldiv64(setup->xrun_max, mul, div);
|
||||
|
||||
/* FIXME: the three above are not a lot sensible */
|
||||
setup->buffer_size = muldiv64(setup->buffer_size, mul, div);
|
||||
setup->frag_size = muldiv64(setup->frag_size, mul, div);
|
||||
setup->xfer_align = muldiv64(setup->xfer_align, mul, div);
|
||||
|
||||
/* FIXME */
|
||||
setup->boundary = LONG_MAX - LONG_MAX % setup->buffer_size;
|
||||
|
||||
if (rate->states)
|
||||
free(rate->states);
|
||||
rate->states = malloc(setup->format.channels * sizeof(*rate->states));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_rate_init(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_rate_t *rate = pcm->private;
|
||||
unsigned int k;
|
||||
for (k = 0; k < pcm->setup.format.channels; ++k) {
|
||||
rate->states[k].sum = 0;
|
||||
rate->states[k].sample = 0;
|
||||
if (rate->func == resample_expand) {
|
||||
/* Get a sample on entry */
|
||||
rate->states[k].pos = rate->pitch + DIV;
|
||||
} else {
|
||||
rate->states[k].pos = 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_rate_write_areas(snd_pcm_t *pcm,
|
||||
snd_pcm_channel_area_t *areas,
|
||||
size_t client_offset,
|
||||
size_t client_size,
|
||||
size_t *slave_sizep)
|
||||
{
|
||||
snd_pcm_rate_t *rate = pcm->private;
|
||||
snd_pcm_t *slave = rate->plug.slave;
|
||||
size_t client_xfer = 0;
|
||||
size_t slave_xfer = 0;
|
||||
ssize_t err = 0;
|
||||
size_t slave_size;
|
||||
if (slave_sizep)
|
||||
slave_size = *slave_sizep;
|
||||
else
|
||||
slave_size = INT_MAX;
|
||||
assert(client_size > 0 && slave_size > 0);
|
||||
while (client_xfer < client_size &&
|
||||
slave_xfer < slave_size) {
|
||||
size_t src_frames, dst_frames;
|
||||
src_frames = client_size - client_xfer;
|
||||
dst_frames = snd_pcm_mmap_playback_xfer(slave, slave_size - slave_xfer);
|
||||
src_frames = rate->func(areas, client_offset, src_frames,
|
||||
slave->mmap_areas, snd_pcm_mmap_offset(slave),
|
||||
&dst_frames,
|
||||
pcm->setup.format.channels,
|
||||
rate->get_idx, rate->put_idx,
|
||||
rate->pitch, rate->states);
|
||||
err = snd_pcm_mmap_forward(slave, dst_frames);
|
||||
if (err < 0)
|
||||
break;
|
||||
assert((size_t)err == dst_frames);
|
||||
client_offset += src_frames;
|
||||
client_xfer += src_frames;
|
||||
snd_pcm_mmap_hw_forward(pcm, src_frames);
|
||||
slave_xfer += dst_frames;
|
||||
}
|
||||
if (client_xfer > 0 || slave_xfer > 0) {
|
||||
if (slave_sizep)
|
||||
*slave_sizep = slave_xfer;
|
||||
return client_xfer;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_rate_read_areas(snd_pcm_t *pcm,
|
||||
snd_pcm_channel_area_t *areas,
|
||||
size_t client_offset,
|
||||
size_t client_size,
|
||||
size_t *slave_sizep)
|
||||
|
||||
{
|
||||
snd_pcm_rate_t *rate = pcm->private;
|
||||
snd_pcm_t *slave = rate->plug.slave;
|
||||
size_t client_xfer = 0;
|
||||
size_t slave_xfer = 0;
|
||||
ssize_t err = 0;
|
||||
size_t slave_size;
|
||||
if (slave_sizep)
|
||||
slave_size = *slave_sizep;
|
||||
else
|
||||
slave_size = INT_MAX;
|
||||
assert(client_size > 0 && slave_size > 0);
|
||||
while (client_xfer < client_size &&
|
||||
slave_xfer < slave_size) {
|
||||
size_t src_frames, dst_frames;
|
||||
dst_frames = client_size - client_xfer;
|
||||
src_frames = snd_pcm_mmap_capture_xfer(slave, slave_size - slave_xfer);
|
||||
src_frames = rate->func(slave->mmap_areas, snd_pcm_mmap_offset(slave),
|
||||
src_frames,
|
||||
areas, client_offset, &dst_frames,
|
||||
pcm->setup.format.channels,
|
||||
rate->get_idx, rate->put_idx,
|
||||
rate->pitch, rate->states);
|
||||
err = snd_pcm_mmap_forward(slave, src_frames);
|
||||
if (err < 0)
|
||||
break;
|
||||
assert((size_t)err == src_frames);
|
||||
client_offset += dst_frames;
|
||||
client_xfer += dst_frames;
|
||||
snd_pcm_mmap_hw_forward(pcm, dst_frames);
|
||||
slave_xfer += src_frames;
|
||||
}
|
||||
if (client_xfer > 0 || slave_xfer > 0) {
|
||||
if (slave_sizep)
|
||||
*slave_sizep = slave_xfer;
|
||||
return client_xfer;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
size_t snd_pcm_rate_client_frames(snd_pcm_t *pcm, size_t frames)
|
||||
{
|
||||
snd_pcm_rate_t *rate = pcm->private;
|
||||
/* Round toward zero */
|
||||
if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
|
||||
return (int64_t)frames * DIV / rate->pitch;
|
||||
else
|
||||
return (int64_t)frames * rate->pitch / DIV;
|
||||
}
|
||||
|
||||
static void snd_pcm_rate_dump(snd_pcm_t *pcm, FILE *fp)
|
||||
{
|
||||
snd_pcm_rate_t *rate = pcm->private;
|
||||
if (rate->req_sformat < 0)
|
||||
fprintf(fp, "Rate conversion PCM (%d)\n",
|
||||
rate->req_srate);
|
||||
else
|
||||
fprintf(fp, "Rate conversion PCM (%d, sformat=%s)\n",
|
||||
rate->req_srate,
|
||||
snd_pcm_format_name(rate->req_sformat));
|
||||
if (pcm->valid_setup) {
|
||||
fprintf(fp, "Its setup is:\n");
|
||||
snd_pcm_dump_setup(pcm, fp);
|
||||
}
|
||||
fprintf(fp, "Slave: ");
|
||||
snd_pcm_dump(rate->plug.slave, fp);
|
||||
}
|
||||
|
||||
struct snd_pcm_ops snd_pcm_rate_ops = {
|
||||
close: snd_pcm_rate_close,
|
||||
info: snd_pcm_plugin_info,
|
||||
params_info: snd_pcm_rate_params_info,
|
||||
params: snd_pcm_rate_params,
|
||||
setup: snd_pcm_rate_setup,
|
||||
channel_info: snd_pcm_plugin_channel_info,
|
||||
channel_params: snd_pcm_plugin_channel_params,
|
||||
channel_setup: snd_pcm_plugin_channel_setup,
|
||||
dump: snd_pcm_rate_dump,
|
||||
nonblock: snd_pcm_plugin_nonblock,
|
||||
mmap_status: snd_pcm_plugin_mmap_status,
|
||||
mmap_control: snd_pcm_plugin_mmap_control,
|
||||
mmap_data: snd_pcm_plugin_mmap_data,
|
||||
munmap_status: snd_pcm_plugin_munmap_status,
|
||||
munmap_control: snd_pcm_plugin_munmap_control,
|
||||
munmap_data: snd_pcm_plugin_munmap_data,
|
||||
};
|
||||
|
||||
int snd_pcm_rate_open(snd_pcm_t **handlep, int sformat, int srate, snd_pcm_t *slave, int close_slave)
|
||||
{
|
||||
snd_pcm_t *handle;
|
||||
snd_pcm_rate_t *rate;
|
||||
int err;
|
||||
assert(handlep && slave);
|
||||
if (sformat >= 0 && snd_pcm_format_linear(sformat) != 1)
|
||||
return -EINVAL;
|
||||
rate = calloc(1, sizeof(snd_pcm_rate_t));
|
||||
if (!rate) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
rate->req_srate = srate;
|
||||
rate->req_sformat = sformat;
|
||||
rate->plug.read = snd_pcm_rate_read_areas;
|
||||
rate->plug.write = snd_pcm_rate_write_areas;
|
||||
rate->plug.client_frames = snd_pcm_rate_client_frames;
|
||||
rate->plug.init = snd_pcm_rate_init;
|
||||
rate->plug.slave = slave;
|
||||
rate->plug.close_slave = close_slave;
|
||||
|
||||
handle = calloc(1, sizeof(snd_pcm_t));
|
||||
if (!handle) {
|
||||
free(rate);
|
||||
return -ENOMEM;
|
||||
}
|
||||
handle->type = SND_PCM_TYPE_RATE;
|
||||
handle->stream = slave->stream;
|
||||
handle->ops = &snd_pcm_rate_ops;
|
||||
handle->op_arg = handle;
|
||||
handle->fast_ops = &snd_pcm_plugin_fast_ops;
|
||||
handle->fast_op_arg = handle;
|
||||
handle->mode = slave->mode;
|
||||
handle->private = rate;
|
||||
err = snd_pcm_init(handle);
|
||||
if (err < 0) {
|
||||
snd_pcm_close(handle);
|
||||
return err;
|
||||
}
|
||||
*handlep = handle;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _snd_pcm_rate_open(snd_pcm_t **pcmp, char *name,
|
||||
snd_config_t *conf,
|
||||
int stream, int mode)
|
||||
{
|
||||
snd_config_iterator_t i;
|
||||
char *sname = NULL;
|
||||
int err;
|
||||
snd_pcm_t *spcm;
|
||||
int sformat = -1;
|
||||
long srate = -1;
|
||||
snd_config_foreach(i, conf) {
|
||||
snd_config_t *n = snd_config_entry(i);
|
||||
if (strcmp(n->id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "type") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "stream") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "sname") == 0) {
|
||||
err = snd_config_string_get(n, &sname);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(n->id, "sformat") == 0) {
|
||||
char *f;
|
||||
err = snd_config_string_get(n, &f);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
sformat = snd_pcm_format_value(f);
|
||||
if (sformat < 0)
|
||||
return -EINVAL;
|
||||
if (snd_pcm_format_linear(sformat) != 1)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(n->id, "srate") == 0) {
|
||||
err = snd_config_integer_get(n, &srate);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!sname || !srate)
|
||||
return -EINVAL;
|
||||
/* This is needed cause snd_config_update may destroy config */
|
||||
sname = strdup(sname);
|
||||
if (!sname)
|
||||
return -ENOMEM;
|
||||
err = snd_pcm_open(&spcm, sname, stream, mode);
|
||||
free(sname);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_pcm_rate_open(pcmp, sformat, srate, spcm, 1);
|
||||
if (err < 0)
|
||||
snd_pcm_close(spcm);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
963
src/pcm/pcm_route.c
Normal file
963
src/pcm/pcm_route.c
Normal file
|
|
@ -0,0 +1,963 @@
|
|||
/*
|
||||
* PCM - Linear conversion
|
||||
* 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 <byteswap.h>
|
||||
#include <math.h>
|
||||
#include "pcm_local.h"
|
||||
#include "pcm_plugin.h"
|
||||
|
||||
/* The best possible hack to support missing optimization in gcc 2.7.2.3 */
|
||||
#if ROUTE_PLUGIN_RESOLUTION & (ROUTE_PLUGIN_RESOLUTION - 1) != 0
|
||||
#define div(a) a /= ROUTE_PLUGIN_RESOLUTION
|
||||
#elif ROUTE_PLUGIN_RESOLUTION == 16
|
||||
#define div(a) a >>= 4
|
||||
#else
|
||||
#error "Add some code here"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int channel;
|
||||
int as_int;
|
||||
#if ROUTE_PLUGIN_FLOAT
|
||||
float as_float;
|
||||
#endif
|
||||
} ttable_src_t;
|
||||
|
||||
typedef struct ttable_dst ttable_dst_t;
|
||||
|
||||
typedef struct {
|
||||
enum {UINT32=0, UINT64=1, FLOAT=2} sum_idx;
|
||||
int get_idx;
|
||||
int put_idx;
|
||||
int conv_idx;
|
||||
int src_size;
|
||||
int dst_sfmt;
|
||||
size_t ndsts;
|
||||
ttable_dst_t *dsts;
|
||||
} route_params_t;
|
||||
|
||||
|
||||
typedef void (*route_f)(snd_pcm_channel_area_t *src_areas,
|
||||
size_t src_offset,
|
||||
snd_pcm_channel_area_t *dst_area,
|
||||
size_t dst_offset,
|
||||
size_t frames,
|
||||
ttable_dst_t *ttable,
|
||||
route_params_t *params);
|
||||
|
||||
struct ttable_dst {
|
||||
int att; /* Attenuated */
|
||||
unsigned int nsrcs;
|
||||
ttable_src_t* srcs;
|
||||
route_f func;
|
||||
};
|
||||
|
||||
typedef union {
|
||||
u_int32_t as_uint32;
|
||||
u_int64_t as_uint64;
|
||||
#if ROUTE_PLUGIN_FLOAT
|
||||
float as_float;
|
||||
#endif
|
||||
} sum_t;
|
||||
|
||||
typedef struct {
|
||||
/* This field need to be the first */
|
||||
snd_pcm_plugin_t plug;
|
||||
int req_sformat, req_schannels;
|
||||
int sformat;
|
||||
int cformat;
|
||||
int schannels;
|
||||
int cchannels;
|
||||
int cxfer_mode, cmmap_shape;
|
||||
route_params_t params;
|
||||
} snd_pcm_route_t;
|
||||
|
||||
|
||||
static void route1_zero(snd_pcm_channel_area_t *src_areas ATTRIBUTE_UNUSED,
|
||||
size_t src_offset ATTRIBUTE_UNUSED,
|
||||
snd_pcm_channel_area_t *dst_area,
|
||||
size_t dst_offset,
|
||||
size_t frames,
|
||||
ttable_dst_t* ttable ATTRIBUTE_UNUSED,
|
||||
route_params_t *params)
|
||||
{
|
||||
#if 0
|
||||
if (dst_area->wanted)
|
||||
snd_pcm_area_silence(dst_area, dst_offset, frames, params->dst_sfmt);
|
||||
dsts_area->enabled = 0;
|
||||
#else
|
||||
snd_pcm_area_silence(dst_area, dst_offset, frames, params->dst_sfmt);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void route1_one(snd_pcm_channel_area_t *src_areas,
|
||||
size_t src_offset,
|
||||
snd_pcm_channel_area_t *dst_area,
|
||||
size_t dst_offset,
|
||||
size_t frames,
|
||||
ttable_dst_t* ttable,
|
||||
route_params_t *params)
|
||||
{
|
||||
#define CONV_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef CONV_LABELS
|
||||
void *conv;
|
||||
snd_pcm_channel_area_t *src_area = 0;
|
||||
unsigned int srcidx;
|
||||
char *src, *dst;
|
||||
int src_step, dst_step;
|
||||
for (srcidx = 0; srcidx < ttable->nsrcs; ++srcidx) {
|
||||
src_area = &src_areas[ttable->srcs[srcidx].channel];
|
||||
if (src_area->addr != NULL)
|
||||
break;
|
||||
}
|
||||
if (srcidx == ttable->nsrcs) {
|
||||
route1_zero(src_areas, src_offset, dst_area, dst_offset, frames, ttable, params);
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
dst_area->enabled = 1;
|
||||
#endif
|
||||
conv = conv_labels[params->conv_idx];
|
||||
src = snd_pcm_channel_area_addr(src_area, src_offset);
|
||||
dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
|
||||
src_step = snd_pcm_channel_area_step(src_area);
|
||||
dst_step = snd_pcm_channel_area_step(dst_area);
|
||||
while (frames-- > 0) {
|
||||
goto *conv;
|
||||
#define CONV_END after
|
||||
#include "plugin_ops.h"
|
||||
#undef CONV_END
|
||||
after:
|
||||
src += src_step;
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
|
||||
static void route1_many(snd_pcm_channel_area_t *src_areas,
|
||||
size_t src_offset,
|
||||
snd_pcm_channel_area_t *dst_area,
|
||||
size_t dst_offset,
|
||||
size_t frames,
|
||||
ttable_dst_t* ttable,
|
||||
route_params_t *params)
|
||||
{
|
||||
#define GET_U_LABELS
|
||||
#define PUT_U32_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_U_LABELS
|
||||
#undef PUT_U32_LABELS
|
||||
static void *zero_labels[3] = { &&zero_int32, &&zero_int64,
|
||||
#if ROUTE_PLUGIN_FLOAT
|
||||
&&zero_float
|
||||
#endif
|
||||
};
|
||||
/* sum_type att */
|
||||
static void *add_labels[3 * 2] = { &&add_int32_noatt, &&add_int32_att,
|
||||
&&add_int64_noatt, &&add_int64_att,
|
||||
#if ROUTE_PLUGIN_FLOAT
|
||||
&&add_float_noatt, &&add_float_att
|
||||
#endif
|
||||
};
|
||||
/* sum_type att shift */
|
||||
static void *norm_labels[3 * 2 * 4] = { 0,
|
||||
&&norm_int32_8_noatt,
|
||||
&&norm_int32_16_noatt,
|
||||
&&norm_int32_24_noatt,
|
||||
0,
|
||||
&&norm_int32_8_att,
|
||||
&&norm_int32_16_att,
|
||||
&&norm_int32_24_att,
|
||||
&&norm_int64_0_noatt,
|
||||
&&norm_int64_8_noatt,
|
||||
&&norm_int64_16_noatt,
|
||||
&&norm_int64_24_noatt,
|
||||
&&norm_int64_0_att,
|
||||
&&norm_int64_8_att,
|
||||
&&norm_int64_16_att,
|
||||
&&norm_int64_24_att,
|
||||
#if ROUTE_PLUGIN_FLOAT
|
||||
&&norm_float_0,
|
||||
&&norm_float_8,
|
||||
&&norm_float_16,
|
||||
&&norm_float_24,
|
||||
&&norm_float_0,
|
||||
&&norm_float_8,
|
||||
&&norm_float_16,
|
||||
&&norm_float_24,
|
||||
#endif
|
||||
};
|
||||
void *zero, *get, *add, *norm, *put_u32;
|
||||
int nsrcs = ttable->nsrcs;
|
||||
char *dst;
|
||||
int dst_step;
|
||||
char *srcs[nsrcs];
|
||||
int src_steps[nsrcs];
|
||||
ttable_src_t src_tt[nsrcs];
|
||||
u_int32_t sample = 0;
|
||||
int srcidx, srcidx1 = 0;
|
||||
for (srcidx = 0; srcidx < nsrcs; ++srcidx) {
|
||||
snd_pcm_channel_area_t *src_area = &src_areas[ttable->srcs[srcidx].channel];
|
||||
#if 0
|
||||
if (!src_area->enabled)
|
||||
continue;
|
||||
#endif
|
||||
srcs[srcidx1] = snd_pcm_channel_area_addr(src_area, src_offset);
|
||||
src_steps[srcidx1] = snd_pcm_channel_area_step(src_area);
|
||||
src_tt[srcidx1] = ttable->srcs[srcidx];
|
||||
srcidx1++;
|
||||
}
|
||||
nsrcs = srcidx1;
|
||||
if (nsrcs == 0) {
|
||||
route1_zero(src_areas, src_offset, dst_area, dst_offset, frames, ttable, params);
|
||||
return;
|
||||
} else if (nsrcs == 1 && src_tt[0].as_int == ROUTE_PLUGIN_RESOLUTION) {
|
||||
route1_one(src_areas, src_offset, dst_area, dst_offset, frames, ttable, params);
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
dst_area->enabled = 1;
|
||||
#endif
|
||||
zero = zero_labels[params->sum_idx];
|
||||
get = get_u_labels[params->get_idx];
|
||||
add = add_labels[params->sum_idx * 2 + ttable->att];
|
||||
norm = norm_labels[params->sum_idx * 8 + ttable->att * 4 + 4 - params->src_size];
|
||||
put_u32 = put_u32_labels[params->put_idx];
|
||||
dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
|
||||
dst_step = snd_pcm_channel_area_step(dst_area);
|
||||
|
||||
while (frames-- > 0) {
|
||||
ttable_src_t *ttp = src_tt;
|
||||
sum_t sum;
|
||||
|
||||
/* Zero sum */
|
||||
goto *zero;
|
||||
zero_int32:
|
||||
sum.as_uint32 = 0;
|
||||
goto zero_end;
|
||||
zero_int64:
|
||||
sum.as_uint64 = 0;
|
||||
goto zero_end;
|
||||
#if ROUTE_PLUGIN_FLOAT
|
||||
zero_float:
|
||||
sum.as_float = 0.0;
|
||||
goto zero_end;
|
||||
#endif
|
||||
zero_end:
|
||||
for (srcidx = 0; srcidx < nsrcs; ++srcidx) {
|
||||
char *src = srcs[srcidx];
|
||||
|
||||
/* Get sample */
|
||||
goto *get;
|
||||
#define GET_U_END after_get
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_U_END
|
||||
after_get:
|
||||
|
||||
/* Sum */
|
||||
goto *add;
|
||||
add_int32_att:
|
||||
sum.as_uint32 += sample * ttp->as_int;
|
||||
goto after_sum;
|
||||
add_int32_noatt:
|
||||
if (ttp->as_int)
|
||||
sum.as_uint32 += sample;
|
||||
goto after_sum;
|
||||
add_int64_att:
|
||||
sum.as_uint64 += (u_int64_t) sample * ttp->as_int;
|
||||
goto after_sum;
|
||||
add_int64_noatt:
|
||||
if (ttp->as_int)
|
||||
sum.as_uint64 += sample;
|
||||
goto after_sum;
|
||||
#if ROUTE_PLUGIN_FLOAT
|
||||
add_float_att:
|
||||
sum.as_float += sample * ttp->as_float;
|
||||
goto after_sum;
|
||||
add_float_noatt:
|
||||
if (ttp->as_int)
|
||||
sum.as_float += sample;
|
||||
goto after_sum;
|
||||
#endif
|
||||
after_sum:
|
||||
srcs[srcidx] += src_steps[srcidx];
|
||||
ttp++;
|
||||
}
|
||||
|
||||
/* Normalization */
|
||||
goto *norm;
|
||||
norm_int32_8_att:
|
||||
sum.as_uint64 = sum.as_uint32;
|
||||
norm_int64_8_att:
|
||||
sum.as_uint64 <<= 8;
|
||||
norm_int64_0_att:
|
||||
div(sum.as_uint64);
|
||||
goto norm_int;
|
||||
|
||||
norm_int32_16_att:
|
||||
sum.as_uint64 = sum.as_uint32;
|
||||
norm_int64_16_att:
|
||||
sum.as_uint64 <<= 16;
|
||||
div(sum.as_uint64);
|
||||
goto norm_int;
|
||||
|
||||
norm_int32_24_att:
|
||||
sum.as_uint64 = sum.as_uint32;
|
||||
norm_int64_24_att:
|
||||
sum.as_uint64 <<= 24;
|
||||
div(sum.as_uint64);
|
||||
goto norm_int;
|
||||
|
||||
norm_int32_8_noatt:
|
||||
sum.as_uint64 = sum.as_uint32;
|
||||
norm_int64_8_noatt:
|
||||
sum.as_uint64 <<= 8;
|
||||
goto norm_int;
|
||||
|
||||
norm_int32_16_noatt:
|
||||
sum.as_uint64 = sum.as_uint32;
|
||||
norm_int64_16_noatt:
|
||||
sum.as_uint64 <<= 16;
|
||||
goto norm_int;
|
||||
|
||||
norm_int32_24_noatt:
|
||||
sum.as_uint64 = sum.as_uint32;
|
||||
norm_int64_24_noatt:
|
||||
sum.as_uint64 <<= 24;
|
||||
goto norm_int;
|
||||
|
||||
norm_int64_0_noatt:
|
||||
norm_int:
|
||||
if (sum.as_uint64 > (u_int32_t)0xffffffff)
|
||||
sample = (u_int32_t)0xffffffff;
|
||||
else
|
||||
sample = sum.as_uint64;
|
||||
goto after_norm;
|
||||
|
||||
#if ROUTE_PLUGIN_FLOAT
|
||||
norm_float_8:
|
||||
sum.as_float *= 1 << 8;
|
||||
goto norm_float;
|
||||
norm_float_16:
|
||||
sum.as_float *= 1 << 16;
|
||||
goto norm_float;
|
||||
norm_float_24:
|
||||
sum.as_float *= 1 << 24;
|
||||
goto norm_float;
|
||||
norm_float_0:
|
||||
norm_float:
|
||||
sum.as_float = floor(sum.as_float + 0.5);
|
||||
if (sum.as_float > (u_int32_t)0xffffffff)
|
||||
sample = (u_int32_t)0xffffffff;
|
||||
else
|
||||
sample = sum.as_float;
|
||||
goto after_norm;
|
||||
#endif
|
||||
after_norm:
|
||||
|
||||
/* Put sample */
|
||||
goto *put_u32;
|
||||
#define PUT_U32_END after_put_u32
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_U32_END
|
||||
after_put_u32:
|
||||
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
|
||||
static void route_transfer(snd_pcm_channel_area_t *src_areas,
|
||||
size_t src_offset,
|
||||
snd_pcm_channel_area_t *dst_areas,
|
||||
size_t dst_offset,
|
||||
size_t frames,
|
||||
size_t dst_channels,
|
||||
route_params_t *params)
|
||||
{
|
||||
size_t dst_channel;
|
||||
ttable_dst_t *dstp;
|
||||
snd_pcm_channel_area_t *dst_area;
|
||||
|
||||
dstp = params->dsts;
|
||||
dst_area = dst_areas;
|
||||
for (dst_channel = 0; dst_channel < dst_channels; ++dst_channel) {
|
||||
if (dst_channel >= params->ndsts)
|
||||
route1_zero(src_areas, src_offset, dst_area, dst_offset, frames, dstp, params);
|
||||
else
|
||||
dstp->func(src_areas, src_offset, dst_area, dst_offset, frames, dstp, params);
|
||||
dstp++;
|
||||
dst_area++;
|
||||
}
|
||||
}
|
||||
|
||||
static int snd_pcm_route_close(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_route_t *route = pcm->private;
|
||||
route_params_t *params = &route->params;
|
||||
int err = 0;
|
||||
size_t dst_channel;
|
||||
if (route->plug.close_slave)
|
||||
err = snd_pcm_close(route->plug.slave);
|
||||
if (params->dsts) {
|
||||
for (dst_channel = 0; dst_channel < params->ndsts; ++dst_channel) {
|
||||
if (params->dsts[dst_channel].srcs != NULL)
|
||||
free(params->dsts[dst_channel].srcs);
|
||||
}
|
||||
free(params->dsts);
|
||||
}
|
||||
free(route);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_route_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info)
|
||||
{
|
||||
snd_pcm_route_t *route = pcm->private;
|
||||
unsigned int req_mask = info->req_mask;
|
||||
unsigned int sfmt = info->req.format.sfmt;
|
||||
unsigned int channels = info->req.format.channels;
|
||||
int err;
|
||||
if (req_mask & SND_PCM_PARAMS_SFMT &&
|
||||
!snd_pcm_format_linear(sfmt)) {
|
||||
info->req.fail_mask = SND_PCM_PARAMS_SFMT;
|
||||
info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
|
||||
return -EINVAL;
|
||||
}
|
||||
if (route->req_sformat >= 0) {
|
||||
info->req_mask |= SND_PCM_PARAMS_SFMT;
|
||||
info->req.format.sfmt = route->req_sformat;
|
||||
}
|
||||
if (route->req_schannels >= 0) {
|
||||
info->req_mask |= SND_PCM_PARAMS_CHANNELS;
|
||||
info->req.format.channels = route->req_schannels;
|
||||
}
|
||||
info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE |
|
||||
SND_PCM_PARAMS_XFER_MODE);
|
||||
err = snd_pcm_params_info(route->plug.slave, info);
|
||||
info->req_mask = req_mask;
|
||||
info->req.format.sfmt = sfmt;
|
||||
info->req.format.channels = channels;
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (req_mask & SND_PCM_PARAMS_SFMT)
|
||||
info->formats = 1 << sfmt;
|
||||
else
|
||||
info->formats = SND_PCM_LINEAR_FORMATS;
|
||||
if (req_mask & SND_PCM_PARAMS_CHANNELS) {
|
||||
info->min_channels = channels;
|
||||
info->max_channels = channels;
|
||||
} else {
|
||||
info->min_channels = 1;
|
||||
info->max_channels = 1024;
|
||||
}
|
||||
info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
|
||||
info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int snd_pcm_route_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
|
||||
{
|
||||
snd_pcm_route_t *route = pcm->private;
|
||||
snd_pcm_t *slave = route->plug.slave;
|
||||
int err;
|
||||
if (!snd_pcm_format_linear(params->format.sfmt)) {
|
||||
params->fail_mask = SND_PCM_PARAMS_SFMT;
|
||||
params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
|
||||
return -EINVAL;
|
||||
}
|
||||
if (slave->mmap_data) {
|
||||
err = snd_pcm_munmap_data(slave);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
route->cformat = params->format.sfmt;
|
||||
route->cchannels = params->format.channels;
|
||||
route->cxfer_mode = params->xfer_mode;
|
||||
route->cmmap_shape = params->mmap_shape;
|
||||
if (route->req_sformat >= 0)
|
||||
params->format.sfmt = route->req_sformat;
|
||||
if (route->req_schannels >= 0)
|
||||
params->format.channels = route->req_schannels;
|
||||
params->xfer_mode = SND_PCM_XFER_UNSPECIFIED;
|
||||
params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;;
|
||||
err = snd_pcm_params(slave, params);
|
||||
params->format.sfmt = route->cformat;
|
||||
params->format.channels = route->cchannels;
|
||||
params->xfer_mode = route->cxfer_mode;
|
||||
params->mmap_shape = route->cmmap_shape;
|
||||
if (slave->valid_setup) {
|
||||
int r = snd_pcm_mmap_data(slave, NULL);
|
||||
assert(r >= 0);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int snd_pcm_route_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
|
||||
{
|
||||
snd_pcm_route_t *route = pcm->private;
|
||||
int src_format, dst_format;
|
||||
int err = snd_pcm_setup(route->plug.slave, setup);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (route->req_sformat >= 0)
|
||||
assert(route->req_sformat == setup->format.sfmt);
|
||||
route->sformat = setup->format.sfmt;
|
||||
route->schannels = setup->format.channels;
|
||||
if (route->cxfer_mode == SND_PCM_XFER_UNSPECIFIED)
|
||||
setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED;
|
||||
else
|
||||
setup->xfer_mode = route->cxfer_mode;
|
||||
if (route->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED)
|
||||
setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED;
|
||||
else
|
||||
setup->mmap_shape = route->cmmap_shape;
|
||||
setup->format.sfmt = route->cformat;
|
||||
setup->format.channels = route->cchannels;
|
||||
setup->mmap_bytes = 0;
|
||||
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
|
||||
src_format = route->cformat;
|
||||
dst_format = route->sformat;
|
||||
} else {
|
||||
src_format = route->sformat;
|
||||
dst_format = route->cformat;
|
||||
}
|
||||
route->params.get_idx = getput_index(src_format);
|
||||
route->params.put_idx = getput_index(dst_format);
|
||||
route->params.conv_idx = conv_index(src_format, dst_format);
|
||||
route->params.src_size = snd_pcm_format_width(src_format) / 8;
|
||||
route->params.dst_sfmt = dst_format;
|
||||
#if ROUTE_PLUGIN_FLOAT
|
||||
route->params.sum_idx = FLOAT;
|
||||
#else
|
||||
if (src_size == 4)
|
||||
route->params.sum_idx = UINT64;
|
||||
else
|
||||
route->params.sum_idx = UINT32;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_route_write_areas(snd_pcm_t *pcm,
|
||||
snd_pcm_channel_area_t *areas,
|
||||
size_t offset,
|
||||
size_t size,
|
||||
size_t *slave_sizep)
|
||||
{
|
||||
snd_pcm_route_t *route = pcm->private;
|
||||
snd_pcm_t *slave = route->plug.slave;
|
||||
size_t xfer = 0;
|
||||
ssize_t err = 0;
|
||||
if (slave_sizep && *slave_sizep < size)
|
||||
size = *slave_sizep;
|
||||
assert(size > 0);
|
||||
while (xfer < size) {
|
||||
size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer);
|
||||
route_transfer(areas, offset,
|
||||
slave->mmap_areas, snd_pcm_mmap_offset(slave),
|
||||
frames, route->schannels, &route->params);
|
||||
err = snd_pcm_mmap_forward(slave, frames);
|
||||
if (err < 0)
|
||||
break;
|
||||
assert((size_t)err == frames);
|
||||
offset += err;
|
||||
xfer += err;
|
||||
snd_pcm_mmap_hw_forward(pcm, err);
|
||||
}
|
||||
if (xfer > 0) {
|
||||
if (slave_sizep)
|
||||
*slave_sizep = xfer;
|
||||
return xfer;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int snd_pcm_route_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup)
|
||||
{
|
||||
#if 0
|
||||
snd_pcm_plugin_t *plugin = pcm->private;
|
||||
int err;
|
||||
err = snd_pcm_channel_setup(plugin->slave, setup);
|
||||
if (err < 0)
|
||||
return err;
|
||||
#endif
|
||||
if (pcm->setup.mmap_shape == SND_PCM_MMAP_INTERLEAVED) {
|
||||
setup->area.addr = pcm->mmap_data;
|
||||
setup->area.first = setup->channel * pcm->bits_per_sample;
|
||||
setup->area.step = pcm->bits_per_frame;
|
||||
} else {
|
||||
setup->area.addr = pcm->mmap_data + setup->channel * pcm->setup.buffer_size * pcm->bits_per_sample / 8;
|
||||
setup->area.first = 0;
|
||||
setup->area.step = pcm->bits_per_sample;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t snd_pcm_route_read_areas(snd_pcm_t *pcm,
|
||||
snd_pcm_channel_area_t *areas,
|
||||
size_t offset,
|
||||
size_t size,
|
||||
size_t *slave_sizep)
|
||||
{
|
||||
snd_pcm_route_t *route = pcm->private;
|
||||
snd_pcm_t *slave = route->plug.slave;
|
||||
size_t xfer = 0;
|
||||
ssize_t err = 0;
|
||||
if (slave_sizep && *slave_sizep < size)
|
||||
size = *slave_sizep;
|
||||
assert(size > 0);
|
||||
while (xfer < size) {
|
||||
size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer);
|
||||
route_transfer(slave->mmap_areas, snd_pcm_mmap_offset(slave),
|
||||
areas, offset,
|
||||
frames, route->cchannels, &route->params);
|
||||
err = snd_pcm_mmap_forward(slave, frames);
|
||||
if (err < 0)
|
||||
break;
|
||||
assert((size_t)err == frames);
|
||||
offset += err;
|
||||
xfer += err;
|
||||
snd_pcm_mmap_hw_forward(pcm, err);
|
||||
}
|
||||
if (xfer > 0) {
|
||||
if (slave_sizep)
|
||||
*slave_sizep = xfer;
|
||||
return xfer;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void snd_pcm_route_dump(snd_pcm_t *pcm, FILE *fp)
|
||||
{
|
||||
snd_pcm_route_t *route = pcm->private;
|
||||
unsigned int dst;
|
||||
if (route->req_sformat < 0)
|
||||
fprintf(fp, "Route conversion PCM\n");
|
||||
else
|
||||
fprintf(fp, "Route conversion PCM (sformat=%s)\n",
|
||||
snd_pcm_format_name(route->req_sformat));
|
||||
fputs("Transformation table:\n", fp);
|
||||
for (dst = 0; dst < route->params.ndsts; dst++) {
|
||||
ttable_dst_t *d = &route->params.dsts[dst];
|
||||
unsigned int src;
|
||||
if (d->nsrcs == 0)
|
||||
continue;
|
||||
fprintf(fp, "%d <- ", dst);
|
||||
src = 0;
|
||||
while (1) {
|
||||
ttable_src_t *s = &d->srcs[src];
|
||||
if (d->att)
|
||||
fprintf(fp, "%d*%g", s->channel, s->as_float);
|
||||
else
|
||||
fprintf(fp, "%d", s->channel);
|
||||
src++;
|
||||
if (src == d->nsrcs)
|
||||
break;
|
||||
fputs(" + ", fp);
|
||||
}
|
||||
putc('\n', fp);
|
||||
}
|
||||
if (pcm->valid_setup) {
|
||||
fprintf(fp, "Its setup is:\n");
|
||||
snd_pcm_dump_setup(pcm, fp);
|
||||
}
|
||||
fprintf(fp, "Slave: ");
|
||||
snd_pcm_dump(route->plug.slave, fp);
|
||||
}
|
||||
|
||||
struct snd_pcm_ops snd_pcm_route_ops = {
|
||||
close: snd_pcm_route_close,
|
||||
info: snd_pcm_plugin_info,
|
||||
params_info: snd_pcm_route_params_info,
|
||||
params: snd_pcm_route_params,
|
||||
setup: snd_pcm_route_setup,
|
||||
channel_info: snd_pcm_plugin_channel_info,
|
||||
channel_params: snd_pcm_plugin_channel_params,
|
||||
channel_setup: snd_pcm_route_channel_setup,
|
||||
dump: snd_pcm_route_dump,
|
||||
nonblock: snd_pcm_plugin_nonblock,
|
||||
mmap_status: snd_pcm_plugin_mmap_status,
|
||||
mmap_control: snd_pcm_plugin_mmap_control,
|
||||
mmap_data: snd_pcm_plugin_mmap_data,
|
||||
munmap_status: snd_pcm_plugin_munmap_status,
|
||||
munmap_control: snd_pcm_plugin_munmap_control,
|
||||
munmap_data: snd_pcm_plugin_munmap_data,
|
||||
};
|
||||
|
||||
int route_load_ttable(route_params_t *params, int stream,
|
||||
unsigned int tt_ssize,
|
||||
ttable_entry_t *ttable,
|
||||
unsigned int tt_cused, unsigned int tt_sused)
|
||||
{
|
||||
unsigned int src_channel, dst_channel;
|
||||
ttable_dst_t *dptr;
|
||||
unsigned int sused, dused, smul, dmul;
|
||||
if (stream == SND_PCM_STREAM_PLAYBACK) {
|
||||
sused = tt_cused;
|
||||
dused = tt_sused;
|
||||
smul = tt_ssize;
|
||||
dmul = 1;
|
||||
} else {
|
||||
sused = tt_sused;
|
||||
dused = tt_cused;
|
||||
smul = 1;
|
||||
dmul = tt_ssize;
|
||||
}
|
||||
params->ndsts = dused;
|
||||
dptr = calloc(dused, sizeof(*params->dsts));
|
||||
if (!dptr)
|
||||
return -ENOMEM;
|
||||
params->dsts = dptr;
|
||||
for (dst_channel = 0; dst_channel < dused; ++dst_channel) {
|
||||
ttable_entry_t t = 0;
|
||||
int att = 0;
|
||||
int nsrcs = 0;
|
||||
ttable_src_t srcs[sused];
|
||||
for (src_channel = 0; src_channel < sused; ++src_channel) {
|
||||
ttable_entry_t v;
|
||||
v = ttable[src_channel * smul + dst_channel * dmul];
|
||||
assert(v >= 0 && v <= FULL);
|
||||
if (v != 0) {
|
||||
srcs[nsrcs].channel = src_channel;
|
||||
#if ROUTE_PLUGIN_FLOAT
|
||||
/* Also in user space for non attenuated */
|
||||
srcs[nsrcs].as_int = (v == FULL ? ROUTE_PLUGIN_RESOLUTION : 0);
|
||||
srcs[nsrcs].as_float = v;
|
||||
#else
|
||||
srcs[nsrcs].as_int = v;
|
||||
#endif
|
||||
if (v != FULL)
|
||||
att = 1;
|
||||
t += v;
|
||||
nsrcs++;
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
assert(t <= FULL);
|
||||
#endif
|
||||
dptr->att = att;
|
||||
dptr->nsrcs = nsrcs;
|
||||
if (nsrcs == 0)
|
||||
dptr->func = route1_zero;
|
||||
else if (nsrcs == 1 && !att)
|
||||
dptr->func = route1_one;
|
||||
else
|
||||
dptr->func = route1_many;
|
||||
if (nsrcs > 0) {
|
||||
dptr->srcs = calloc(nsrcs, sizeof(*srcs));
|
||||
if (!dptr->srcs)
|
||||
return -ENOMEM;
|
||||
memcpy(dptr->srcs, srcs, sizeof(*srcs) * nsrcs);
|
||||
} else
|
||||
dptr->srcs = 0;
|
||||
dptr++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int snd_pcm_route_open(snd_pcm_t **handlep,
|
||||
int sformat, unsigned int schannels,
|
||||
ttable_entry_t *ttable,
|
||||
unsigned int tt_ssize,
|
||||
unsigned int tt_cused, unsigned int tt_sused,
|
||||
snd_pcm_t *slave, int close_slave)
|
||||
{
|
||||
snd_pcm_t *handle;
|
||||
snd_pcm_route_t *route;
|
||||
int err;
|
||||
assert(handlep && slave && ttable);
|
||||
if (sformat >= 0 && snd_pcm_format_linear(sformat) != 1)
|
||||
return -EINVAL;
|
||||
route = calloc(1, sizeof(snd_pcm_route_t));
|
||||
if (!route) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
route->req_sformat = sformat;
|
||||
route->req_schannels = schannels;
|
||||
route->plug.read = snd_pcm_route_read_areas;
|
||||
route->plug.write = snd_pcm_route_write_areas;
|
||||
route->plug.slave = slave;
|
||||
route->plug.close_slave = close_slave;
|
||||
|
||||
handle = calloc(1, sizeof(snd_pcm_t));
|
||||
if (!handle) {
|
||||
free(route);
|
||||
return -ENOMEM;
|
||||
}
|
||||
handle->type = SND_PCM_TYPE_ROUTE;
|
||||
handle->stream = slave->stream;
|
||||
handle->ops = &snd_pcm_route_ops;
|
||||
handle->op_arg = handle;
|
||||
handle->fast_ops = &snd_pcm_plugin_fast_ops;
|
||||
handle->fast_op_arg = handle;
|
||||
handle->mode = slave->mode;
|
||||
handle->private = route;
|
||||
err = snd_pcm_init(handle);
|
||||
if (err < 0) {
|
||||
snd_pcm_close(handle);
|
||||
return err;
|
||||
}
|
||||
err = route_load_ttable(&route->params, handle->stream, tt_ssize, ttable, tt_cused, tt_sused);
|
||||
if (err < 0) {
|
||||
snd_pcm_close(handle);
|
||||
return err;
|
||||
}
|
||||
*handlep = handle;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_route_load_ttable(snd_config_t *tt, ttable_entry_t *ttable,
|
||||
unsigned int tt_csize, unsigned int tt_ssize,
|
||||
unsigned int *tt_cused, unsigned int *tt_sused,
|
||||
int schannels)
|
||||
{
|
||||
int cused = -1;
|
||||
int sused = -1;
|
||||
snd_config_iterator_t i;
|
||||
unsigned int k;
|
||||
for (k = 0; k < tt_csize * tt_ssize; ++k)
|
||||
ttable[k] = 0.0;
|
||||
snd_config_foreach(i, tt) {
|
||||
snd_config_t *in = snd_config_entry(i);
|
||||
snd_config_iterator_t j;
|
||||
char *p;
|
||||
long cchannel;
|
||||
errno = 0;
|
||||
cchannel = strtol(in->id, &p, 10);
|
||||
if (errno || *p ||
|
||||
cchannel < 0 || (unsigned int) cchannel > tt_csize)
|
||||
return -EINVAL;
|
||||
if (snd_config_type(in) != SND_CONFIG_TYPE_COMPOUND)
|
||||
return -EINVAL;
|
||||
snd_config_foreach(j, in) {
|
||||
snd_config_t *jn = snd_config_entry(j);
|
||||
double value;
|
||||
long schannel;
|
||||
int err;
|
||||
errno = 0;
|
||||
schannel = strtol(jn->id, &p, 10);
|
||||
if (errno || *p ||
|
||||
schannel < 0 || (unsigned int) schannel > tt_ssize ||
|
||||
(schannels > 0 && schannel >= schannels))
|
||||
return -EINVAL;
|
||||
err = snd_config_real_get(jn, &value);
|
||||
if (err < 0) {
|
||||
long v;
|
||||
err = snd_config_integer_get(jn, &v);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
value = v;
|
||||
}
|
||||
ttable[cchannel * tt_ssize + schannel] = value;
|
||||
if (schannel > sused)
|
||||
sused = schannel;
|
||||
}
|
||||
if (cchannel > cused)
|
||||
cused = cchannel;
|
||||
}
|
||||
*tt_sused = sused + 1;
|
||||
*tt_cused = cused + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MAX_CHANNELS 32
|
||||
|
||||
int _snd_pcm_route_open(snd_pcm_t **pcmp, char *name,
|
||||
snd_config_t *conf,
|
||||
int stream, int mode)
|
||||
{
|
||||
snd_config_iterator_t i;
|
||||
char *sname = NULL;
|
||||
int err;
|
||||
snd_pcm_t *spcm;
|
||||
int sformat = -1;
|
||||
long schannels = -1;
|
||||
snd_config_t *tt = NULL;
|
||||
ttable_entry_t ttable[MAX_CHANNELS*MAX_CHANNELS];
|
||||
unsigned int cused, sused;
|
||||
snd_config_foreach(i, conf) {
|
||||
snd_config_t *n = snd_config_entry(i);
|
||||
if (strcmp(n->id, "comment") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "type") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "stream") == 0)
|
||||
continue;
|
||||
if (strcmp(n->id, "sname") == 0) {
|
||||
err = snd_config_string_get(n, &sname);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(n->id, "sformat") == 0) {
|
||||
char *f;
|
||||
err = snd_config_string_get(n, &f);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
sformat = snd_pcm_format_value(f);
|
||||
if (sformat < 0)
|
||||
return -EINVAL;
|
||||
if (snd_pcm_format_linear(sformat) != 1)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(n->id, "schannels") == 0) {
|
||||
err = snd_config_integer_get(n, &schannels);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(n->id, "ttable") == 0) {
|
||||
if (snd_config_type(n) != SND_CONFIG_TYPE_COMPOUND)
|
||||
return -EINVAL;
|
||||
tt = n;
|
||||
continue;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!sname || !tt)
|
||||
return -EINVAL;
|
||||
|
||||
err = snd_pcm_route_load_ttable(tt, ttable, MAX_CHANNELS, MAX_CHANNELS,
|
||||
&cused, &sused, schannels);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* This is needed cause snd_config_update may destroy config */
|
||||
sname = strdup(sname);
|
||||
if (!sname)
|
||||
return -ENOMEM;
|
||||
err = snd_pcm_open(&spcm, sname, stream, mode);
|
||||
free(sname);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_pcm_route_open(pcmp, sformat, schannels,
|
||||
ttable, MAX_CHANNELS,
|
||||
cused, sused,
|
||||
spcm, 1);
|
||||
if (err < 0)
|
||||
snd_pcm_close(spcm);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
EXTRA_LTLIBRARIES = libpcmplugin.la
|
||||
|
||||
libpcmplugin_la_SOURCES = io.c mmap.c copy.c linear.c \
|
||||
mulaw.c alaw.c adpcm.c rate.c route.c
|
||||
all: libpcmplugin.la
|
||||
|
||||
|
||||
INCLUDES=-I$(top_srcdir)/include
|
||||
|
|
@ -1,417 +0,0 @@
|
|||
/*
|
||||
* Ima-ADPCM conversion Plug-In Interface
|
||||
* Copyright (c) 1999 by Uros Bizjak <uros@kss-loka.si>
|
||||
* Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
* Based on Version 1.2, 18-Dec-92 implementation of Intel/DVI ADPCM code
|
||||
* by Jack Jansen, CWI, Amsterdam <Jack.Jansen@cwi.nl>, Copyright 1992
|
||||
* by Stichting Mathematisch Centrum, Amsterdam, The Netherlands.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
These routines convert 16 bit linear PCM samples to 4 bit ADPCM code
|
||||
and vice versa. The ADPCM code used is the Intel/DVI ADPCM code which
|
||||
is being recommended by the IMA Digital Audio Technical Working Group.
|
||||
|
||||
The algorithm for this coder was taken from:
|
||||
Proposal for Standardized Audio Interstreamge Formats,
|
||||
IMA compatability project proceedings, Vol 2, Issue 2, May 1992.
|
||||
|
||||
- No, this is *not* a G.721 coder/decoder. The algorithm used by G.721
|
||||
is very complicated, requiring oodles of floating-point ops per
|
||||
sample (resulting in very poor performance). I have not done any
|
||||
tests myself but various people have assured my that 721 quality is
|
||||
actually lower than DVI quality.
|
||||
|
||||
- No, it probably isn't a RIFF ADPCM decoder either. Trying to decode
|
||||
RIFF ADPCM with these routines seems to result in something
|
||||
recognizable but very distorted.
|
||||
|
||||
- No, it is not a CDROM-XA coder either, as far as I know. I haven't
|
||||
come across a good description of XA yet.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <byteswap.h>
|
||||
#include "../pcm_local.h"
|
||||
|
||||
/* First table lookup for Ima-ADPCM quantizer */
|
||||
static char IndexAdjust[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };
|
||||
|
||||
/* Second table lookup for Ima-ADPCM quantizer */
|
||||
static short StepSize[89] = {
|
||||
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
|
||||
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
|
||||
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
|
||||
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
|
||||
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
|
||||
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
|
||||
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
|
||||
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
|
||||
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int pred_val; /* Calculated predicted value */
|
||||
int step_idx; /* Previous StepSize lookup index */
|
||||
} adpcm_channel_t;
|
||||
|
||||
typedef void (*adpcm_f)(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
size_t frames);
|
||||
|
||||
typedef struct adpcm_private_data {
|
||||
adpcm_f func;
|
||||
int conv;
|
||||
adpcm_channel_t channels[0];
|
||||
} adpcm_t;
|
||||
|
||||
|
||||
static void adpcm_init(snd_pcm_plugin_t *plugin)
|
||||
{
|
||||
unsigned int channel;
|
||||
adpcm_t *data = (adpcm_t *)plugin->extra_data;
|
||||
for (channel = 0; channel < plugin->src_format.channels; channel++) {
|
||||
adpcm_channel_t *v = &data->channels[channel];
|
||||
v->pred_val = 0;
|
||||
v->step_idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static char adpcm_encoder(int sl, adpcm_channel_t * state)
|
||||
{
|
||||
short diff; /* Difference between sl and predicted sample */
|
||||
short pred_diff; /* Predicted difference to next sample */
|
||||
|
||||
unsigned char sign; /* sign of diff */
|
||||
short step; /* holds previous StepSize value */
|
||||
unsigned char adjust_idx; /* Index to IndexAdjust lookup table */
|
||||
|
||||
int i;
|
||||
|
||||
/* Compute difference to previous predicted value */
|
||||
diff = sl - state->pred_val;
|
||||
sign = (diff < 0) ? 0x8 : 0x0;
|
||||
if (sign) {
|
||||
diff = -diff;
|
||||
}
|
||||
|
||||
/*
|
||||
* This code *approximately* computes:
|
||||
* adjust_idx = diff * 4 / step;
|
||||
* pred_diff = (adjust_idx + 0.5) * step / 4;
|
||||
*
|
||||
* But in shift step bits are dropped. The net result of this is
|
||||
* that even if you have fast mul/div hardware you cannot put it to
|
||||
* good use since the fixup would be too expensive.
|
||||
*/
|
||||
|
||||
step = StepSize[state->step_idx];
|
||||
|
||||
/* Divide and clamp */
|
||||
pred_diff = step >> 3;
|
||||
for (adjust_idx = 0, i = 0x4; i; i >>= 1, step >>= 1) {
|
||||
if (diff >= step) {
|
||||
adjust_idx |= i;
|
||||
diff -= step;
|
||||
pred_diff += step;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update and clamp previous predicted value */
|
||||
state->pred_val += sign ? -pred_diff : pred_diff;
|
||||
|
||||
if (state->pred_val > 32767) {
|
||||
state->pred_val = 32767;
|
||||
} else if (state->pred_val < -32768) {
|
||||
state->pred_val = -32768;
|
||||
}
|
||||
|
||||
/* Update and clamp StepSize lookup table index */
|
||||
state->step_idx += IndexAdjust[adjust_idx];
|
||||
|
||||
if (state->step_idx < 0) {
|
||||
state->step_idx = 0;
|
||||
} else if (state->step_idx > 88) {
|
||||
state->step_idx = 88;
|
||||
}
|
||||
return (sign | adjust_idx);
|
||||
}
|
||||
|
||||
|
||||
static int adpcm_decoder(unsigned char code, adpcm_channel_t * state)
|
||||
{
|
||||
short pred_diff; /* Predicted difference to next sample */
|
||||
short step; /* holds previous StepSize value */
|
||||
char sign;
|
||||
|
||||
int i;
|
||||
|
||||
/* Separate sign and magnitude */
|
||||
sign = code & 0x8;
|
||||
code &= 0x7;
|
||||
|
||||
/*
|
||||
* Computes pred_diff = (code + 0.5) * step / 4,
|
||||
* but see comment in adpcm_coder.
|
||||
*/
|
||||
|
||||
step = StepSize[state->step_idx];
|
||||
|
||||
/* Compute difference and new predicted value */
|
||||
pred_diff = step >> 3;
|
||||
for (i = 0x4; i; i >>= 1, step >>= 1) {
|
||||
if (code & i) {
|
||||
pred_diff += step;
|
||||
}
|
||||
}
|
||||
state->pred_val += (sign) ? -pred_diff : pred_diff;
|
||||
|
||||
/* Clamp output value */
|
||||
if (state->pred_val > 32767) {
|
||||
state->pred_val = 32767;
|
||||
} else if (state->pred_val < -32768) {
|
||||
state->pred_val = -32768;
|
||||
}
|
||||
|
||||
/* Find new StepSize index value */
|
||||
state->step_idx += IndexAdjust[code];
|
||||
|
||||
if (state->step_idx < 0) {
|
||||
state->step_idx = 0;
|
||||
} else if (state->step_idx > 88) {
|
||||
state->step_idx = 88;
|
||||
}
|
||||
return (state->pred_val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic Ima-ADPCM plugin
|
||||
*/
|
||||
|
||||
static void adpcm_decode(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
size_t frames)
|
||||
{
|
||||
#define PUT_S16_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_S16_LABELS
|
||||
adpcm_t *data = (adpcm_t *)plugin->extra_data;
|
||||
void *put = put_s16_labels[data->conv];
|
||||
int channel;
|
||||
int nchannels = plugin->src_format.channels;
|
||||
for (channel = 0; channel < nchannels; ++channel) {
|
||||
char *src;
|
||||
int srcbit;
|
||||
char *dst;
|
||||
int src_step, srcbit_step, dst_step;
|
||||
size_t frames1;
|
||||
adpcm_channel_t *state;
|
||||
if (!src_channels[channel].enabled) {
|
||||
if (dst_channels[channel].wanted)
|
||||
snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
|
||||
dst_channels[channel].enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_channels[channel].enabled = 1;
|
||||
src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
|
||||
srcbit = src_channels[channel].area.first % 8;
|
||||
dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
|
||||
src_step = src_channels[channel].area.step / 8;
|
||||
srcbit_step = src_channels[channel].area.step % 8;
|
||||
dst_step = dst_channels[channel].area.step / 8;
|
||||
state = &data->channels[channel];
|
||||
frames1 = frames;
|
||||
while (frames1-- > 0) {
|
||||
signed short sample;
|
||||
int v;
|
||||
if (srcbit)
|
||||
v = *src & 0x0f;
|
||||
else
|
||||
v = (*src >> 4) & 0x0f;
|
||||
sample = adpcm_decoder(v, state);
|
||||
goto *put;
|
||||
#define PUT_S16_END after
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_S16_END
|
||||
after:
|
||||
src += src_step;
|
||||
srcbit += srcbit_step;
|
||||
if (srcbit == 8) {
|
||||
src++;
|
||||
srcbit = 0;
|
||||
}
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void adpcm_encode(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
size_t frames)
|
||||
{
|
||||
#define GET_S16_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_LABELS
|
||||
adpcm_t *data = (adpcm_t *)plugin->extra_data;
|
||||
void *get = get_s16_labels[data->conv];
|
||||
int channel;
|
||||
int nchannels = plugin->src_format.channels;
|
||||
signed short sample = 0;
|
||||
for (channel = 0; channel < nchannels; ++channel) {
|
||||
char *src;
|
||||
char *dst;
|
||||
int dstbit;
|
||||
int src_step, dst_step, dstbit_step;
|
||||
size_t frames1;
|
||||
adpcm_channel_t *state;
|
||||
if (!src_channels[channel].enabled) {
|
||||
if (dst_channels[channel].wanted)
|
||||
snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
|
||||
dst_channels[channel].enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_channels[channel].enabled = 1;
|
||||
src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
|
||||
dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
|
||||
dstbit = dst_channels[channel].area.first % 8;
|
||||
src_step = src_channels[channel].area.step / 8;
|
||||
dst_step = dst_channels[channel].area.step / 8;
|
||||
dstbit_step = dst_channels[channel].area.step % 8;
|
||||
state = &data->channels[channel];
|
||||
frames1 = frames;
|
||||
while (frames1-- > 0) {
|
||||
int v;
|
||||
goto *get;
|
||||
#define GET_S16_END after
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_END
|
||||
after:
|
||||
v = adpcm_encoder(sample, state);
|
||||
if (dstbit)
|
||||
*dst = (*dst & 0xf0) | v;
|
||||
else
|
||||
*dst = (*dst & 0x0f) | (v << 4);
|
||||
src += src_step;
|
||||
dst += dst_step;
|
||||
dstbit += dstbit_step;
|
||||
if (dstbit == 8) {
|
||||
dst++;
|
||||
dstbit = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t adpcm_transfer(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
size_t frames)
|
||||
{
|
||||
adpcm_t *data;
|
||||
unsigned int channel;
|
||||
|
||||
assert(plugin && src_channels && dst_channels);
|
||||
if (frames == 0)
|
||||
return 0;
|
||||
for (channel = 0; channel < plugin->src_format.channels; channel++) {
|
||||
if (plugin->src_format.format == SND_PCM_SFMT_IMA_ADPCM) {
|
||||
assert(src_channels[channel].area.first % 4 == 0 &&
|
||||
src_channels[channel].area.step % 4 == 0 &&
|
||||
dst_channels[channel].area.first % 8 == 0 &&
|
||||
dst_channels[channel].area.step % 8 == 0);
|
||||
} else {
|
||||
assert(src_channels[channel].area.first % 8 == 0 &&
|
||||
src_channels[channel].area.step % 8 == 0 &&
|
||||
dst_channels[channel].area.first % 4 == 0 &&
|
||||
dst_channels[channel].area.step % 4 == 0);
|
||||
}
|
||||
}
|
||||
data = (adpcm_t *)plugin->extra_data;
|
||||
data->func(plugin, src_channels, dst_channels, frames);
|
||||
return frames;
|
||||
}
|
||||
|
||||
static int adpcm_action(snd_pcm_plugin_t * plugin,
|
||||
snd_pcm_plugin_action_t action,
|
||||
unsigned long udata ATTRIBUTE_UNUSED)
|
||||
{
|
||||
assert(plugin);
|
||||
switch (action) {
|
||||
case INIT:
|
||||
case PREPARE:
|
||||
case DRAIN:
|
||||
case FLUSH:
|
||||
adpcm_init(plugin);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0; /* silenty ignore other actions */
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_build_adpcm(snd_pcm_plug_t *plug,
|
||||
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;
|
||||
adpcm_f func;
|
||||
|
||||
assert(r_plugin);
|
||||
*r_plugin = NULL;
|
||||
|
||||
assert(src_format->rate == dst_format->rate);
|
||||
assert(src_format->channels == dst_format->channels);
|
||||
|
||||
if (dst_format->format == SND_PCM_SFMT_IMA_ADPCM) {
|
||||
format = src_format;
|
||||
func = adpcm_encode;
|
||||
}
|
||||
else if (src_format->format == SND_PCM_SFMT_IMA_ADPCM) {
|
||||
format = dst_format;
|
||||
func = adpcm_decode;
|
||||
}
|
||||
else
|
||||
assert(0);
|
||||
assert(snd_pcm_format_linear(format->format));
|
||||
|
||||
err = snd_pcm_plugin_build(plug, "Ima-ADPCM<->linear conversion",
|
||||
src_format, dst_format,
|
||||
sizeof(adpcm_t) + src_format->channels * sizeof(adpcm_channel_t),
|
||||
&plugin);
|
||||
if (err < 0)
|
||||
return err;
|
||||
data = (adpcm_t *)plugin->extra_data;
|
||||
data->func = func;
|
||||
data->conv = getput_index(format->format);
|
||||
plugin->transfer = adpcm_transfer;
|
||||
plugin->action = adpcm_action;
|
||||
*r_plugin = plugin;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,291 +0,0 @@
|
|||
/*
|
||||
* A-Law conversion Plug-In Interface
|
||||
* Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
|
||||
* Uros Bizjak <uros@kss-loka.si>
|
||||
*
|
||||
* Based on reference implementation by Sun Microsystems, Inc.
|
||||
*
|
||||
* 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 <byteswap.h>
|
||||
#include <sys/uio.h>
|
||||
#include "../pcm_local.h"
|
||||
|
||||
#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
|
||||
#define QUANT_MASK (0xf) /* Quantization field mask. */
|
||||
#define NSEGS (8) /* Number of A-law segments. */
|
||||
#define SEG_SHIFT (4) /* Left shift for segment number. */
|
||||
#define SEG_MASK (0x70) /* Segment field mask. */
|
||||
|
||||
static short alaw_seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF,
|
||||
0xFFF, 0x1FFF, 0x3FFF, 0x7FFF};
|
||||
|
||||
static inline int search(int val, short *table, int size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (val <= *table++)
|
||||
return (i);
|
||||
}
|
||||
return (size);
|
||||
}
|
||||
|
||||
/*
|
||||
* linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
|
||||
*
|
||||
* linear2alaw() accepts an 16-bit integer and encodes it as A-law data.
|
||||
*
|
||||
* Linear Input Code Compressed Code
|
||||
* ------------------------ ---------------
|
||||
* 0000000wxyza 000wxyz
|
||||
* 0000001wxyza 001wxyz
|
||||
* 000001wxyzab 010wxyz
|
||||
* 00001wxyzabc 011wxyz
|
||||
* 0001wxyzabcd 100wxyz
|
||||
* 001wxyzabcde 101wxyz
|
||||
* 01wxyzabcdef 110wxyz
|
||||
* 1wxyzabcdefg 111wxyz
|
||||
*
|
||||
* For further information see John C. Bellamy's Digital Telephony, 1982,
|
||||
* John Wiley & Sons, pps 98-111 and 472-476.
|
||||
*/
|
||||
static unsigned char linear2alaw(int pcm_val) /* 2's complement (16-bit range) */
|
||||
{
|
||||
int mask;
|
||||
int seg;
|
||||
unsigned char aval;
|
||||
|
||||
if (pcm_val >= 0) {
|
||||
mask = 0xD5; /* sign (7th) bit = 1 */
|
||||
} else {
|
||||
mask = 0x55; /* sign bit = 0 */
|
||||
pcm_val = -pcm_val - 8;
|
||||
}
|
||||
|
||||
/* Convert the scaled magnitude to segment number. */
|
||||
seg = search(pcm_val, alaw_seg_end, NSEGS);
|
||||
|
||||
/* Combine the sign, segment, and quantization bits. */
|
||||
|
||||
if (seg >= 8) /* out of range, return maximum value. */
|
||||
return (0x7F ^ mask);
|
||||
else {
|
||||
aval = seg << SEG_SHIFT;
|
||||
if (seg < 2)
|
||||
aval |= (pcm_val >> 4) & QUANT_MASK;
|
||||
else
|
||||
aval |= (pcm_val >> (seg + 3)) & QUANT_MASK;
|
||||
return (aval ^ mask);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* alaw2linear() - Convert an A-law value to 16-bit linear PCM
|
||||
*
|
||||
*/
|
||||
static int alaw2linear(unsigned char a_val)
|
||||
{
|
||||
int t;
|
||||
int seg;
|
||||
|
||||
a_val ^= 0x55;
|
||||
|
||||
t = (a_val & QUANT_MASK) << 4;
|
||||
seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
|
||||
switch (seg) {
|
||||
case 0:
|
||||
t += 8;
|
||||
break;
|
||||
case 1:
|
||||
t += 0x108;
|
||||
break;
|
||||
default:
|
||||
t += 0x108;
|
||||
t <<= seg - 1;
|
||||
}
|
||||
return ((a_val & SIGN_BIT) ? t : -t);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Basic A-Law plugin
|
||||
*/
|
||||
|
||||
typedef void (*alaw_f)(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
size_t frames);
|
||||
|
||||
typedef struct alaw_private_data {
|
||||
alaw_f func;
|
||||
int conv;
|
||||
} alaw_t;
|
||||
|
||||
static void alaw_decode(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
size_t frames)
|
||||
{
|
||||
#define PUT_S16_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_S16_LABELS
|
||||
alaw_t *data = (alaw_t *)plugin->extra_data;
|
||||
void *put = put_s16_labels[data->conv];
|
||||
int channel;
|
||||
int nchannels = plugin->src_format.channels;
|
||||
for (channel = 0; channel < nchannels; ++channel) {
|
||||
char *src;
|
||||
char *dst;
|
||||
int src_step, dst_step;
|
||||
size_t frames1;
|
||||
if (!src_channels[channel].enabled) {
|
||||
if (dst_channels[channel].wanted)
|
||||
snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
|
||||
dst_channels[channel].enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_channels[channel].enabled = 1;
|
||||
src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
|
||||
dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
|
||||
src_step = src_channels[channel].area.step / 8;
|
||||
dst_step = dst_channels[channel].area.step / 8;
|
||||
frames1 = frames;
|
||||
while (frames1-- > 0) {
|
||||
signed short sample = alaw2linear(*src);
|
||||
goto *put;
|
||||
#define PUT_S16_END after
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_S16_END
|
||||
after:
|
||||
src += src_step;
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void alaw_encode(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
size_t frames)
|
||||
{
|
||||
#define GET_S16_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_LABELS
|
||||
alaw_t *data = (alaw_t *)plugin->extra_data;
|
||||
void *get = get_s16_labels[data->conv];
|
||||
int channel;
|
||||
int nchannels = plugin->src_format.channels;
|
||||
signed short sample = 0;
|
||||
for (channel = 0; channel < nchannels; ++channel) {
|
||||
char *src;
|
||||
char *dst;
|
||||
int src_step, dst_step;
|
||||
size_t frames1;
|
||||
if (!src_channels[channel].enabled) {
|
||||
if (dst_channels[channel].wanted)
|
||||
snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
|
||||
dst_channels[channel].enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_channels[channel].enabled = 1;
|
||||
src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
|
||||
dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
|
||||
src_step = src_channels[channel].area.step / 8;
|
||||
dst_step = dst_channels[channel].area.step / 8;
|
||||
frames1 = frames;
|
||||
while (frames1-- > 0) {
|
||||
goto *get;
|
||||
#define GET_S16_END after
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_END
|
||||
after:
|
||||
*dst = linear2alaw(sample);
|
||||
src += src_step;
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t alaw_transfer(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
size_t frames)
|
||||
{
|
||||
alaw_t *data;
|
||||
unsigned int channel;
|
||||
|
||||
assert(plugin && src_channels && dst_channels);
|
||||
if (frames == 0)
|
||||
return 0;
|
||||
for (channel = 0; channel < plugin->src_format.channels; channel++) {
|
||||
assert(src_channels[channel].area.first % 8 == 0 &&
|
||||
src_channels[channel].area.step % 8 == 0);
|
||||
assert(dst_channels[channel].area.first % 8 == 0 &&
|
||||
dst_channels[channel].area.step % 8 == 0);
|
||||
}
|
||||
data = (alaw_t *)plugin->extra_data;
|
||||
data->func(plugin, src_channels, dst_channels, frames);
|
||||
return frames;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_build_alaw(snd_pcm_plug_t *plug,
|
||||
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;
|
||||
alaw_f func;
|
||||
|
||||
assert(r_plugin);
|
||||
*r_plugin = NULL;
|
||||
|
||||
assert(src_format->rate == dst_format->rate);
|
||||
assert(src_format->channels == dst_format->channels);
|
||||
|
||||
if (dst_format->format == SND_PCM_SFMT_A_LAW) {
|
||||
format = src_format;
|
||||
func = alaw_encode;
|
||||
}
|
||||
else if (src_format->format == SND_PCM_SFMT_A_LAW) {
|
||||
format = dst_format;
|
||||
func = alaw_decode;
|
||||
}
|
||||
else
|
||||
assert(0);
|
||||
assert(snd_pcm_format_linear(format->format));
|
||||
|
||||
err = snd_pcm_plugin_build(plug, "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);
|
||||
plugin->transfer = alaw_transfer;
|
||||
*r_plugin = plugin;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
/*
|
||||
* 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 <byteswap.h>
|
||||
#include <sys/uio.h>
|
||||
#include "../pcm_local.h"
|
||||
#endif
|
||||
|
||||
static ssize_t copy_transfer(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
size_t frames)
|
||||
{
|
||||
unsigned int channel;
|
||||
unsigned int nchannels;
|
||||
|
||||
assert(plugin && src_channels && dst_channels);
|
||||
if (frames == 0)
|
||||
return 0;
|
||||
nchannels = plugin->src_format.channels;
|
||||
for (channel = 0; channel < nchannels; channel++) {
|
||||
assert(src_channels->area.first % 8 == 0 &&
|
||||
src_channels->area.step % 8 == 0);
|
||||
assert(dst_channels->area.first % 8 == 0 &&
|
||||
dst_channels->area.step % 8 == 0);
|
||||
if (!src_channels->enabled) {
|
||||
if (dst_channels->wanted)
|
||||
snd_pcm_area_silence(&dst_channels->area, 0, frames, plugin->dst_format.format);
|
||||
dst_channels->enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_channels->enabled = 1;
|
||||
snd_pcm_area_copy(&src_channels->area, 0, &dst_channels->area, 0, frames, plugin->src_format.format);
|
||||
src_channels++;
|
||||
dst_channels++;
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_build_copy(snd_pcm_plug_t *plug,
|
||||
snd_pcm_format_t *src_format,
|
||||
snd_pcm_format_t *dst_format,
|
||||
snd_pcm_plugin_t **r_plugin)
|
||||
{
|
||||
int err;
|
||||
snd_pcm_plugin_t *plugin;
|
||||
int width;
|
||||
|
||||
assert(r_plugin);
|
||||
*r_plugin = NULL;
|
||||
|
||||
assert(src_format->format == dst_format->format);
|
||||
assert(src_format->rate == dst_format->rate);
|
||||
assert(src_format->channels == dst_format->channels);
|
||||
|
||||
width = snd_pcm_format_physical_width(src_format->format);
|
||||
assert(width > 0);
|
||||
|
||||
err = snd_pcm_plugin_build(plug, "copy", src_format, dst_format,
|
||||
0, &plugin);
|
||||
if (err < 0)
|
||||
return err;
|
||||
plugin->transfer = copy_transfer;
|
||||
*r_plugin = plugin;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,161 +0,0 @@
|
|||
/*
|
||||
* PCM I/O 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include "../../include/driver.h"
|
||||
#include "../../include/pcm.h"
|
||||
#include "../../include/pcm_plugin.h"
|
||||
#define pcm_write(plug,buf,count) snd_pcm_oss_write3(plug,buf,count,1)
|
||||
#define pcm_writev(plug,vec,count) snd_pcm_oss_writev3(plug,vec,count,1)
|
||||
#define pcm_read(plug,buf,count) snd_pcm_oss_read3(plug,buf,count,1)
|
||||
#define pcm_readv(plug,vec,count) snd_pcm_oss_readv3(plug,vec,count,1)
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/uio.h>
|
||||
#include "../pcm_local.h"
|
||||
#define pcm_write(plug,buf,count) snd_pcm_write(plug->slave,buf,count)
|
||||
#define pcm_writev(plug,vec,count) snd_pcm_writev(plug->slave,vec,count)
|
||||
#define pcm_read(plug,buf,count) snd_pcm_read(plug->slave,buf,count)
|
||||
#define pcm_readv(plug,vec,count) snd_pcm_readv(plug->slave,vec,count)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Basic io plugin
|
||||
*/
|
||||
|
||||
static ssize_t io_playback_transfer(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels ATTRIBUTE_UNUSED,
|
||||
size_t frames)
|
||||
{
|
||||
struct iovec *vec;
|
||||
int count, channel;
|
||||
|
||||
assert(plugin);
|
||||
vec = (struct iovec *)plugin->extra_data;
|
||||
assert(vec);
|
||||
assert(src_channels);
|
||||
count = plugin->src_format.channels;
|
||||
if (plugin->src_format.interleave) {
|
||||
return pcm_write(plugin->plug, src_channels->area.addr, frames);
|
||||
} else {
|
||||
for (channel = 0; channel < count; channel++) {
|
||||
if (src_channels[channel].enabled)
|
||||
vec[channel].iov_base = src_channels[channel].area.addr;
|
||||
else
|
||||
vec[channel].iov_base = 0;
|
||||
vec[channel].iov_len = frames;
|
||||
}
|
||||
return pcm_writev(plugin->plug, vec, count);
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t io_capture_transfer(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
size_t frames)
|
||||
{
|
||||
struct iovec *vec;
|
||||
int count, channel;
|
||||
|
||||
assert(plugin);
|
||||
vec = (struct iovec *)plugin->extra_data;
|
||||
assert(vec);
|
||||
assert(dst_channels);
|
||||
count = plugin->dst_format.channels;
|
||||
if (plugin->dst_format.interleave) {
|
||||
return pcm_read(plugin->plug, dst_channels->area.addr, frames);
|
||||
} else {
|
||||
for (channel = 0; channel < count; channel++) {
|
||||
if (dst_channels[channel].enabled)
|
||||
vec[channel].iov_base = dst_channels[channel].area.addr;
|
||||
else
|
||||
vec[channel].iov_base = 0;
|
||||
vec[channel].iov_len = frames;
|
||||
}
|
||||
return pcm_readv(plugin->plug, vec, count);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t io_src_channels(snd_pcm_plugin_t *plugin,
|
||||
size_t frames,
|
||||
snd_pcm_plugin_channel_t **channels)
|
||||
{
|
||||
int err;
|
||||
unsigned int channel;
|
||||
snd_pcm_plugin_channel_t *v;
|
||||
err = snd_pcm_plugin_client_channels(plugin, frames, &v);
|
||||
if (err < 0)
|
||||
return err;
|
||||
*channels = v;
|
||||
if (plugin->src_format.interleave) {
|
||||
for (channel = 0; channel < plugin->src_format.channels; ++channel, ++v)
|
||||
v->wanted = 1;
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
#ifndef __KERNEL__
|
||||
static void io_dump(snd_pcm_plugin_t *plugin, FILE *fp)
|
||||
{
|
||||
snd_pcm_t *slave = plugin->plug->slave;
|
||||
if (slave->valid_setup) {
|
||||
fprintf(fp, "Slave: ");
|
||||
snd_pcm_dump(slave, fp);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int snd_pcm_plugin_build_io(snd_pcm_plug_t *plug,
|
||||
snd_pcm_format_t *format,
|
||||
snd_pcm_plugin_t **r_plugin)
|
||||
{
|
||||
int err;
|
||||
snd_pcm_plugin_t *plugin;
|
||||
|
||||
assert(r_plugin);
|
||||
*r_plugin = NULL;
|
||||
assert(plug && format);
|
||||
err = snd_pcm_plugin_build(plug, "I/O io",
|
||||
format, format,
|
||||
sizeof(struct iovec) * format->channels,
|
||||
&plugin);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (snd_pcm_plug_stream(plug) == SND_PCM_STREAM_PLAYBACK) {
|
||||
plugin->transfer = io_playback_transfer;
|
||||
if (format->interleave)
|
||||
plugin->client_channels = io_src_channels;
|
||||
} else {
|
||||
plugin->transfer = io_capture_transfer;
|
||||
}
|
||||
#ifndef __KERNEL__
|
||||
plugin->dump = io_dump;
|
||||
#endif
|
||||
|
||||
*r_plugin = plugin;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,161 +0,0 @@
|
|||
/*
|
||||
* Linear conversion Plug-In
|
||||
* Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>,
|
||||
* Abramo Bagnara <abramo@alsa-project.org>
|
||||
*
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Library General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#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 <byteswap.h>
|
||||
#include <sys/uio.h>
|
||||
#include "../pcm_local.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Basic linear conversion plugin
|
||||
*/
|
||||
|
||||
typedef struct linear_private_data {
|
||||
int conv;
|
||||
} linear_t;
|
||||
|
||||
static void convert(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
size_t frames)
|
||||
{
|
||||
#define CONV_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef CONV_LABELS
|
||||
linear_t *data = (linear_t *)plugin->extra_data;
|
||||
void *conv = conv_labels[data->conv];
|
||||
int channel;
|
||||
int nchannels = plugin->src_format.channels;
|
||||
for (channel = 0; channel < nchannels; ++channel) {
|
||||
char *src;
|
||||
char *dst;
|
||||
int src_step, dst_step;
|
||||
size_t frames1;
|
||||
if (!src_channels[channel].enabled) {
|
||||
if (dst_channels[channel].wanted)
|
||||
snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
|
||||
dst_channels[channel].enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_channels[channel].enabled = 1;
|
||||
src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
|
||||
dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
|
||||
src_step = src_channels[channel].area.step / 8;
|
||||
dst_step = dst_channels[channel].area.step / 8;
|
||||
frames1 = frames;
|
||||
while (frames1-- > 0) {
|
||||
goto *conv;
|
||||
#define CONV_END after
|
||||
#include "plugin_ops.h"
|
||||
#undef CONV_END
|
||||
after:
|
||||
src += src_step;
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t linear_transfer(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
size_t frames)
|
||||
{
|
||||
linear_t *data;
|
||||
unsigned int channel;
|
||||
|
||||
assert(plugin && src_channels && dst_channels);
|
||||
data = (linear_t *)plugin->extra_data;
|
||||
if (frames == 0)
|
||||
return 0;
|
||||
for (channel = 0; channel < plugin->src_format.channels; channel++) {
|
||||
assert(src_channels[channel].area.first % 8 == 0 &&
|
||||
src_channels[channel].area.step % 8 == 0);
|
||||
assert(dst_channels[channel].area.first % 8 == 0 &&
|
||||
dst_channels[channel].area.step % 8 == 0);
|
||||
}
|
||||
convert(plugin, src_channels, dst_channels, frames);
|
||||
return frames;
|
||||
}
|
||||
|
||||
int conv_index(int src_format, int dst_format)
|
||||
{
|
||||
int src_endian, dst_endian, sign, src_width, dst_width;
|
||||
|
||||
sign = (snd_pcm_format_signed(src_format) !=
|
||||
snd_pcm_format_signed(dst_format));
|
||||
#ifdef SND_LITTLE_ENDIAN
|
||||
src_endian = snd_pcm_format_big_endian(src_format);
|
||||
dst_endian = snd_pcm_format_big_endian(dst_format);
|
||||
#else
|
||||
src_endian = snd_pcm_format_little_endian(src_format);
|
||||
dst_endian = snd_pcm_format_little_endian(dst_format);
|
||||
#endif
|
||||
|
||||
if (src_endian < 0)
|
||||
src_endian = 0;
|
||||
if (dst_endian < 0)
|
||||
dst_endian = 0;
|
||||
|
||||
src_width = snd_pcm_format_width(src_format) / 8 - 1;
|
||||
dst_width = snd_pcm_format_width(dst_format) / 8 - 1;
|
||||
|
||||
return src_width * 32 + src_endian * 16 + sign * 8 + dst_width * 2 + dst_endian;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_build_linear(snd_pcm_plug_t *plug,
|
||||
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;
|
||||
|
||||
assert(r_plugin);
|
||||
*r_plugin = NULL;
|
||||
|
||||
assert(src_format->rate == dst_format->rate);
|
||||
assert(src_format->channels == dst_format->channels);
|
||||
assert(snd_pcm_format_linear(src_format->format) &&
|
||||
snd_pcm_format_linear(dst_format->format));
|
||||
|
||||
err = snd_pcm_plugin_build(plug, "linear format conversion",
|
||||
src_format, dst_format,
|
||||
sizeof(linear_t), &plugin);
|
||||
if (err < 0)
|
||||
return err;
|
||||
data = (linear_t *)plugin->extra_data;
|
||||
data->conv = conv_index(src_format->format, dst_format->format);
|
||||
plugin->transfer = linear_transfer;
|
||||
*r_plugin = plugin;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,315 +0,0 @@
|
|||
/*
|
||||
* PCM MMAP 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 <assert.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/uio.h>
|
||||
#include "../pcm_local.h"
|
||||
|
||||
/*
|
||||
* Basic mmap plugin
|
||||
*/
|
||||
|
||||
typedef struct mmap_private_data {
|
||||
void *buffer;
|
||||
#if 0
|
||||
char *silence;
|
||||
#endif
|
||||
} mmap_t;
|
||||
|
||||
|
||||
static ssize_t mmap_src_channels(snd_pcm_plugin_t *plugin,
|
||||
size_t frames,
|
||||
snd_pcm_plugin_channel_t **channels)
|
||||
{
|
||||
mmap_t *data;
|
||||
snd_pcm_plugin_channel_t *sv;
|
||||
snd_pcm_channel_area_t *dv;
|
||||
snd_pcm_t *stream;
|
||||
snd_pcm_setup_t *setup;
|
||||
size_t pos;
|
||||
int ready;
|
||||
unsigned int channel;
|
||||
|
||||
assert(plugin && channels);
|
||||
data = (mmap_t *)plugin->extra_data;
|
||||
stream = plugin->plug->slave;
|
||||
|
||||
setup = &stream->setup;
|
||||
if (snd_pcm_mmap_state(stream) < SND_PCM_STATE_PREPARED)
|
||||
return -EBADFD;
|
||||
|
||||
ready = snd_pcm_mmap_ready(stream);
|
||||
if (ready < 0)
|
||||
return ready;
|
||||
if (!ready) {
|
||||
struct pollfd pfd;
|
||||
if (snd_pcm_mmap_state(stream) != SND_PCM_STATE_RUNNING)
|
||||
return -EPIPE;
|
||||
if (stream->mode & SND_PCM_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
pfd.fd = snd_pcm_file_descriptor(stream);
|
||||
pfd.events = POLLOUT | POLLERR;
|
||||
ready = poll(&pfd, 1, 10000);
|
||||
if (ready < 0)
|
||||
return ready;
|
||||
if (ready == 0 || (pfd.revents & POLLERR))
|
||||
return -EPIPE;
|
||||
assert(snd_pcm_mmap_ready(stream));
|
||||
}
|
||||
pos = snd_pcm_mmap_offset(stream);
|
||||
assert(pos % setup->align == 0);
|
||||
|
||||
sv = plugin->buf_channels;
|
||||
dv = stream->channels;
|
||||
*channels = sv;
|
||||
for (channel = 0; channel < plugin->src_format.channels; ++channel) {
|
||||
sv->enabled = 1;
|
||||
#if 0
|
||||
sv->wanted = !data->silence[channel * setup->frags + f];
|
||||
#else
|
||||
sv->wanted = 1;
|
||||
#endif
|
||||
sv->area.addr = dv->addr + dv->step * pos / 8;
|
||||
sv->area.first = dv->first;
|
||||
sv->area.step = dv->step;
|
||||
++sv;
|
||||
++dv;
|
||||
}
|
||||
return snd_pcm_mmap_xfer(stream, frames);
|
||||
}
|
||||
|
||||
static ssize_t mmap_dst_channels(snd_pcm_plugin_t *plugin,
|
||||
size_t frames,
|
||||
snd_pcm_plugin_channel_t **channels)
|
||||
{
|
||||
mmap_t *data;
|
||||
int err;
|
||||
unsigned int channel;
|
||||
snd_pcm_plugin_channel_t *dv;
|
||||
snd_pcm_channel_area_t *sv;
|
||||
snd_pcm_t *stream;
|
||||
snd_pcm_setup_t *setup;
|
||||
size_t pos;
|
||||
int ready;
|
||||
|
||||
assert(plugin && channels);
|
||||
data = (mmap_t *)plugin->extra_data;
|
||||
stream = plugin->plug->slave;
|
||||
|
||||
setup = &stream->setup;
|
||||
if (snd_pcm_mmap_state(stream) < SND_PCM_STATE_PREPARED)
|
||||
return -EBADFD;
|
||||
if (snd_pcm_mmap_state(stream) == SND_PCM_STATE_PREPARED &&
|
||||
stream->setup.start_mode == SND_PCM_START_DATA) {
|
||||
err = snd_pcm_go(stream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
ready = snd_pcm_mmap_ready(stream);
|
||||
if (ready < 0)
|
||||
return ready;
|
||||
if (!ready) {
|
||||
struct pollfd pfd;
|
||||
if (snd_pcm_mmap_state(stream) != SND_PCM_STATE_RUNNING)
|
||||
return -EPIPE;
|
||||
if (stream->mode & SND_PCM_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
pfd.fd = snd_pcm_file_descriptor(stream);
|
||||
pfd.events = POLLIN | POLLERR;
|
||||
ready = poll(&pfd, 1, 10000);
|
||||
if (ready < 0)
|
||||
return ready;
|
||||
if (ready == 0 || (pfd.revents & POLLERR))
|
||||
return -EPIPE;
|
||||
assert(snd_pcm_mmap_ready(stream));
|
||||
}
|
||||
pos = snd_pcm_mmap_offset(stream);
|
||||
assert(pos % setup->align == 0);
|
||||
|
||||
sv = stream->channels;
|
||||
dv = plugin->buf_channels;
|
||||
*channels = dv;
|
||||
for (channel = 0; channel < plugin->dst_format.channels; ++channel) {
|
||||
dv->enabled = 1;
|
||||
dv->wanted = 0;
|
||||
dv->area.addr = sv->addr + sv->step * pos / 8;
|
||||
dv->area.first = sv->first;
|
||||
dv->area.step = sv->step;
|
||||
++sv;
|
||||
++dv;
|
||||
}
|
||||
return snd_pcm_mmap_xfer(stream, frames);
|
||||
}
|
||||
|
||||
static ssize_t mmap_playback_transfer(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels ATTRIBUTE_UNUSED,
|
||||
size_t frames)
|
||||
{
|
||||
mmap_t *data;
|
||||
snd_pcm_setup_t *setup;
|
||||
snd_pcm_t *stream;
|
||||
int err;
|
||||
|
||||
assert(plugin && plugin->prev);
|
||||
assert(src_channels);
|
||||
data = (mmap_t *)plugin->extra_data;
|
||||
stream = plugin->plug->slave;
|
||||
setup = &stream->setup;
|
||||
|
||||
#if 0
|
||||
for (channel = 0; channel < plugin->src_format.channels; channel++) {
|
||||
if (src_channels[channel].enabled)
|
||||
data->silence[channel * setup->frags + f] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
err = snd_pcm_appl_ptr(stream, frames);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (snd_pcm_mmap_state(stream) == SND_PCM_STATE_PREPARED &&
|
||||
(setup->start_mode == SND_PCM_START_DATA ||
|
||||
(setup->start_mode == SND_PCM_START_FULL &&
|
||||
!snd_pcm_mmap_ready(stream)))) {
|
||||
err = snd_pcm_go(stream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
static ssize_t mmap_capture_transfer(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED,
|
||||
snd_pcm_plugin_channel_t *dst_channels ATTRIBUTE_UNUSED,
|
||||
size_t frames)
|
||||
{
|
||||
mmap_t *data;
|
||||
snd_pcm_t *stream;
|
||||
int err;
|
||||
|
||||
assert(plugin && plugin->next);
|
||||
data = (mmap_t *)plugin->extra_data;
|
||||
stream = plugin->plug->slave;
|
||||
|
||||
/* FIXME: not here the increment */
|
||||
err = snd_pcm_appl_ptr(stream, frames);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
||||
static int mmap_action(snd_pcm_plugin_t *plugin,
|
||||
snd_pcm_plugin_action_t action,
|
||||
unsigned long udata ATTRIBUTE_UNUSED)
|
||||
{
|
||||
struct mmap_private_data *data;
|
||||
snd_pcm_t *stream;
|
||||
|
||||
assert(plugin);
|
||||
stream = plugin->plug->slave;
|
||||
data = (mmap_t *)plugin->extra_data;
|
||||
if (action == INIT) {
|
||||
snd_pcm_setup_t *setup;
|
||||
int result;
|
||||
|
||||
if (data->buffer) {
|
||||
snd_pcm_munmap(stream);
|
||||
data->buffer = 0;
|
||||
}
|
||||
result = snd_pcm_mmap(stream, NULL, NULL, (void **)&data->buffer);
|
||||
if (result < 0)
|
||||
return result;
|
||||
setup = &stream->setup;
|
||||
|
||||
#if 0
|
||||
if (plugin->stream == SND_PCM_STREAM_PLAYBACK) {
|
||||
data->silence = malloc(setup->frags * setup->format.channels);
|
||||
memset(data->silence, 0, setup->frags * setup->format.channels);
|
||||
} else
|
||||
data->silence = 0;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
return 0; /* silenty ignore other actions */
|
||||
}
|
||||
|
||||
static void mmap_free(snd_pcm_plugin_t *plugin)
|
||||
{
|
||||
struct mmap_private_data *data;
|
||||
|
||||
if (plugin == NULL)
|
||||
return;
|
||||
data = (mmap_t *)plugin->extra_data;
|
||||
#if 0
|
||||
if (data->silence)
|
||||
free(data->silence);
|
||||
#endif
|
||||
if (data->buffer)
|
||||
snd_pcm_munmap(plugin->plug->slave);
|
||||
}
|
||||
|
||||
static void mmap_dump(snd_pcm_plugin_t *plugin, FILE *fp)
|
||||
{
|
||||
snd_pcm_t *slave = plugin->plug->slave;
|
||||
if (slave->valid_setup) {
|
||||
fprintf(fp, "Slave: ");
|
||||
snd_pcm_dump(slave, fp);
|
||||
}
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_build_mmap(snd_pcm_plug_t *plug,
|
||||
snd_pcm_format_t *format,
|
||||
snd_pcm_plugin_t **r_plugin)
|
||||
{
|
||||
int err;
|
||||
mmap_t *data;
|
||||
snd_pcm_plugin_t *plugin;
|
||||
|
||||
assert(r_plugin);
|
||||
*r_plugin = NULL;
|
||||
assert(plug);
|
||||
err = snd_pcm_plugin_build(plug, "I/O mmap",
|
||||
format, format,
|
||||
sizeof(mmap_t) + sizeof(snd_pcm_plugin_channel_t) * format->channels,
|
||||
&plugin);
|
||||
if (err < 0)
|
||||
return err;
|
||||
data = (mmap_t *)plugin->extra_data;
|
||||
if (plug->handle->stream == SND_PCM_STREAM_PLAYBACK) {
|
||||
plugin->client_channels = mmap_src_channels;
|
||||
plugin->transfer = mmap_playback_transfer;
|
||||
} else {
|
||||
plugin->client_channels = mmap_dst_channels;
|
||||
plugin->transfer = mmap_capture_transfer;
|
||||
}
|
||||
plugin->action = mmap_action;
|
||||
plugin->private_free = mmap_free;
|
||||
plugin->dump = mmap_dump;
|
||||
*r_plugin = plugin;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,309 +0,0 @@
|
|||
/*
|
||||
* Mu-Law conversion Plug-In Interface
|
||||
* Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
|
||||
* Uros Bizjak <uros@kss-loka.si>
|
||||
*
|
||||
* Based on reference implementation by Sun Microsystems, Inc.
|
||||
*
|
||||
* 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 <byteswap.h>
|
||||
#include <sys/uio.h>
|
||||
#include "../pcm_local.h"
|
||||
#endif
|
||||
|
||||
#define SIGN_BIT (0x80) /* Sign bit for a u-law byte. */
|
||||
#define QUANT_MASK (0xf) /* Quantization field mask. */
|
||||
#define NSEGS (8) /* Number of u-law segments. */
|
||||
#define SEG_SHIFT (4) /* Left shift for segment number. */
|
||||
#define SEG_MASK (0x70) /* Segment field mask. */
|
||||
|
||||
static short ulaw_seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF,
|
||||
0xFFF, 0x1FFF, 0x3FFF, 0x7FFF};
|
||||
|
||||
static inline int search(int val, short *table, int size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (val <= *table++)
|
||||
return (i);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
#define BIAS (0x84) /* Bias for linear code. */
|
||||
|
||||
/*
|
||||
* linear2ulaw() - Convert a linear PCM value to u-law
|
||||
*
|
||||
* In order to simplify the encoding process, the original linear magnitude
|
||||
* is biased by adding 33 which shifts the encoding range from (0 - 8158) to
|
||||
* (33 - 8191). The result can be seen in the following encoding table:
|
||||
*
|
||||
* Biased Linear Input Code Compressed Code
|
||||
* ------------------------ ---------------
|
||||
* 00000001wxyza 000wxyz
|
||||
* 0000001wxyzab 001wxyz
|
||||
* 000001wxyzabc 010wxyz
|
||||
* 00001wxyzabcd 011wxyz
|
||||
* 0001wxyzabcde 100wxyz
|
||||
* 001wxyzabcdef 101wxyz
|
||||
* 01wxyzabcdefg 110wxyz
|
||||
* 1wxyzabcdefgh 111wxyz
|
||||
*
|
||||
* Each biased linear code has a leading 1 which identifies the segment
|
||||
* number. The value of the segment number is equal to 7 minus the number
|
||||
* of leading 0's. The quantization interval is directly available as the
|
||||
* four bits wxyz. * The trailing bits (a - h) are ignored.
|
||||
*
|
||||
* Ordinarily the complement of the resulting code word is used for
|
||||
* transmission, and so the code word is complemented before it is returned.
|
||||
*
|
||||
* For further information see John C. Bellamy's Digital Telephony, 1982,
|
||||
* John Wiley & Sons, pps 98-111 and 472-476.
|
||||
*/
|
||||
static unsigned char linear2ulaw(int pcm_val) /* 2's complement (16-bit range) */
|
||||
{
|
||||
int mask;
|
||||
int seg;
|
||||
unsigned char uval;
|
||||
|
||||
/* Get the sign and the magnitude of the value. */
|
||||
if (pcm_val < 0) {
|
||||
pcm_val = BIAS - pcm_val;
|
||||
mask = 0x7F;
|
||||
} else {
|
||||
pcm_val += BIAS;
|
||||
mask = 0xFF;
|
||||
}
|
||||
|
||||
/* Convert the scaled magnitude to segment number. */
|
||||
seg = search(pcm_val, ulaw_seg_end, NSEGS);
|
||||
|
||||
/*
|
||||
* Combine the sign, segment, quantization bits;
|
||||
* and complement the code word.
|
||||
*/
|
||||
if (seg >= 8) /* out of range, return maximum value. */
|
||||
return 0x7F ^ mask;
|
||||
else {
|
||||
uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
|
||||
return uval ^ mask;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ulaw2linear() - Convert a u-law value to 16-bit linear PCM
|
||||
*
|
||||
* First, a biased linear code is derived from the code word. An unbiased
|
||||
* output can then be obtained by subtracting 33 from the biased code.
|
||||
*
|
||||
* Note that this function expects to be passed the complement of the
|
||||
* original code word. This is in keeping with ISDN conventions.
|
||||
*/
|
||||
static int ulaw2linear(unsigned char u_val)
|
||||
{
|
||||
int t;
|
||||
|
||||
/* Complement to obtain normal u-law value. */
|
||||
u_val = ~u_val;
|
||||
|
||||
/*
|
||||
* Extract and bias the quantization bits. Then
|
||||
* shift up by the segment number and subtract out the bias.
|
||||
*/
|
||||
t = ((u_val & QUANT_MASK) << 3) + BIAS;
|
||||
t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
|
||||
|
||||
return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic Mu-Law plugin
|
||||
*/
|
||||
|
||||
typedef void (*mulaw_f)(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
size_t frames);
|
||||
|
||||
typedef struct mulaw_private_data {
|
||||
mulaw_f func;
|
||||
int conv;
|
||||
} mulaw_t;
|
||||
|
||||
static void mulaw_decode(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
size_t frames)
|
||||
{
|
||||
#define PUT_S16_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_S16_LABELS
|
||||
mulaw_t *data = (mulaw_t *)plugin->extra_data;
|
||||
void *put = put_s16_labels[data->conv];
|
||||
int channel;
|
||||
int nchannels = plugin->src_format.channels;
|
||||
for (channel = 0; channel < nchannels; ++channel) {
|
||||
char *src;
|
||||
char *dst;
|
||||
int src_step, dst_step;
|
||||
size_t frames1;
|
||||
if (!src_channels[channel].enabled) {
|
||||
if (dst_channels[channel].wanted)
|
||||
snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
|
||||
dst_channels[channel].enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_channels[channel].enabled = 1;
|
||||
src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
|
||||
dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
|
||||
src_step = src_channels[channel].area.step / 8;
|
||||
dst_step = dst_channels[channel].area.step / 8;
|
||||
frames1 = frames;
|
||||
while (frames1-- > 0) {
|
||||
signed short sample = ulaw2linear(*src);
|
||||
goto *put;
|
||||
#define PUT_S16_END after
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_S16_END
|
||||
after:
|
||||
src += src_step;
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mulaw_encode(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
size_t frames)
|
||||
{
|
||||
#define GET_S16_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_LABELS
|
||||
mulaw_t *data = (mulaw_t *)plugin->extra_data;
|
||||
void *get = get_s16_labels[data->conv];
|
||||
int channel;
|
||||
int nchannels = plugin->src_format.channels;
|
||||
signed short sample = 0;
|
||||
for (channel = 0; channel < nchannels; ++channel) {
|
||||
char *src;
|
||||
char *dst;
|
||||
int src_step, dst_step;
|
||||
size_t frames1;
|
||||
if (!src_channels[channel].enabled) {
|
||||
if (dst_channels[channel].wanted)
|
||||
snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
|
||||
dst_channels[channel].enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_channels[channel].enabled = 1;
|
||||
src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
|
||||
dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
|
||||
src_step = src_channels[channel].area.step / 8;
|
||||
dst_step = dst_channels[channel].area.step / 8;
|
||||
frames1 = frames;
|
||||
while (frames1-- > 0) {
|
||||
goto *get;
|
||||
#define GET_S16_END after
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_END
|
||||
after:
|
||||
*dst = linear2ulaw(sample);
|
||||
src += src_step;
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t mulaw_transfer(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
size_t frames)
|
||||
{
|
||||
mulaw_t *data;
|
||||
unsigned int channel;
|
||||
|
||||
assert(plugin && src_channels && dst_channels);
|
||||
if (frames == 0)
|
||||
return 0;
|
||||
for (channel = 0; channel < plugin->src_format.channels; channel++) {
|
||||
assert(src_channels[channel].area.first % 8 == 0 &&
|
||||
src_channels[channel].area.step % 8 == 0);
|
||||
assert(dst_channels[channel].area.first % 8 == 0 &&
|
||||
dst_channels[channel].area.step % 8 == 0);
|
||||
}
|
||||
data = (mulaw_t *)plugin->extra_data;
|
||||
data->func(plugin, src_channels, dst_channels, frames);
|
||||
return frames;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_build_mulaw(snd_pcm_plug_t *plug,
|
||||
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;
|
||||
mulaw_f func;
|
||||
|
||||
assert(r_plugin);
|
||||
*r_plugin = NULL;
|
||||
|
||||
assert(src_format->rate == dst_format->rate);
|
||||
assert(src_format->channels == dst_format->channels);
|
||||
|
||||
if (dst_format->format == SND_PCM_SFMT_MU_LAW) {
|
||||
format = src_format;
|
||||
func = mulaw_encode;
|
||||
}
|
||||
else if (src_format->format == SND_PCM_SFMT_MU_LAW) {
|
||||
format = dst_format;
|
||||
func = mulaw_decode;
|
||||
}
|
||||
else {
|
||||
assert(0);
|
||||
return -EINVAL;
|
||||
}
|
||||
assert(snd_pcm_format_linear(format->format));
|
||||
|
||||
err = snd_pcm_plugin_build(plug, "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);
|
||||
plugin->transfer = mulaw_transfer;
|
||||
*r_plugin = plugin;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,390 +0,0 @@
|
|||
/*
|
||||
* Rate conversion Plug-In
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#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 <byteswap.h>
|
||||
#include "../pcm_local.h"
|
||||
#endif
|
||||
|
||||
#define SHIFT 11
|
||||
#define BITS (1<<SHIFT)
|
||||
#define MASK (BITS-1)
|
||||
|
||||
/*
|
||||
* Basic rate conversion plugin
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
signed short last_S1;
|
||||
signed short last_S2;
|
||||
} rate_channel_t;
|
||||
|
||||
typedef void (*rate_f)(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
int src_frames, int dst_frames);
|
||||
|
||||
typedef struct rate_private_data {
|
||||
unsigned int pitch;
|
||||
unsigned int pos;
|
||||
rate_f func;
|
||||
int get, put;
|
||||
ssize_t old_src_frames, old_dst_frames;
|
||||
rate_channel_t channels[0];
|
||||
} rate_t;
|
||||
|
||||
static void rate_init(snd_pcm_plugin_t *plugin)
|
||||
{
|
||||
unsigned int channel;
|
||||
rate_t *data = (rate_t *)plugin->extra_data;
|
||||
data->pos = 0;
|
||||
for (channel = 0; channel < plugin->src_format.channels; channel++) {
|
||||
data->channels[channel].last_S1 = 0;
|
||||
data->channels[channel].last_S2 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void resample_expand(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
int src_frames, int dst_frames)
|
||||
{
|
||||
unsigned int pos = 0;
|
||||
signed int val;
|
||||
signed short S1, S2;
|
||||
char *src, *dst;
|
||||
unsigned int channel;
|
||||
int src_step, dst_step;
|
||||
int src_frames1, dst_frames1;
|
||||
rate_t *data = (rate_t *)plugin->extra_data;
|
||||
rate_channel_t *rchannels = data->channels;
|
||||
|
||||
#define GET_S16_LABELS
|
||||
#define PUT_S16_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_LABELS
|
||||
#undef PUT_S16_LABELS
|
||||
void *get = get_s16_labels[data->get];
|
||||
void *put = put_s16_labels[data->put];
|
||||
void *get_s16_end = 0;
|
||||
signed short sample = 0;
|
||||
#define GET_S16_END *get_s16_end
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_END
|
||||
|
||||
for (channel = 0; channel < plugin->src_format.channels; channel++) {
|
||||
pos = data->pos;
|
||||
S1 = rchannels->last_S1;
|
||||
S2 = rchannels->last_S2;
|
||||
if (!src_channels[channel].enabled) {
|
||||
if (dst_channels[channel].wanted)
|
||||
snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format);
|
||||
dst_channels[channel].enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_channels[channel].enabled = 1;
|
||||
src = (char *)src_channels[channel].area.addr + src_channels[channel].area.first / 8;
|
||||
dst = (char *)dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
|
||||
src_step = src_channels[channel].area.step / 8;
|
||||
dst_step = dst_channels[channel].area.step / 8;
|
||||
src_frames1 = src_frames;
|
||||
dst_frames1 = dst_frames;
|
||||
if (pos & ~MASK) {
|
||||
get_s16_end = &&after_get1;
|
||||
goto *get;
|
||||
after_get1:
|
||||
pos &= MASK;
|
||||
S1 = S2;
|
||||
S2 = sample;
|
||||
src += src_step;
|
||||
src_frames--;
|
||||
}
|
||||
while (dst_frames1-- > 0) {
|
||||
if (pos & ~MASK) {
|
||||
pos &= MASK;
|
||||
S1 = S2;
|
||||
if (src_frames1-- > 0) {
|
||||
get_s16_end = &&after_get2;
|
||||
goto *get;
|
||||
after_get2:
|
||||
S2 = sample;
|
||||
src += src_step;
|
||||
}
|
||||
}
|
||||
val = S1 + ((S2 - S1) * (signed int)pos) / BITS;
|
||||
if (val < -32768)
|
||||
val = -32768;
|
||||
else if (val > 32767)
|
||||
val = 32767;
|
||||
sample = val;
|
||||
goto *put;
|
||||
#define PUT_S16_END after_put
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_S16_END
|
||||
after_put:
|
||||
dst += dst_step;
|
||||
pos += data->pitch;
|
||||
}
|
||||
rchannels->last_S1 = S1;
|
||||
rchannels->last_S2 = S2;
|
||||
rchannels++;
|
||||
}
|
||||
data->pos = pos;
|
||||
}
|
||||
|
||||
static void resample_shrink(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
int src_frames, int dst_frames)
|
||||
{
|
||||
unsigned int pos = 0;
|
||||
signed int val;
|
||||
signed short S1, S2;
|
||||
char *src, *dst;
|
||||
unsigned int channel;
|
||||
int src_step, dst_step;
|
||||
int src_frames1, dst_frames1;
|
||||
rate_t *data = (rate_t *)plugin->extra_data;
|
||||
rate_channel_t *rchannels = data->channels;
|
||||
|
||||
#define GET_S16_LABELS
|
||||
#define PUT_S16_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_LABELS
|
||||
#undef PUT_S16_LABELS
|
||||
void *get = get_s16_labels[data->get];
|
||||
void *put = put_s16_labels[data->put];
|
||||
signed short sample = 0;
|
||||
|
||||
for (channel = 0; channel < plugin->src_format.channels; ++channel) {
|
||||
pos = data->pos;
|
||||
S1 = rchannels->last_S1;
|
||||
S2 = rchannels->last_S2;
|
||||
if (!src_channels[channel].enabled) {
|
||||
if (dst_channels[channel].wanted)
|
||||
snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format);
|
||||
dst_channels[channel].enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_channels[channel].enabled = 1;
|
||||
src = (char *)src_channels[channel].area.addr + src_channels[channel].area.first / 8;
|
||||
dst = (char *)dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
|
||||
src_step = src_channels[channel].area.step / 8;
|
||||
dst_step = dst_channels[channel].area.step / 8;
|
||||
src_frames1 = src_frames;
|
||||
dst_frames1 = dst_frames;
|
||||
while (dst_frames1 > 0) {
|
||||
S1 = S2;
|
||||
if (src_frames1-- > 0) {
|
||||
goto *get;
|
||||
#define GET_S16_END after_get
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_END
|
||||
after_get:
|
||||
S2 = sample;
|
||||
src += src_step;
|
||||
}
|
||||
if (pos & ~MASK) {
|
||||
pos &= MASK;
|
||||
val = S1 + ((S2 - S1) * (signed int)pos) / BITS;
|
||||
if (val < -32768)
|
||||
val = -32768;
|
||||
else if (val > 32767)
|
||||
val = 32767;
|
||||
sample = val;
|
||||
goto *put;
|
||||
#define PUT_S16_END after_put
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_S16_END
|
||||
after_put:
|
||||
dst += dst_step;
|
||||
dst_frames1--;
|
||||
}
|
||||
pos += data->pitch;
|
||||
}
|
||||
rchannels->last_S1 = S1;
|
||||
rchannels->last_S2 = S2;
|
||||
rchannels++;
|
||||
}
|
||||
data->pos = pos;
|
||||
}
|
||||
|
||||
static ssize_t rate_src_frames(snd_pcm_plugin_t *plugin, size_t frames)
|
||||
{
|
||||
rate_t *data;
|
||||
ssize_t res;
|
||||
|
||||
assert(plugin);
|
||||
if (frames == 0)
|
||||
return 0;
|
||||
data = (rate_t *)plugin->extra_data;
|
||||
if (plugin->src_format.rate < plugin->dst_format.rate) {
|
||||
res = (((frames * data->pitch) + (BITS/2)) >> SHIFT);
|
||||
} else {
|
||||
res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch);
|
||||
}
|
||||
if (data->old_src_frames > 0) {
|
||||
ssize_t frames1 = frames, res1 = data->old_dst_frames;
|
||||
while (data->old_src_frames < frames1) {
|
||||
frames1 >>= 1;
|
||||
res1 <<= 1;
|
||||
}
|
||||
while (data->old_src_frames > frames1) {
|
||||
frames1 <<= 1;
|
||||
res1 >>= 1;
|
||||
}
|
||||
if (data->old_src_frames == frames1)
|
||||
return res1;
|
||||
}
|
||||
data->old_src_frames = frames;
|
||||
data->old_dst_frames = res;
|
||||
return res;
|
||||
}
|
||||
|
||||
static ssize_t rate_dst_frames(snd_pcm_plugin_t *plugin, size_t frames)
|
||||
{
|
||||
rate_t *data;
|
||||
ssize_t res;
|
||||
|
||||
assert(plugin);
|
||||
if (frames == 0)
|
||||
return 0;
|
||||
data = (rate_t *)plugin->extra_data;
|
||||
if (plugin->src_format.rate < plugin->dst_format.rate) {
|
||||
res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch);
|
||||
} else {
|
||||
res = (((frames * data->pitch) + (BITS/2)) >> SHIFT);
|
||||
}
|
||||
if (data->old_dst_frames > 0) {
|
||||
ssize_t frames1 = frames, res1 = data->old_src_frames;
|
||||
while (data->old_dst_frames < frames1) {
|
||||
frames1 >>= 1;
|
||||
res1 <<= 1;
|
||||
}
|
||||
while (data->old_dst_frames > frames1) {
|
||||
frames1 <<= 1;
|
||||
res1 >>= 1;
|
||||
}
|
||||
if (data->old_dst_frames == frames1)
|
||||
return res1;
|
||||
}
|
||||
data->old_dst_frames = frames;
|
||||
data->old_src_frames = res;
|
||||
return res;
|
||||
}
|
||||
|
||||
static ssize_t rate_transfer(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
size_t frames)
|
||||
{
|
||||
size_t dst_frames;
|
||||
unsigned int channel;
|
||||
rate_t *data;
|
||||
|
||||
assert(plugin && src_channels && dst_channels);
|
||||
if (frames == 0)
|
||||
return 0;
|
||||
for (channel = 0; channel < plugin->src_format.channels; channel++) {
|
||||
assert(src_channels[channel].area.first % 8 == 0 &&
|
||||
src_channels[channel].area.step % 8 == 0);
|
||||
assert(dst_channels[channel].area.first % 8 == 0 &&
|
||||
dst_channels[channel].area.step % 8 == 0);
|
||||
}
|
||||
|
||||
dst_frames = rate_dst_frames(plugin, frames);
|
||||
data = (rate_t *)plugin->extra_data;
|
||||
data->func(plugin, src_channels, dst_channels, frames, dst_frames);
|
||||
return dst_frames;
|
||||
}
|
||||
|
||||
static int rate_action(snd_pcm_plugin_t *plugin,
|
||||
snd_pcm_plugin_action_t action,
|
||||
unsigned long udata ATTRIBUTE_UNUSED)
|
||||
{
|
||||
assert(plugin);
|
||||
switch (action) {
|
||||
case INIT:
|
||||
case PREPARE:
|
||||
case DRAIN:
|
||||
case FLUSH:
|
||||
rate_init(plugin);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0; /* silenty ignore other actions */
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_build_rate(snd_pcm_plug_t *plug,
|
||||
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;
|
||||
|
||||
assert(r_plugin);
|
||||
*r_plugin = NULL;
|
||||
|
||||
assert(src_format->channels == dst_format->channels);
|
||||
assert(src_format->channels > 0);
|
||||
assert(snd_pcm_format_linear(src_format->format) > 0);
|
||||
assert(snd_pcm_format_linear(dst_format->format) > 0);
|
||||
assert(src_format->rate != dst_format->rate);
|
||||
|
||||
err = snd_pcm_plugin_build(plug, "rate conversion",
|
||||
src_format, dst_format,
|
||||
sizeof(rate_t) + src_format->channels * sizeof(rate_channel_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);
|
||||
|
||||
if (src_format->rate < dst_format->rate) {
|
||||
data->pitch = ((src_format->rate << SHIFT) + (dst_format->rate >> 1)) / dst_format->rate;
|
||||
data->func = resample_expand;
|
||||
} else {
|
||||
data->pitch = ((dst_format->rate << SHIFT) + (src_format->rate >> 1)) / src_format->rate;
|
||||
data->func = resample_shrink;
|
||||
}
|
||||
data->pos = 0;
|
||||
rate_init(plugin);
|
||||
data->old_src_frames = data->old_dst_frames = 0;
|
||||
plugin->transfer = rate_transfer;
|
||||
plugin->src_frames = rate_src_frames;
|
||||
plugin->dst_frames = rate_dst_frames;
|
||||
plugin->action = rate_action;
|
||||
*r_plugin = plugin;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,621 +0,0 @@
|
|||
/*
|
||||
* Attenuated route 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 <byteswap.h>
|
||||
#include <math.h>
|
||||
#include "../pcm_local.h"
|
||||
#endif
|
||||
|
||||
/* The best possible hack to support missing optimization in gcc 2.7.2.3 */
|
||||
#if ROUTE_PLUGIN_RESOLUTION & (ROUTE_PLUGIN_RESOLUTION - 1) != 0
|
||||
#define div(a) a /= ROUTE_PLUGIN_RESOLUTION
|
||||
#elif ROUTE_PLUGIN_RESOLUTION == 16
|
||||
#define div(a) a >>= 4
|
||||
#else
|
||||
#error "Add some code here"
|
||||
#endif
|
||||
|
||||
typedef struct ttable_dst ttable_dst_t;
|
||||
typedef struct route_private_data route_t;
|
||||
|
||||
typedef void (*route_channel_f)(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channel,
|
||||
ttable_dst_t* ttable, size_t frames);
|
||||
|
||||
typedef struct {
|
||||
int channel;
|
||||
int as_int;
|
||||
#if ROUTE_PLUGIN_USE_FLOAT
|
||||
float as_float;
|
||||
#endif
|
||||
} ttable_src_t;
|
||||
|
||||
struct ttable_dst {
|
||||
int att; /* Attenuated */
|
||||
unsigned int nsrcs;
|
||||
ttable_src_t* srcs;
|
||||
route_channel_f func;
|
||||
};
|
||||
|
||||
struct route_private_data {
|
||||
enum {UINT32=0, UINT64=1, FLOAT=2} sum_type;
|
||||
int get, put;
|
||||
int conv;
|
||||
int src_sample_size;
|
||||
ttable_dst_t ttable[0];
|
||||
};
|
||||
|
||||
typedef union {
|
||||
u_int32_t as_uint32;
|
||||
u_int64_t as_uint64;
|
||||
#if ROUTE_PLUGIN_USE_FLOAT
|
||||
float as_float;
|
||||
#endif
|
||||
} sum_t;
|
||||
|
||||
|
||||
static void route_to_channel_from_zero(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED,
|
||||
snd_pcm_plugin_channel_t *dst_channel,
|
||||
ttable_dst_t* ttable ATTRIBUTE_UNUSED, size_t frames)
|
||||
{
|
||||
if (dst_channel->wanted)
|
||||
snd_pcm_area_silence(&dst_channel->area, 0, frames, plugin->dst_format.format);
|
||||
dst_channel->enabled = 0;
|
||||
}
|
||||
|
||||
static void route_to_channel_from_one(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channel,
|
||||
ttable_dst_t* ttable, size_t frames)
|
||||
{
|
||||
#define CONV_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef CONV_LABELS
|
||||
route_t *data = (route_t *)plugin->extra_data;
|
||||
void *conv;
|
||||
const snd_pcm_plugin_channel_t *src_channel = 0;
|
||||
unsigned int srcidx;
|
||||
char *src, *dst;
|
||||
int src_step, dst_step;
|
||||
for (srcidx = 0; srcidx < ttable->nsrcs; ++srcidx) {
|
||||
src_channel = &src_channels[ttable->srcs[srcidx].channel];
|
||||
if (src_channel->area.addr != NULL)
|
||||
break;
|
||||
}
|
||||
if (srcidx == ttable->nsrcs) {
|
||||
route_to_channel_from_zero(plugin, src_channels, dst_channel, ttable, frames);
|
||||
return;
|
||||
}
|
||||
|
||||
dst_channel->enabled = 1;
|
||||
conv = conv_labels[data->conv];
|
||||
src = src_channel->area.addr + src_channel->area.first / 8;
|
||||
src_step = src_channel->area.step / 8;
|
||||
dst = dst_channel->area.addr + dst_channel->area.first / 8;
|
||||
dst_step = dst_channel->area.step / 8;
|
||||
while (frames-- > 0) {
|
||||
goto *conv;
|
||||
#define CONV_END after
|
||||
#include "plugin_ops.h"
|
||||
#undef CONV_END
|
||||
after:
|
||||
src += src_step;
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
|
||||
static void route_to_channel(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channel,
|
||||
ttable_dst_t* ttable, size_t frames)
|
||||
{
|
||||
#define GET_U_LABELS
|
||||
#define PUT_U32_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_U_LABELS
|
||||
#undef PUT_U32_LABELS
|
||||
static void *zero_labels[3] = { &&zero_int32, &&zero_int64,
|
||||
#if ROUTE_PLUGIN_USE_FLOAT
|
||||
&&zero_float
|
||||
#endif
|
||||
};
|
||||
/* sum_type att */
|
||||
static void *add_labels[3 * 2] = { &&add_int32_noatt, &&add_int32_att,
|
||||
&&add_int64_noatt, &&add_int64_att,
|
||||
#if ROUTE_PLUGIN_USE_FLOAT
|
||||
&&add_float_noatt, &&add_float_att
|
||||
#endif
|
||||
};
|
||||
/* sum_type att shift */
|
||||
static void *norm_labels[3 * 2 * 4] = { 0,
|
||||
&&norm_int32_8_noatt,
|
||||
&&norm_int32_16_noatt,
|
||||
&&norm_int32_24_noatt,
|
||||
0,
|
||||
&&norm_int32_8_att,
|
||||
&&norm_int32_16_att,
|
||||
&&norm_int32_24_att,
|
||||
&&norm_int64_0_noatt,
|
||||
&&norm_int64_8_noatt,
|
||||
&&norm_int64_16_noatt,
|
||||
&&norm_int64_24_noatt,
|
||||
&&norm_int64_0_att,
|
||||
&&norm_int64_8_att,
|
||||
&&norm_int64_16_att,
|
||||
&&norm_int64_24_att,
|
||||
#if ROUTE_PLUGIN_USE_FLOAT
|
||||
&&norm_float_0,
|
||||
&&norm_float_8,
|
||||
&&norm_float_16,
|
||||
&&norm_float_24,
|
||||
&&norm_float_0,
|
||||
&&norm_float_8,
|
||||
&&norm_float_16,
|
||||
&&norm_float_24,
|
||||
#endif
|
||||
};
|
||||
route_t *data = (route_t *)plugin->extra_data;
|
||||
void *zero, *get, *add, *norm, *put_u32;
|
||||
int nsrcs = ttable->nsrcs;
|
||||
char *dst;
|
||||
int dst_step;
|
||||
char *srcs[nsrcs];
|
||||
int src_steps[nsrcs];
|
||||
ttable_src_t src_tt[nsrcs];
|
||||
u_int32_t sample = 0;
|
||||
int srcidx, srcidx1 = 0;
|
||||
for (srcidx = 0; srcidx < nsrcs; ++srcidx) {
|
||||
const snd_pcm_plugin_channel_t *src_channel = &src_channels[ttable->srcs[srcidx].channel];
|
||||
if (!src_channel->enabled)
|
||||
continue;
|
||||
srcs[srcidx1] = src_channel->area.addr + src_channels->area.first / 8;
|
||||
src_steps[srcidx1] = src_channel->area.step / 8;
|
||||
src_tt[srcidx1] = ttable->srcs[srcidx];
|
||||
srcidx1++;
|
||||
}
|
||||
nsrcs = srcidx1;
|
||||
if (nsrcs == 0) {
|
||||
route_to_channel_from_zero(plugin, src_channels, dst_channel, ttable, frames);
|
||||
return;
|
||||
} else if (nsrcs == 1 && src_tt[0].as_int == ROUTE_PLUGIN_RESOLUTION) {
|
||||
route_to_channel_from_one(plugin, src_channels, dst_channel, ttable, frames);
|
||||
return;
|
||||
}
|
||||
|
||||
dst_channel->enabled = 1;
|
||||
zero = zero_labels[data->sum_type];
|
||||
get = get_u_labels[data->get];
|
||||
add = add_labels[data->sum_type * 2 + ttable->att];
|
||||
norm = norm_labels[data->sum_type * 8 + ttable->att * 4 + 4 - data->src_sample_size];
|
||||
put_u32 = put_u32_labels[data->put];
|
||||
dst = dst_channel->area.addr + dst_channel->area.first / 8;
|
||||
dst_step = dst_channel->area.step / 8;
|
||||
|
||||
while (frames-- > 0) {
|
||||
ttable_src_t *ttp = src_tt;
|
||||
sum_t sum;
|
||||
|
||||
/* Zero sum */
|
||||
goto *zero;
|
||||
zero_int32:
|
||||
sum.as_uint32 = 0;
|
||||
goto zero_end;
|
||||
zero_int64:
|
||||
sum.as_uint64 = 0;
|
||||
goto zero_end;
|
||||
#if ROUTE_PLUGIN_USE_FLOAT
|
||||
zero_float:
|
||||
sum.as_float = 0.0;
|
||||
goto zero_end;
|
||||
#endif
|
||||
zero_end:
|
||||
for (srcidx = 0; srcidx < nsrcs; ++srcidx) {
|
||||
char *src = srcs[srcidx];
|
||||
|
||||
/* Get sample */
|
||||
goto *get;
|
||||
#define GET_U_END after_get
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_U_END
|
||||
after_get:
|
||||
|
||||
/* Sum */
|
||||
goto *add;
|
||||
add_int32_att:
|
||||
sum.as_uint32 += sample * ttp->as_int;
|
||||
goto after_sum;
|
||||
add_int32_noatt:
|
||||
if (ttp->as_int)
|
||||
sum.as_uint32 += sample;
|
||||
goto after_sum;
|
||||
add_int64_att:
|
||||
sum.as_uint64 += (u_int64_t) sample * ttp->as_int;
|
||||
goto after_sum;
|
||||
add_int64_noatt:
|
||||
if (ttp->as_int)
|
||||
sum.as_uint64 += sample;
|
||||
goto after_sum;
|
||||
#if ROUTE_PLUGIN_USE_FLOAT
|
||||
add_float_att:
|
||||
sum.as_float += sample * ttp->as_float;
|
||||
goto after_sum;
|
||||
add_float_noatt:
|
||||
if (ttp->as_int)
|
||||
sum.as_float += sample;
|
||||
goto after_sum;
|
||||
#endif
|
||||
after_sum:
|
||||
srcs[srcidx] += src_steps[srcidx];
|
||||
ttp++;
|
||||
}
|
||||
|
||||
/* Normalization */
|
||||
goto *norm;
|
||||
norm_int32_8_att:
|
||||
sum.as_uint64 = sum.as_uint32;
|
||||
norm_int64_8_att:
|
||||
sum.as_uint64 <<= 8;
|
||||
norm_int64_0_att:
|
||||
div(sum.as_uint64);
|
||||
goto norm_int;
|
||||
|
||||
norm_int32_16_att:
|
||||
sum.as_uint64 = sum.as_uint32;
|
||||
norm_int64_16_att:
|
||||
sum.as_uint64 <<= 16;
|
||||
div(sum.as_uint64);
|
||||
goto norm_int;
|
||||
|
||||
norm_int32_24_att:
|
||||
sum.as_uint64 = sum.as_uint32;
|
||||
norm_int64_24_att:
|
||||
sum.as_uint64 <<= 24;
|
||||
div(sum.as_uint64);
|
||||
goto norm_int;
|
||||
|
||||
norm_int32_8_noatt:
|
||||
sum.as_uint64 = sum.as_uint32;
|
||||
norm_int64_8_noatt:
|
||||
sum.as_uint64 <<= 8;
|
||||
goto norm_int;
|
||||
|
||||
norm_int32_16_noatt:
|
||||
sum.as_uint64 = sum.as_uint32;
|
||||
norm_int64_16_noatt:
|
||||
sum.as_uint64 <<= 16;
|
||||
goto norm_int;
|
||||
|
||||
norm_int32_24_noatt:
|
||||
sum.as_uint64 = sum.as_uint32;
|
||||
norm_int64_24_noatt:
|
||||
sum.as_uint64 <<= 24;
|
||||
goto norm_int;
|
||||
|
||||
norm_int64_0_noatt:
|
||||
norm_int:
|
||||
if (sum.as_uint64 > (u_int32_t)0xffffffff)
|
||||
sample = (u_int32_t)0xffffffff;
|
||||
else
|
||||
sample = sum.as_uint64;
|
||||
goto after_norm;
|
||||
|
||||
#if ROUTE_PLUGIN_USE_FLOAT
|
||||
norm_float_8:
|
||||
sum.as_float *= 1 << 8;
|
||||
goto norm_float;
|
||||
norm_float_16:
|
||||
sum.as_float *= 1 << 16;
|
||||
goto norm_float;
|
||||
norm_float_24:
|
||||
sum.as_float *= 1 << 24;
|
||||
goto norm_float;
|
||||
norm_float_0:
|
||||
norm_float:
|
||||
sum.as_float = floor(sum.as_float + 0.5);
|
||||
if (sum.as_float > (u_int32_t)0xffffffff)
|
||||
sample = (u_int32_t)0xffffffff;
|
||||
else
|
||||
sample = sum.as_float;
|
||||
goto after_norm;
|
||||
#endif
|
||||
after_norm:
|
||||
|
||||
/* Put sample */
|
||||
goto *put_u32;
|
||||
#define PUT_U32_END after_put_u32
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_U32_END
|
||||
after_put_u32:
|
||||
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
|
||||
int route_src_channels_mask(snd_pcm_plugin_t *plugin,
|
||||
bitset_t *dst_vmask,
|
||||
bitset_t **src_vmask)
|
||||
{
|
||||
route_t *data = (route_t *)plugin->extra_data;
|
||||
int schannels = plugin->src_format.channels;
|
||||
int dchannels = plugin->dst_format.channels;
|
||||
bitset_t *vmask = plugin->src_vmask;
|
||||
int channel;
|
||||
ttable_dst_t *dp = data->ttable;
|
||||
bitset_zero(vmask, schannels);
|
||||
for (channel = 0; channel < dchannels; channel++, dp++) {
|
||||
unsigned int src;
|
||||
ttable_src_t *sp;
|
||||
if (!bitset_get(dst_vmask, channel))
|
||||
continue;
|
||||
sp = dp->srcs;
|
||||
for (src = 0; src < dp->nsrcs; src++, sp++)
|
||||
bitset_set(vmask, sp->channel);
|
||||
}
|
||||
*src_vmask = vmask;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int route_dst_channels_mask(snd_pcm_plugin_t *plugin,
|
||||
bitset_t *src_vmask,
|
||||
bitset_t **dst_vmask)
|
||||
{
|
||||
route_t *data = (route_t *)plugin->extra_data;
|
||||
int dchannels = plugin->dst_format.channels;
|
||||
bitset_t *vmask = plugin->dst_vmask;
|
||||
int channel;
|
||||
ttable_dst_t *dp = data->ttable;
|
||||
bitset_zero(vmask, dchannels);
|
||||
for (channel = 0; channel < dchannels; channel++, dp++) {
|
||||
unsigned int src;
|
||||
ttable_src_t *sp;
|
||||
sp = dp->srcs;
|
||||
for (src = 0; src < dp->nsrcs; src++, sp++) {
|
||||
if (bitset_get(src_vmask, sp->channel)) {
|
||||
bitset_set(vmask, channel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*dst_vmask = vmask;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void route_free(snd_pcm_plugin_t *plugin)
|
||||
{
|
||||
route_t *data = (route_t *)plugin->extra_data;
|
||||
unsigned int dst_channel;
|
||||
for (dst_channel = 0; dst_channel < plugin->dst_format.channels; ++dst_channel) {
|
||||
if (data->ttable[dst_channel].srcs != NULL)
|
||||
free(data->ttable[dst_channel].srcs);
|
||||
}
|
||||
}
|
||||
|
||||
static int route_load_ttable(snd_pcm_plugin_t *plugin,
|
||||
const route_ttable_entry_t* src_ttable)
|
||||
{
|
||||
route_t *data;
|
||||
unsigned int src_channel, dst_channel;
|
||||
const route_ttable_entry_t *sptr;
|
||||
ttable_dst_t *dptr;
|
||||
if (src_ttable == NULL)
|
||||
return 0;
|
||||
data = (route_t *)plugin->extra_data;
|
||||
dptr = data->ttable;
|
||||
sptr = src_ttable;
|
||||
plugin->private_free = route_free;
|
||||
for (dst_channel = 0; dst_channel < plugin->dst_format.channels; ++dst_channel) {
|
||||
route_ttable_entry_t t = 0;
|
||||
int att = 0;
|
||||
int nsrcs = 0;
|
||||
ttable_src_t srcs[plugin->src_format.channels];
|
||||
for (src_channel = 0; src_channel < plugin->src_format.channels; ++src_channel) {
|
||||
assert(*sptr >= 0 && *sptr <= FULL);
|
||||
if (*sptr != 0) {
|
||||
srcs[nsrcs].channel = src_channel;
|
||||
#if ROUTE_PLUGIN_USE_FLOAT
|
||||
/* Also in user space for non attenuated */
|
||||
srcs[nsrcs].as_int = (*sptr == FULL ? ROUTE_PLUGIN_RESOLUTION : 0);
|
||||
srcs[nsrcs].as_float = *sptr;
|
||||
#else
|
||||
srcs[nsrcs].as_int = *sptr;
|
||||
#endif
|
||||
if (*sptr != FULL)
|
||||
att = 1;
|
||||
t += *sptr;
|
||||
nsrcs++;
|
||||
}
|
||||
sptr++;
|
||||
}
|
||||
#if 0
|
||||
assert(t <= FULL);
|
||||
#endif
|
||||
dptr->att = att;
|
||||
dptr->nsrcs = nsrcs;
|
||||
switch (nsrcs) {
|
||||
case 0:
|
||||
dptr->func = route_to_channel_from_zero;
|
||||
break;
|
||||
case 1:
|
||||
dptr->func = route_to_channel_from_one;
|
||||
break;
|
||||
default:
|
||||
dptr->func = route_to_channel;
|
||||
break;
|
||||
}
|
||||
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_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
size_t frames)
|
||||
{
|
||||
route_t *data;
|
||||
int src_nchannels, dst_nchannels;
|
||||
int src_channel, dst_channel;
|
||||
ttable_dst_t *ttp;
|
||||
snd_pcm_plugin_channel_t *dvp;
|
||||
|
||||
assert(plugin && src_channels && dst_channels);
|
||||
if (frames == 0)
|
||||
return 0;
|
||||
data = (route_t *)plugin->extra_data;
|
||||
|
||||
src_nchannels = plugin->src_format.channels;
|
||||
for (src_channel = 0; src_channel < src_nchannels; ++src_channel) {
|
||||
assert(src_channels[src_channel].area.first % 8 == 0 &&
|
||||
src_channels[src_channel].area.step % 8 == 0);
|
||||
}
|
||||
|
||||
dst_nchannels = plugin->dst_format.channels;
|
||||
for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) {
|
||||
assert(dst_channels[dst_channel].area.first % 8 == 0 &&
|
||||
dst_channels[dst_channel].area.step % 8 == 0);
|
||||
}
|
||||
|
||||
ttp = data->ttable;
|
||||
dvp = dst_channels;
|
||||
for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) {
|
||||
ttp->func(plugin, src_channels, dvp, ttp, frames);
|
||||
dvp++;
|
||||
ttp++;
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
int getput_index(int format)
|
||||
{
|
||||
int sign, width, endian;
|
||||
sign = !snd_pcm_format_signed(format);
|
||||
width = snd_pcm_format_width(format) / 8 - 1;
|
||||
#ifdef SND_LITTLE_ENDIAN
|
||||
endian = snd_pcm_format_big_endian(format);
|
||||
#else
|
||||
endian = snd_pcm_format_little_endian(format);
|
||||
#endif
|
||||
if (endian < 0)
|
||||
endian = 0;
|
||||
return width * 4 + endian * 2 + sign;
|
||||
}
|
||||
|
||||
#ifndef __KERNEL__
|
||||
static void route_dump(snd_pcm_plugin_t *plugin, FILE *fp)
|
||||
{
|
||||
route_t *data;
|
||||
ttable_dst_t *tdp;
|
||||
unsigned int dst_channel, dst_nchannels;
|
||||
data = (route_t *)plugin->extra_data;
|
||||
tdp = data->ttable;
|
||||
dst_nchannels = plugin->dst_format.channels;
|
||||
for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) {
|
||||
unsigned int k;
|
||||
ttable_src_t *tsp = tdp->srcs;
|
||||
fprintf(fp, "Channel %d = ", dst_channel);
|
||||
for (k = 0; k < tdp->nsrcs; ++k) {
|
||||
if (k > 0)
|
||||
fprintf(fp, " + ");
|
||||
fprintf(fp, "[%d]", tsp->channel);
|
||||
if (tdp->att) {
|
||||
#if ROUTE_PLUGIN_USE_FLOAT
|
||||
fprintf(fp, "*%g", tsp->as_float);
|
||||
#else
|
||||
fprintf(fp, "*%d/%d", tsp->as_int, ROUTE_PLUGIN_RESOLUTION);
|
||||
#endif
|
||||
}
|
||||
++tsp;
|
||||
}
|
||||
fprintf(fp, "\n");
|
||||
++tdp;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int snd_pcm_plugin_build_route(snd_pcm_plug_t *plug,
|
||||
snd_pcm_format_t *src_format,
|
||||
snd_pcm_format_t *dst_format,
|
||||
route_ttable_entry_t *ttable,
|
||||
snd_pcm_plugin_t **r_plugin)
|
||||
{
|
||||
route_t *data;
|
||||
snd_pcm_plugin_t *plugin;
|
||||
int err;
|
||||
|
||||
assert(r_plugin);
|
||||
*r_plugin = NULL;
|
||||
assert(src_format->rate == dst_format->rate);
|
||||
assert(snd_pcm_format_linear(src_format->format) &&
|
||||
snd_pcm_format_linear(dst_format->format));
|
||||
|
||||
err = snd_pcm_plugin_build(plug, "attenuated route conversion",
|
||||
src_format, dst_format,
|
||||
sizeof(route_t) + sizeof(data->ttable[0]) * dst_format->channels,
|
||||
&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->conv = conv_index(src_format->format, dst_format->format);
|
||||
|
||||
#if ROUTE_PLUGIN_USE_FLOAT
|
||||
data->sum_type = FLOAT;
|
||||
#else
|
||||
if (snd_pcm_format_width(src_format->format) == 32)
|
||||
data->sum_type = UINT64;
|
||||
else
|
||||
data->sum_type = UINT32;
|
||||
#endif
|
||||
data->src_sample_size = snd_pcm_format_width(src_format->format) / 8;
|
||||
|
||||
if ((err = route_load_ttable(plugin, ttable)) < 0) {
|
||||
snd_pcm_plugin_free(plugin);
|
||||
return err;
|
||||
}
|
||||
plugin->transfer = route_transfer;
|
||||
plugin->src_channels_mask = route_src_channels_mask;
|
||||
plugin->dst_channels_mask = route_dst_channels_mask;
|
||||
#ifndef __KERNEL__
|
||||
plugin->dump = route_dump;
|
||||
#endif
|
||||
*r_plugin = plugin;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -86,7 +86,7 @@ int snd_rawmidi_close(snd_rawmidi_t *rmidi)
|
|||
return res;
|
||||
}
|
||||
|
||||
int snd_rawmidi_file_descriptor(snd_rawmidi_t *rmidi)
|
||||
int snd_rawmidi_poll_descriptor(snd_rawmidi_t *rmidi)
|
||||
{
|
||||
if (!rmidi)
|
||||
return -EINVAL;
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ int snd_seq_close(snd_seq_t *seq)
|
|||
/*
|
||||
* returns the file descriptor of the client
|
||||
*/
|
||||
int snd_seq_file_descriptor(snd_seq_t *seq)
|
||||
int snd_seq_poll_descriptor(snd_seq_t *seq)
|
||||
{
|
||||
if (!seq)
|
||||
return -EINVAL;
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ int snd_timer_close(snd_timer_t *handle)
|
|||
return res;
|
||||
}
|
||||
|
||||
int snd_timer_file_descriptor(snd_timer_t *handle)
|
||||
int snd_timer_poll_descriptor(snd_timer_t *handle)
|
||||
{
|
||||
snd_timer_t *tmr;
|
||||
|
||||
|
|
|
|||
|
|
@ -47,11 +47,11 @@ int setparams(snd_pcm_t *phandle, snd_pcm_t *chandle, int *bufsize)
|
|||
params.mode = SND_PCM_MODE_FRAME;
|
||||
#endif
|
||||
params.format.interleave = 1;
|
||||
params.format.format = SND_PCM_SFMT_S16_LE;
|
||||
params.format.sfmt = SND_PCM_SFMT_S16_LE;
|
||||
params.format.channels = 2;
|
||||
params.format.rate = USED_RATE;
|
||||
params.start_mode = SND_PCM_START_GO;
|
||||
params.xrun_mode = SND_PCM_XRUN_DRAIN;
|
||||
params.start_mode = SND_PCM_START_EXPLICIT;
|
||||
params.xrun_action = SND_PCM_XRUN_ACT_DRAIN;
|
||||
params.time = 1;
|
||||
*bufsize += 4;
|
||||
|
||||
|
|
@ -152,7 +152,7 @@ long readbuf(snd_pcm_t *handle, char *buf, long len, size_t *frames)
|
|||
long r;
|
||||
|
||||
do {
|
||||
r = snd_pcm_read(handle, buf, len);
|
||||
r = snd_pcm_readi(handle, buf, len);
|
||||
} while (r == -EAGAIN);
|
||||
if (r > 0)
|
||||
*frames += r;
|
||||
|
|
@ -166,7 +166,7 @@ long writebuf(snd_pcm_t *handle, char *buf, long len, size_t *frames)
|
|||
long r;
|
||||
|
||||
while (len > 0) {
|
||||
r = snd_pcm_write(handle, buf, len);
|
||||
r = snd_pcm_writei(handle, buf, len);
|
||||
if (r == -EAGAIN)
|
||||
continue;
|
||||
// printf("write = %li\n", r);
|
||||
|
|
@ -225,7 +225,7 @@ int main(void)
|
|||
break;
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_go(phandle)) < 0) {
|
||||
if ((err = snd_pcm_start(phandle)) < 0) {
|
||||
printf("Go error: %s\n", snd_strerror(err));
|
||||
exit(0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ static void show_format1(const char *prefix, snd_pcm_format_t *format)
|
|||
format->interleave ? 1 : 0,
|
||||
format->rate,
|
||||
format->voices,
|
||||
format->format);
|
||||
format->sfmt);
|
||||
}
|
||||
|
||||
static void show_format(snd_pcm_loopback_t *handle)
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ int main(void)
|
|||
fprintf(stderr, "open failed: %s\n", snd_strerror(err));
|
||||
return 0;
|
||||
}
|
||||
format.format = SND_PCM_SFMT_MU_LAW;
|
||||
format.sfmt = SND_PCM_SFMT_MU_LAW;
|
||||
format.rate = 8000;
|
||||
format.channels = 1;
|
||||
if ((err = snd_pcm_playback_format(handle, &format)) < 0) {
|
||||
|
|
@ -82,7 +82,7 @@ int main(void)
|
|||
}
|
||||
count1 = status.fragment_size * 12;
|
||||
show_playback_status(handle);
|
||||
size = snd_pcm_write(handle, buffer1, count1);
|
||||
size = snd_pcm_writei(handle, buffer1, count1);
|
||||
sleep(2);
|
||||
show_playback_status(handle);
|
||||
printf("Pause.. Bytes written %i from %i...\n", size, count1);
|
||||
|
|
@ -94,7 +94,7 @@ int main(void)
|
|||
printf("Pause end..\n");
|
||||
snd_pcm_playback_pause(handle, 0);
|
||||
show_playback_status(handle);
|
||||
size = snd_pcm_write(handle, buffer1, count);
|
||||
size = snd_pcm_writei(handle, buffer1, count);
|
||||
printf("Pause end.. Bytes written %i from %i...\n", size, count);
|
||||
snd_pcm_close(handle);
|
||||
free(buffer);
|
||||
|
|
|
|||
14
test/pcm.c
14
test/pcm.c
|
|
@ -18,7 +18,7 @@ void setformat(void *phandle, void *rhandle)
|
|||
snd_pcm_format_t format;
|
||||
|
||||
bzero(&format, sizeof(format));
|
||||
format.format = SND_PCM_SFMT_S16_LE;
|
||||
format.sfmt = SND_PCM_SFMT_S16_LE;
|
||||
format.channels = 2;
|
||||
format.rate = 22050;
|
||||
if ((err = snd_pcm_playback_format(phandle, &format)) < 0) {
|
||||
|
|
@ -46,14 +46,14 @@ void method1(void)
|
|||
setformat(phandle, rhandle);
|
||||
printf("Recording... ");
|
||||
fflush(stdout);
|
||||
if ((err = snd_pcm_read(rhandle, buffer, sizeof(buffer))) != sizeof(buffer)) {
|
||||
if ((err = snd_pcm_readi(rhandle, buffer, sizeof(buffer))) != sizeof(buffer)) {
|
||||
printf("Read error: %s\n", err < 0 ? snd_strerror(err) : xitoa(err));
|
||||
return;
|
||||
}
|
||||
printf("done...\n");
|
||||
printf("Playback... ");
|
||||
fflush(stdout);
|
||||
if ((err = snd_pcm_write(phandle, buffer, sizeof(buffer))) != sizeof(buffer)) {
|
||||
if ((err = snd_pcm_writei(phandle, buffer, sizeof(buffer))) != sizeof(buffer)) {
|
||||
printf("Write error: %s\n", err < 0 ? snd_strerror(err) : xitoa(err));
|
||||
return;
|
||||
}
|
||||
|
|
@ -81,7 +81,7 @@ void method2(void)
|
|||
setformat(phandle, rhandle);
|
||||
printf("Recording... ");
|
||||
fflush(stdout);
|
||||
if ((err = snd_pcm_read(rhandle, buffer, sizeof(buffer))) != sizeof(buffer)) {
|
||||
if ((err = snd_pcm_readi(rhandle, buffer, sizeof(buffer))) != sizeof(buffer)) {
|
||||
printf("Read error: %s\n", err < 0 ? snd_strerror(err) : xitoa(err));
|
||||
return;
|
||||
}
|
||||
|
|
@ -93,7 +93,7 @@ void method2(void)
|
|||
printf("Record flush done...\n");
|
||||
printf("Playback... ");
|
||||
fflush(stdout);
|
||||
if ((err = snd_pcm_write(phandle, buffer, sizeof(buffer))) != sizeof(buffer)) {
|
||||
if ((err = snd_pcm_writei(phandle, buffer, sizeof(buffer))) != sizeof(buffer)) {
|
||||
printf("Write error: %s\n", err < 0 ? snd_strerror(err) : xitoa(err));
|
||||
return;
|
||||
}
|
||||
|
|
@ -122,7 +122,7 @@ void method3(void)
|
|||
setformat(handle, handle);
|
||||
printf("Recording... ");
|
||||
fflush(stdout);
|
||||
if ((err = snd_pcm_read(handle, buffer, sizeof(buffer))) != sizeof(buffer)) {
|
||||
if ((err = snd_pcm_readi(handle, buffer, sizeof(buffer))) != sizeof(buffer)) {
|
||||
printf("Read error: %s\n", err < 0 ? snd_strerror(err) : xitoa(err));
|
||||
return;
|
||||
}
|
||||
|
|
@ -134,7 +134,7 @@ void method3(void)
|
|||
printf("Record flush done...\n");
|
||||
printf("Playback... ");
|
||||
fflush(stdout);
|
||||
if ((err = snd_pcm_write(handle, buffer, sizeof(buffer))) != sizeof(buffer)) {
|
||||
if ((err = snd_pcm_writei(handle, buffer, sizeof(buffer))) != sizeof(buffer)) {
|
||||
printf("Write error: %s\n", err < 0 ? snd_strerror(err) : xitoa(err));
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ static void write_ev(snd_seq_event_t *ev)
|
|||
while ((rc = snd_seq_event_output(seq_handle, ev)) < 0) {
|
||||
int seqfd;
|
||||
fd_set fds;
|
||||
seqfd = snd_seq_file_descriptor(seq_handle);
|
||||
seqfd = snd_seq_poll_descriptor(seq_handle);
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(seqfd, &fds);
|
||||
if ((rc = select(seqfd + 1, NULL, &fds, NULL, NULL)) < 0) {
|
||||
|
|
@ -366,7 +366,7 @@ static snd_seq_event_t *wait_for_event(void)
|
|||
input_event == NULL) {
|
||||
int seqfd;
|
||||
fd_set fds;
|
||||
seqfd = snd_seq_file_descriptor(seq_handle);
|
||||
seqfd = snd_seq_poll_descriptor(seq_handle);
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(seqfd, &fds);
|
||||
if ((left = select(seqfd + 1, &fds, NULL, NULL, NULL)) < 0) {
|
||||
|
|
|
|||
|
|
@ -481,7 +481,7 @@ void event_decoder(snd_seq_t *handle, int argc, char *argv[])
|
|||
|
||||
while (1) {
|
||||
FD_ZERO(&in);
|
||||
FD_SET(max = snd_seq_file_descriptor(handle), &in);
|
||||
FD_SET(max = snd_seq_poll_descriptor(handle), &in);
|
||||
if (select(max + 1, &in, NULL, NULL, NULL) < 0)
|
||||
break;
|
||||
do {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ void set_format(snd_pcm_t *phandle)
|
|||
snd_pcm_format_t format;
|
||||
|
||||
bzero(&format, sizeof(format));
|
||||
format.format = SND_PCM_SFMT_S16_LE;
|
||||
format.sfmt = SND_PCM_SFMT_S16_LE;
|
||||
format.channels = 2;
|
||||
format.rate = 44100;
|
||||
if ((err = snd_pcm_playback_format(phandle, &format)) < 0) {
|
||||
|
|
@ -225,31 +225,31 @@ void event_sender(snd_seq_t *handle, int argc, char *argv[])
|
|||
while (1) {
|
||||
FD_ZERO(&out);
|
||||
FD_ZERO(&in);
|
||||
max = snd_seq_file_descriptor(handle);
|
||||
FD_SET(snd_seq_file_descriptor(handle), &in);
|
||||
max = snd_seq_poll_descriptor(handle);
|
||||
FD_SET(snd_seq_poll_descriptor(handle), &in);
|
||||
if (snd_seq_event_output_pending(handle)) {
|
||||
FD_SET(snd_seq_file_descriptor(handle), &out);
|
||||
FD_SET(snd_seq_poll_descriptor(handle), &out);
|
||||
}
|
||||
#ifdef USE_PCM
|
||||
if (phandle) {
|
||||
if (snd_pcm_file_descriptor(phandle) > max)
|
||||
max = snd_pcm_file_descriptor(phandle);
|
||||
FD_SET(snd_pcm_file_descriptor(phandle), &out);
|
||||
if (snd_pcm_poll_descriptor(phandle) > max)
|
||||
max = snd_pcm_poll_descriptor(phandle);
|
||||
FD_SET(snd_pcm_poll_descriptor(phandle), &out);
|
||||
}
|
||||
#endif
|
||||
if (select(max + 1, &in, &out, NULL, NULL) < 0)
|
||||
break;
|
||||
#ifdef USE_PCM
|
||||
if (phandle && FD_ISSET(snd_pcm_file_descriptor(phandle), &out)) {
|
||||
if (snd_pcm_write(phandle, pbuf, pfragment_size) != pfragment_size) {
|
||||
if (phandle && FD_ISSET(snd_pcm_poll_descriptor(phandle), &out)) {
|
||||
if (snd_pcm_writei(phandle, pbuf, pfragment_size) != pfragment_size) {
|
||||
fprintf(stderr, "Playback write error!!\n");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (FD_ISSET(snd_seq_file_descriptor(handle), &out))
|
||||
if (FD_ISSET(snd_seq_poll_descriptor(handle), &out))
|
||||
snd_seq_flush_output(handle);
|
||||
if (FD_ISSET(snd_seq_file_descriptor(handle), &in)) {
|
||||
if (FD_ISSET(snd_seq_poll_descriptor(handle), &in)) {
|
||||
do {
|
||||
if ((err = snd_seq_event_input(handle, &ev))<0)
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ void read_loop(void *handle, int master_ticks, int timeout)
|
|||
|
||||
while (master_ticks-- > 0) {
|
||||
FD_ZERO(&in);
|
||||
max = snd_timer_file_descriptor(handle);
|
||||
max = snd_timer_poll_descriptor(handle);
|
||||
FD_SET(max, &in);
|
||||
tv.tv_sec = timeout;
|
||||
tv.tv_usec = 0;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue