diff options
Diffstat (limited to 'src/pcm.c')
-rw-r--r-- | src/pcm.c | 910 |
1 files changed, 910 insertions, 0 deletions
diff --git a/src/pcm.c b/src/pcm.c new file mode 100644 index 0000000..ad77c06 --- /dev/null +++ b/src/pcm.c @@ -0,0 +1,910 @@ +#include <alsa/asoundlib.h> +#include <sndio.h> +#include <poll.h> +#include <stdbool.h> +#include <assert.h> +#include "util/dsp.h" +#include "util/util.h" + +struct _snd_pcm_hw_params { + struct sio_cap cap; + struct sio_par par; + snd_pcm_format_t alsa_format; + snd_pcm_access_t access; + bool needs_conversion; // for unsupported formats +}; + +struct _snd_pcm_sw_params { char noop; }; + +struct _snd_pcm { + struct _snd_pcm_hw_params hw, hw_requested; + struct _snd_pcm_sw_params sw; + struct sio_hdl *hdl; + const char *name; + snd_pcm_uframes_t position, written, avail; + snd_pcm_stream_t stream; + int mode; + bool started; +}; + +static int +sndio_stream(snd_pcm_stream_t stream) +{ + switch (stream) { + case SND_PCM_STREAM_PLAYBACK: return SIO_PLAY; + case SND_PCM_STREAM_CAPTURE: return SIO_REC; + } + ERRX(EXIT_FAILURE, "unknown stream: %u", stream); +} + +static int +sndio_mode(int mode) +{ + switch (mode) { + case SND_PCM_NONBLOCK: WARNX1("SND_PCM_NONBLOCK"); return true; + // ASYNC: SIGIO will be emitted whenever a period has been completely processed by the soundcard. + case SND_PCM_ASYNC: ERRX1(EXIT_FAILURE, "SND_PCM_ASYNC is not supported"); + } + return false; +} + +static void +onmove(void *arg, int delta) +{ + snd_pcm_t *pcm = arg; + pcm->position += delta; + pcm->avail += delta; +} + +static struct sio_hdl* +device_open(snd_pcm_t *pcm, const char *name, snd_pcm_stream_t stream, int mode) +{ + const char *sndio_name = (!name || !strcmp(name, "default") ? SIO_DEVANY : name); + + struct sio_hdl *hdl; + if (!(hdl = sio_open(sndio_name, sndio_stream(stream), sndio_mode(mode))) && + !(hdl = sio_open(SIO_DEVANY, sndio_stream(stream), sndio_mode(mode)))) { + WARNX1("sio_open failed"); + return NULL; + } + + sio_onmove(hdl, onmove, pcm); + pcm->mode = mode; + return hdl; +} + +int +snd_pcm_open(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode) +{ + if (!(*pcm = calloc(1, sizeof(**pcm)))) { + WARN1("calloc"); + return -1; + } + + if (!((*pcm)->hdl = device_open(*pcm, name, stream, mode))) + goto fail; + + sio_initpar(&(*pcm)->hw_requested.par); + (*pcm)->name = (name ? name : "default"); + return (sio_getcap((*pcm)->hdl, &(*pcm)->hw.cap) && sio_getpar((*pcm)->hdl, &(*pcm)->hw.par) ? 0 : -1); + +fail: + free(*pcm); + return -1; +} + +int +snd_pcm_close(snd_pcm_t *pcm) +{ + sio_close(pcm->hdl); + free(pcm); + return 0; +} + +const char* +snd_pcm_name(snd_pcm_t *pcm) +{ + return pcm->name; +} + +int +snd_pcm_nonblock(snd_pcm_t *pcm, int nonblock) +{ + if ((pcm->mode == SND_PCM_NONBLOCK && nonblock) || (!pcm->mode && !nonblock)) + return 0; + + WARNX("snd_pcm_nonblock(%d)", nonblock); + snd_pcm_drain(pcm); + sio_close(pcm->hdl); + + if (!(pcm->hdl = device_open(pcm, pcm->name, pcm->stream, (nonblock ? SND_PCM_NONBLOCK : false)))) + return -1; + + return snd_pcm_hw_params(pcm, &pcm->hw_requested); +} + +int +snd_pcm_poll_descriptors_count(snd_pcm_t *pcm) +{ + return sio_nfds(pcm->hdl); +} + +int +snd_pcm_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space) +{ + if (space > (unsigned int)sio_nfds(pcm->hdl)) + return -1; + + return sio_pollfd(pcm->hdl, pfds, (pcm->stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN)); +} + +int +snd_pcm_poll_descriptors_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents) +{ + if (!revents || nfds > (unsigned int)sio_nfds(pcm->hdl)) + return -1; + + *revents = sio_revents(pcm->hdl, pfds); + return 0; +} + +snd_pcm_state_t +snd_pcm_state(snd_pcm_t *pcm) +{ + if (pcm->started) + return SND_PCM_STATE_RUNNING; + + return SND_PCM_STATE_OPEN; +} + +int +snd_pcm_wait(snd_pcm_t *pcm, int timeout) +{ + return 1; // we are always ready for io +} + +static size_t +io_do(snd_pcm_t *pcm, void *buffer, const size_t frames, size_t (*io)(struct sio_hdl*, void*, size_t)) +{ + if (pcm->hw.needs_conversion) { + struct aparams params = { + .bps = pcm->hw.par.bps, + .bits = pcm->hw.par.bits, + .le = pcm->hw.par.le, + .sig = pcm->hw.par.sig, + .msb = pcm->hw.par.msb + }; + + struct conv dec, enc; + dec_init(&dec, ¶ms, pcm->hw.par.pchan); + enc_init(&enc, ¶ms, pcm->hw.par.pchan); + + size_t total_frames = frames, io_bytes = 0; + unsigned char decoded[4096], encoded[sizeof(decoded)]; + const size_t max_frames = snd_pcm_bytes_to_frames(pcm, sizeof(decoded)); + for (unsigned char *p = buffer; total_frames > 0;) { + const int todo_frames = (total_frames > max_frames ? max_frames : total_frames); + total_frames -= todo_frames; + + // sadly can't function pointer here as some formats may need different parameters for decoder + if (pcm->hw.alsa_format == SND_PCM_FORMAT_FLOAT_LE || pcm->hw.alsa_format == SND_PCM_FORMAT_FLOAT_BE) { + dec_do_float(&dec, p, decoded, todo_frames); + } else { + dec_do(&dec, p, decoded, todo_frames); + } + + enc_do(&enc, decoded, encoded, todo_frames); + + const size_t todo_bytes = snd_pcm_frames_to_bytes(pcm, todo_frames); + io_bytes += io(pcm->hdl, encoded, todo_bytes); + p += todo_bytes; + } + + return io_bytes; + } + + return io(pcm->hdl, buffer, snd_pcm_frames_to_bytes(pcm, frames)); +} + +snd_pcm_sframes_t +snd_pcm_bytes_to_frames(snd_pcm_t *pcm, ssize_t bytes) +{ + const int bpf = (pcm->hw.par.bps * pcm->hw.par.pchan); + return bytes / bpf; +} + +ssize_t +snd_pcm_frames_to_bytes(snd_pcm_t *pcm, snd_pcm_sframes_t frames) +{ + const int bpf = (pcm->hw.par.bps * pcm->hw.par.pchan); + return frames * bpf; +} + +snd_pcm_sframes_t +snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size) +{ + const snd_pcm_sframes_t ret = snd_pcm_bytes_to_frames(pcm, io_do(pcm, (void*)buffer, size, (size_t(*)(struct sio_hdl*, void*, size_t))sio_write)); + pcm->written += ret; + pcm->avail -= ret; + return ret; +} + +snd_pcm_sframes_t +snd_pcm_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size) +{ + const snd_pcm_sframes_t ret = snd_pcm_bytes_to_frames(pcm, io_do(pcm, buffer, size, sio_read)); + pcm->written += ret; + pcm->avail -= ret; + return ret; +} + +snd_pcm_sframes_t +snd_pcm_avail_update(snd_pcm_t *pcm) +{ + struct pollfd pfd[16]; + int nfds = sio_nfds(pcm->hdl); + assert((unsigned int)nfds < ARRAY_SIZE(pfd)); + + nfds = sio_pollfd(pcm->hdl, pfd, (pcm->stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN)); + + // XXX: timeout should be period time/buffer time(?) + errno = 0; + while ((nfds = poll(pfd, nfds, -1)) < 0) { + if (errno == EINVAL) { + WARNX1("poll EINVAL"); + goto nodata; + } + } + + const int revents = sio_revents(pcm->hdl, pfd); + if (!(revents & POLLOUT) && !(revents & POLLIN)) + goto nodata; + + return pcm->avail; + +nodata: + // NOTE: returning 1, as some programs don't check the return value :/ (namely qwebengine) + // thus they SIGFPE by dividing by 0 or -1 + return 1; +} + +snd_pcm_sframes_t +snd_pcm_avail(snd_pcm_t *pcm) +{ + return pcm->avail; +} + +int +snd_pcm_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp) +{ + if (delayp) *delayp = pcm->written - pcm->position; + return 0; +} + +int +snd_pcm_prepare(snd_pcm_t *pcm) +{ + if (!pcm->started && sio_start(pcm->hdl)) { + pcm->started = true; + pcm->written = pcm->position = 0; + pcm->avail = pcm->hw.par.bufsz; + } + + return (pcm->started ? 0 : -1); +} + +int +snd_pcm_start(snd_pcm_t *pcm) +{ + return snd_pcm_prepare(pcm); +} + +int +snd_pcm_drain(snd_pcm_t *pcm) +{ + if (pcm->started && sio_stop(pcm->hdl)) { + pcm->started = false; + pcm->written = pcm->position = 0; + } + + return (!pcm->started ? 0 : -1); +} + +int +snd_pcm_drop(snd_pcm_t *pcm) +{ + // FIXME: not correct, we need to do emulation + return snd_pcm_drain(pcm); +} + +int +snd_pcm_resume(snd_pcm_t *pcm) +{ + // FIXME: not correct, we need to do emulation + return snd_pcm_drain(pcm); +} + +int +snd_pcm_pause(snd_pcm_t *pcm, int enable) +{ + // FIXME: not correct, we need to do emulation + return (enable ? snd_pcm_drain(pcm) : snd_pcm_start(pcm)); +} + +int +snd_pcm_reset(snd_pcm_t *pcm) +{ + return (!snd_pcm_drain(pcm) && !snd_pcm_prepare(pcm) ? 0 : -1); +} + +size_t +snd_pcm_hw_params_sizeof(void) +{ + return sizeof(snd_pcm_hw_params_t); +} + +void +snd_pcm_hw_params_copy(snd_pcm_hw_params_t *dst, const snd_pcm_hw_params_t *src) +{ + *dst = *src; +} + +static void +copy_important_params(struct sio_par *dst, const struct sio_par *src) +{ + assert(dst && src); + dst->rate = src->rate; + dst->pchan = src->pchan; + dst->rchan = src->rchan; + dst->appbufsz = src->appbufsz; + dst->round = src->round; +} + +int +snd_pcm_hw_params_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) +{ + params->cap = pcm->hw.cap; + sio_initpar(¶ms->par); + copy_important_params(¶ms->par, &pcm->hw.par); + WARNX("rate: %u, round: %u, appbufsz: %u pchan: %u", params->par.rate, params->par.round, params->par.appbufsz, params->par.pchan); + return 0; +} + +int +snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) +{ + if (memcmp(params, &pcm->hw, sizeof(*params))) { + snd_pcm_drain(pcm); + + pcm->hw_requested = *params; + if (!sio_setpar(pcm->hdl, ¶ms->par)) { + WARNX1("sio_setpar failed"); + return -1; + } + + pcm->hw = *params; + if (!sio_getpar(pcm->hdl, &pcm->hw.par)) { + WARNX1("sio_getpar failed"); + return -1; + } + } + + WARNX("rate: %u, round: %u, appbufsz: %u, bufsz: %u, pchan: %u", pcm->hw.par.rate, pcm->hw.par.round, pcm->hw.par.appbufsz, pcm->hw.par.bufsz, pcm->hw.par.pchan); + return snd_pcm_prepare(pcm); +} + +int +snd_pcm_hw_params_current(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) +{ + *params = pcm->hw; + return 0; +} + +int +snd_pcm_hw_params_set_access(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access) +{ + if (_access != SND_PCM_ACCESS_RW_INTERLEAVED) + goto fail; + + params->access = _access; + return 0; + +fail: + WARNX("access mode `0x%x` not supported yet", _access); + return -1; +} + +int +snd_pcm_hw_params_get_access(const snd_pcm_hw_params_t *params, snd_pcm_access_t *_access) +{ + if (_access) *_access = params->access; + return 0; +} + +const char* +snd_pcm_format_name(const snd_pcm_format_t format) +{ +#define NAME(x) case x: return #x + switch (format) { + NAME(SND_PCM_FORMAT_U8); + NAME(SND_PCM_FORMAT_S8); + NAME(SND_PCM_FORMAT_S16_LE); + NAME(SND_PCM_FORMAT_S16_BE); + NAME(SND_PCM_FORMAT_U16_LE); + NAME(SND_PCM_FORMAT_U16_BE); + NAME(SND_PCM_FORMAT_S24_LE); + NAME(SND_PCM_FORMAT_S24_BE); + NAME(SND_PCM_FORMAT_U24_LE); + NAME(SND_PCM_FORMAT_U24_BE); + NAME(SND_PCM_FORMAT_S32_LE); + NAME(SND_PCM_FORMAT_S32_BE); + NAME(SND_PCM_FORMAT_U32_LE); + NAME(SND_PCM_FORMAT_U32_BE); + NAME(SND_PCM_FORMAT_FLOAT_LE); + NAME(SND_PCM_FORMAT_FLOAT_BE); + default: + WARNX("unsupported format: 0x%x", format); + return "unsupported"; + } +#undef NAME +} + +static bool +pcm_format(const snd_pcm_format_t format, struct sio_par *par, bool *out_needs_conversion) +{ + switch (format) { + case SND_PCM_FORMAT_U8: + par->bits = 8; + par->sig = 0; + break; + case SND_PCM_FORMAT_S8: + par->bits = 8; + par->sig = 1; + break; + case SND_PCM_FORMAT_S16_LE: + par->bits = 16; + par->sig = 1; + par->le = 1; + break; + case SND_PCM_FORMAT_S16_BE: + par->bits = 16; + par->sig = 1; + par->le = 0; + break; + case SND_PCM_FORMAT_U16_LE: + par->bits = 16; + par->sig = 0; + par->le = 1; + break; + case SND_PCM_FORMAT_U16_BE: + par->bits = 16; + par->sig = 0; + par->le = 0; + break; + case SND_PCM_FORMAT_S24_LE: + par->bits = 24; + par->sig = 1; + par->le = 1; + break; + case SND_PCM_FORMAT_S24_BE: + par->bits = 24; + par->sig = 1; + par->le = 0; + break; + case SND_PCM_FORMAT_U24_LE: + par->bits = 24; + par->sig = 0; + par->le = 1; + break; + case SND_PCM_FORMAT_U24_BE: + par->bits = 24; + par->sig = 0; + par->le = 0; + break; + case SND_PCM_FORMAT_FLOAT_LE: + *out_needs_conversion = true; + /* fallthrough */ + case SND_PCM_FORMAT_S32_LE: + par->bits = 32; + par->sig = 1; + par->le = 1; + break; + case SND_PCM_FORMAT_FLOAT_BE: + *out_needs_conversion = true; + /* fallthrough */ + case SND_PCM_FORMAT_S32_BE: + par->bits = 32; + par->sig = 1; + par->le = 0; + break; + case SND_PCM_FORMAT_U32_LE: + par->bits = 32; + par->sig = 0; + par->le = 1; + break; + case SND_PCM_FORMAT_U32_BE: + par->bits = 32; + par->sig = 0; + par->le = 0; + break; + default: + WARNX("unsupported format: 0x%x", format); + return false; + } + return true; +} + +struct _snd_pcm_format_mask { + snd_pcm_format_t *fmts; + uint8_t nmemb; +}; + +size_t +snd_pcm_format_mask_sizeof(void) +{ + return sizeof(snd_pcm_format_mask_t); +} + +int +snd_pcm_hw_params_malloc(snd_pcm_hw_params_t **ptr) +{ + // OpenAL-soft uses this :( + *ptr = calloc(1, sizeof(**ptr)); + return (*ptr ? 0 : -1); +} + +void +snd_pcm_hw_params_free(snd_pcm_hw_params_t *obj) +{ + free(obj); +} + +void +snd_pcm_hw_params_get_format_mask(snd_pcm_hw_params_t *params, snd_pcm_format_mask_t *mask) +{ + static snd_pcm_format_t def_fmts[] = { + SND_PCM_FORMAT_U8, + SND_PCM_FORMAT_S8, + SND_PCM_FORMAT_S16_LE, + SND_PCM_FORMAT_S16_BE, + SND_PCM_FORMAT_U16_LE, + SND_PCM_FORMAT_U16_BE, + SND_PCM_FORMAT_S24_LE, + SND_PCM_FORMAT_S24_BE, + SND_PCM_FORMAT_U24_LE, + SND_PCM_FORMAT_U24_BE, + SND_PCM_FORMAT_S32_LE, + SND_PCM_FORMAT_S32_BE, + SND_PCM_FORMAT_U32_LE, + SND_PCM_FORMAT_U32_BE, + SND_PCM_FORMAT_FLOAT_LE, + SND_PCM_FORMAT_FLOAT_BE + }; + static snd_pcm_format_mask_t def_mask = { .fmts = def_fmts, .nmemb = ARRAY_SIZE(def_fmts) }; + if (mask) *mask = def_mask; +} + +int +snd_pcm_format_mask_test(const snd_pcm_format_mask_t *mask, snd_pcm_format_t val) +{ + for (uint8_t i = 0; i < mask->nmemb; ++i) { + if (mask->fmts[i] != val) + continue; + + return true; + } + return false; +} + +int +snd_pcm_hw_params_test_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val) +{ + snd_pcm_format_mask_t mask; + snd_pcm_hw_params_get_format_mask(params, &mask); + return (snd_pcm_format_mask_test(&mask, val) ? 0 : -1); +} + +int +snd_pcm_hw_params_set_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val) +{ + // FIXME: prob should check hw caps + if (!pcm_format(val, ¶ms->par, ¶ms->needs_conversion)) + return -1; + + WARNX1(snd_pcm_format_name(val)); + params->alsa_format = val; + params->par.bps = SIO_BPS(params->par.bits); + return 0; +} + +static int +update(snd_pcm_t *pcm, struct sio_par *par, void *curv, void *newv, const size_t size) +{ + if (!newv) + return 0; + + struct sio_par old = *par; + memcpy(curv, newv, size); + const bool was_started = pcm->started; + + if (was_started) + snd_pcm_drain(pcm); + + if (!sio_setpar(pcm->hdl, par)) { + WARNX1("sio_setpar failed"); + *par = old; + return 0; + } + + struct sio_par hpar; + if (sio_getpar(pcm->hdl, &hpar)) { + copy_important_params(par, &hpar); + } else { + WARNX1("sio_getpar failed"); + } + + memcpy(newv, curv, size); + + if (was_started) + snd_pcm_prepare(pcm); + + return 0; +} + +int +snd_pcm_hw_params_get_channels(const snd_pcm_hw_params_t *params, unsigned int *val) +{ + // FIXME: need to store stream info in params + if (val) *val = params->par.pchan; + return 0; +} + +int +snd_pcm_hw_params_set_channels_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val) +{ + if (val) { + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { + assert(sizeof(params->par.pchan) == sizeof(*val)); + return update(pcm, ¶ms->par, ¶ms->par.pchan, val, sizeof(*val)); + } else { + assert(sizeof(params->par.rchan) == sizeof(*val)); + return update(pcm, ¶ms->par, ¶ms->par.rchan, val, sizeof(*val)); + } + } + return 0; +} + +int +snd_pcm_hw_params_set_channels(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val) +{ + return snd_pcm_hw_params_set_channels_near(pcm, params, &val); +} + +int +snd_pcm_hw_params_get_channels_min(const snd_pcm_hw_params_t *params, unsigned int *val) +{ + // FIXME: need to store stream info in params + unsigned int min = (unsigned int)~0; + for (int i = 0; i < SIO_NCHAN; ++i) { + if (!(params->cap.confs[0].pchan & (1 << i))) + continue; + min = (params->cap.pchan[i] < min ? params->cap.pchan[i] : min); + } + if (val) *val = min; + return 0; +} + +int +snd_pcm_hw_params_get_channels_max(const snd_pcm_hw_params_t *params, unsigned int *val) +{ + // FIXME: need to store stream info in params + unsigned int max = 0; + for (int i = 0; i < SIO_NCHAN; ++i) { + if (!(params->cap.confs[0].pchan & (1 << i))) + continue; + max = (params->cap.pchan[i] > max ? params->cap.pchan[i] : max); + } + if (val) *val = max; + return 0; +} + +int +snd_pcm_hw_params_get_rate(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir) +{ + if (dir) *dir = 0; + if (val) *val = params->par.rate; + return 0; +} + +int +snd_pcm_hw_params_set_rate_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir) +{ + if (dir) *dir = 0; + assert(sizeof(params->par.rate) == sizeof(*val)); + return update(pcm, ¶ms->par, ¶ms->par.rate, val, sizeof(*val)); +} + +int +snd_pcm_hw_params_set_rate(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir) +{ + return snd_pcm_hw_params_set_rate_near(pcm, params, &val, &dir); +} + +int +snd_pcm_hw_params_get_rate_min(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir) +{ + unsigned int min = (unsigned int)~0; + for (int i = 0; i < SIO_NRATE; ++i) { + if (!(params->cap.confs[0].rate & (1 << i))) + continue; + min = (params->cap.rate[i] < min ? params->cap.rate[i] : min); + } + if (dir) *dir = 0; + if (val) *val = min; + return 0; +} + +int +snd_pcm_hw_params_get_rate_max(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir) +{ + unsigned int max = 0; + for (int i = 0; i < SIO_NRATE; ++i) { + if (!(params->cap.confs[0].rate & (1 << i))) + continue; + max = (params->cap.rate[i] > max ? params->cap.rate[i] : max); + } + if (dir) *dir = 0; + if (val) *val = max; + return 0; +} + +int +snd_pcm_hw_params_get_buffer_size(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val) +{ + if (val) *val = params->par.appbufsz; + return 0; +} + +int +snd_pcm_hw_params_set_buffer_size_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val) +{ + if (val) { + unsigned int newv = *val; + assert(sizeof(params->par.appbufsz) == sizeof(newv)); + const int ret = update(pcm, ¶ms->par, ¶ms->par.appbufsz, &newv, sizeof(newv)); + *val = newv; + return ret; + } + return 0; +} + +int +snd_pcm_hw_params_get_buffer_size_min(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val) +{ + if (val) *val = params->par.appbufsz; + return 0; +} + +int +snd_pcm_hw_params_get_buffer_size_max(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val) +{ + if (val) *val = params->par.appbufsz; + return 0; +} + +int +snd_pcm_hw_params_get_period_size(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir) +{ + if (dir) *dir = 0; + if (val) *val = params->par.round; + return 0; +} + +int +snd_pcm_hw_params_set_period_size_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir) +{ + if (dir) *dir = 0; + assert(sizeof(params->par.round) == sizeof(*val)); + return update(pcm, ¶ms->par, ¶ms->par.round, val, sizeof(*val)); +} + +int +snd_pcm_hw_params_get_period_size_min(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir) +{ + if (dir) *dir = 0; + if (val) *val = params->par.round; + return 0; +} + +int +snd_pcm_hw_params_get_period_size_max(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir) +{ + if (dir) *dir = 0; + if (val) *val = params->par.round; + return 0; +} + +int +snd_pcm_hw_params_get_periods(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir) +{ + if (dir) *dir = 0; + if (val) *val = params->par.appbufsz / params->par.round; + return 0; +} + +int +snd_pcm_hw_params_set_periods_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir) +{ + if (dir) *dir = 0; + if (val) { + unsigned int round = params->par.appbufsz / *val; + assert(sizeof(params->par.round) == sizeof(round)); + return update(pcm, ¶ms->par, ¶ms->par.round, &round, sizeof(*val)); + } + return 0; +} + +size_t +snd_pcm_sw_params_sizeof(void) +{ + return sizeof(snd_pcm_sw_params_t); +} + +int +snd_pcm_sw_params_malloc(snd_pcm_sw_params_t **ptr) +{ + // OpenAL-soft uses this :( + *ptr = calloc(1, sizeof(**ptr)); + return (*ptr ? 0 : -1); +} + +void +snd_pcm_sw_params_free(snd_pcm_sw_params_t *ptr) +{ + free(ptr); +} + +int +snd_pcm_sw_params_current(snd_pcm_t *pcm, snd_pcm_sw_params_t *params) +{ + *params = pcm->sw; + return 0; +} + +int +snd_pcm_set_params(snd_pcm_t *pcm, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int channels, unsigned int rate, int soft_resample, unsigned int latency) +{ + snd_pcm_hw_params_t params; + return (!snd_pcm_hw_params_any(pcm, ¶ms) && !snd_pcm_hw_params_set_format(pcm, ¶ms, format) && + !snd_pcm_hw_params_set_access(pcm, ¶ms, access) && !snd_pcm_hw_params_set_channels(pcm, ¶ms, channels) && + !snd_pcm_hw_params_set_rate(pcm, ¶ms, rate, 0) && !snd_pcm_hw_params(pcm, ¶ms) ? 0 : -1); +} + +int +snd_pcm_get_params(snd_pcm_t *pcm, snd_pcm_uframes_t *buffer_size, snd_pcm_uframes_t *period_size) +{ + if (buffer_size) *buffer_size = pcm->hw.par.appbufsz; + if (period_size) *period_size = pcm->hw.par.round; + return 0; +} + +snd_pcm_chmap_t* +snd_pcm_get_chmap(snd_pcm_t *pcm) +{ + const unsigned int nc = (pcm->stream == SND_PCM_STREAM_PLAYBACK ? pcm->hw.par.pchan : pcm->hw.par.rchan); + + snd_pcm_chmap_t *map; + if (!(map = calloc(1, sizeof(*map) + nc))) + return NULL; + + map->channels = nc; + + if (nc == 1) { + map->pos[0] = SND_CHMAP_MONO; + } else if (nc == 2) { + map->pos[0] = SND_CHMAP_FL; + map->pos[1] = SND_CHMAP_FR; + } + + return map; +} |