diff options
author | Alfred E. Heggestad <aeh@db.org> | 2014-02-09 11:50:07 +0100 |
---|---|---|
committer | Alfred E. Heggestad <aeh@db.org> | 2014-02-09 11:50:07 +0100 |
commit | 98bf08bdcf2edd9d397f32650a8bfe62186fbecf (patch) | |
tree | ebc6ec71f44bff8c42e4eefced61948623df02fc /modules/oss | |
parent | e6ad5cf4401b860ba402d4b7b3c7c254bc87a019 (diff) |
baresip 0.4.10
Diffstat (limited to 'modules/oss')
-rw-r--r-- | modules/oss/module.mk | 18 | ||||
-rw-r--r-- | modules/oss/oss.c | 353 |
2 files changed, 371 insertions, 0 deletions
diff --git a/modules/oss/module.mk b/modules/oss/module.mk new file mode 100644 index 0000000..dd4b09a --- /dev/null +++ b/modules/oss/module.mk @@ -0,0 +1,18 @@ +# +# module.mk +# +# Copyright (C) 2010 Creytiv.com +# + +MOD := oss +$(MOD)_SRCS += oss.c +$(MOD)_LFLAGS += + +ifeq ($(OS), openbsd) +$(MOD)_LFLAGS += -lossaudio +endif +ifeq ($(OS), netbsd) +$(MOD)_LFLAGS += -lossaudio +endif + +include mk/mod.mk diff --git a/modules/oss/oss.c b/modules/oss/oss.c new file mode 100644 index 0000000..3acd9df --- /dev/null +++ b/modules/oss/oss.c @@ -0,0 +1,353 @@ +/** + * @file oss.c Open Sound System (OSS) driver + * + * Copyright (C) 2010 Creytiv.com + */ +#include <re.h> +#include <rem.h> +#include <baresip.h> +#include <string.h> +#include <unistd.h> +#include <pthread.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#if defined(NETBSD) || defined(OPENBSD) +#include <soundcard.h> +#elif defined (LINUX) +#include <linux/soundcard.h> +#else +#include <sys/soundcard.h> +#endif + + +struct ausrc_st { + struct ausrc *as; /* inheritance */ + int fd; + struct mbuf *mb; + ausrc_read_h *rh; + ausrc_error_h *errh; + void *arg; +}; + +struct auplay_st { + struct auplay *ap; /* inheritance */ + pthread_t thread; + bool run; + int fd; + uint8_t *buf; + uint32_t sz; + auplay_write_h *wh; + void *arg; +}; + + +static struct ausrc *ausrc; +static struct auplay *auplay; +static char oss_dev[64] = "/dev/dsp"; + + +/* + * Automatically calculate the fragment size depending on sampling rate + * and number of channels. More entries can be added to the table below. + * + * NOTE. Powermac 8200 and linux 2.4.18 gives: + * SNDCTL_DSP_SETFRAGMENT: Invalid argument + */ +static int set_fragment(int fd, uint32_t sampc) +{ + static const struct { + uint16_t max; + uint16_t size; + } fragv[] = { + {10, 7}, /* 10 x 2^7 = 1280 = 4 x 320 */ + {15, 7}, /* 15 x 2^7 = 1920 = 6 x 320 */ + {20, 7}, /* 20 x 2^7 = 2560 = 8 x 320 */ + {25, 7}, /* 25 x 2^7 = 3200 = 10 x 320 */ + {15, 8}, /* 15 x 2^8 = 3840 = 12 x 320 */ + {20, 8}, /* 20 x 2^8 = 5120 = 16 x 320 */ + {25, 8} /* 25 x 2^8 = 6400 = 20 x 320 */ + }; + size_t i; + const uint32_t buf_size = 2 * sampc; + + for (i=0; i<ARRAY_SIZE(fragv); i++) { + const uint16_t frag_max = fragv[i].max; + const uint16_t frag_size = fragv[i].size; + const uint32_t fragment_size = frag_max * (1<<frag_size); + + if (0 == (fragment_size%buf_size)) { + int fragment = (frag_max<<16) | frag_size; + + if (0 == ioctl(fd, SNDCTL_DSP_SETFRAGMENT, + &fragment)) { + return 0; + } + } + } + + return ENODEV; +} + + +static int oss_reset(int fd, uint32_t srate, uint8_t ch, int sampc, + int nonblock) +{ + int format = AFMT_S16_LE; + int speed = srate; + int channels = ch; + int blocksize = 0; + int err; + + err = set_fragment(fd, sampc); + if (err) + return err; + + if (0 != ioctl(fd, FIONBIO, &nonblock)) + return errno; + if (0 != ioctl(fd, SNDCTL_DSP_SETFMT, &format)) + return errno; + if (0 != ioctl(fd, SNDCTL_DSP_CHANNELS, &channels)) + return errno; + if (2 == channels) { + int stereo = 1; + if (0 != ioctl(fd, SNDCTL_DSP_STEREO, &stereo)) + return errno; + } + if (0 != ioctl(fd, SNDCTL_DSP_SPEED, &speed)) + return errno; + + (void)ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &blocksize); + + info("oss: init: %u bit %d Hz %d ch, blocksize=%d\n", + format, speed, channels, blocksize); + + return 0; +} + + +static void auplay_destructor(void *arg) +{ + struct auplay_st *st = arg; + + if (st->run) { + st->run = false; + pthread_join(st->thread, NULL); + } + + if (-1 != st->fd) { + fd_close(st->fd); + (void)close(st->fd); + } + + mem_deref(st->buf); + mem_deref(st->ap); +} + + +static void ausrc_destructor(void *arg) +{ + struct ausrc_st *st = arg; + + if (-1 != st->fd) { + fd_close(st->fd); + (void)close(st->fd); + } + + mem_deref(st->mb); + mem_deref(st->as); +} + + +static void read_handler(int flags, void *arg) +{ + struct ausrc_st *st = arg; + struct mbuf *mb = st->mb; + int n; + (void)flags; + + n = read(st->fd, mbuf_buf(mb), mbuf_get_space(mb)); + if (n <= 0) + return; + + mb->pos += n; + + if (mb->pos < mb->size) + return; + + st->rh(mb->buf, mb->size, st->arg); + + mb->pos = 0; +} + + +static void *play_thread(void *arg) +{ + struct auplay_st *st = arg; + int n; + + while (st->run) { + + st->wh(st->buf, st->sz, st->arg); + + n = write(st->fd, st->buf, st->sz); + if (n < 0) { + warning("oss: write: %m\n", errno); + break; + } + } + + return NULL; +} + + +static int src_alloc(struct ausrc_st **stp, struct ausrc *as, + struct media_ctx **ctx, + struct ausrc_prm *prm, const char *device, + ausrc_read_h *rh, ausrc_error_h *errh, void *arg) +{ + struct ausrc_st *st; + unsigned sampc; + int err; + + (void)ctx; + (void)errh; + + if (!stp || !as || !prm || !rh) + return EINVAL; + + st = mem_zalloc(sizeof(*st), ausrc_destructor); + if (!st) + return ENOMEM; + + st->fd = -1; + st->rh = rh; + st->errh = errh; + st->arg = arg; + + if (!device) + device = oss_dev; + + prm->fmt = AUFMT_S16LE; + + sampc = prm->srate * prm->ch * prm->ptime / 1000; + + st->mb = mbuf_alloc(2 * sampc); + if (!st->mb) { + err = ENOMEM; + goto out; + } + + st->fd = open(device, O_RDONLY); + if (st->fd < 0) { + err = errno; + goto out; + } + + err = fd_listen(st->fd, FD_READ, read_handler, st); + if (err) + goto out; + + err = oss_reset(st->fd, prm->srate, prm->ch, sampc, 1); + if (err) + goto out; + + st->as = mem_ref(as); + + out: + if (err) + mem_deref(st); + else + *stp = st; + + return err; +} + + +static int play_alloc(struct auplay_st **stp, struct auplay *ap, + struct auplay_prm *prm, const char *device, + auplay_write_h *wh, void *arg) +{ + struct auplay_st *st; + unsigned sampc; + int err; + + if (!stp || !ap || !prm || !wh) + return EINVAL; + + st = mem_zalloc(sizeof(*st), auplay_destructor); + if (!st) + return ENOMEM; + + st->fd = -1; + st->wh = wh; + st->arg = arg; + + if (!device) + device = oss_dev; + + prm->fmt = AUFMT_S16LE; + + sampc = prm->srate * prm->ch * prm->ptime / 1000; + + st->sz = 2 * sampc; + st->buf = mem_alloc(st->sz, NULL); + if (!st->buf) { + err = ENOMEM; + goto out; + } + + st->fd = open(device, O_WRONLY); + if (st->fd < 0) { + err = errno; + goto out; + } + + err = oss_reset(st->fd, prm->srate, prm->ch, sampc, 0); + if (err) + goto out; + + st->ap = mem_ref(ap); + + st->run = true; + err = pthread_create(&st->thread, NULL, play_thread, st); + if (err) { + st->run = false; + goto out; + } + + out: + if (err) + mem_deref(st); + else + *stp = st; + + return err; +} + + +static int module_init(void) +{ + int err; + + err = ausrc_register(&ausrc, "oss", src_alloc); + err |= auplay_register(&auplay, "oss", play_alloc); + + return err; +} + + +static int module_close(void) +{ + ausrc = mem_deref(ausrc); + auplay = mem_deref(auplay); + + return 0; +} + + +EXPORT_SYM const struct mod_export DECL_EXPORTS(oss) = { + "oss", + "audio", + module_init, + module_close, +}; |