diff options
| author | Lennart Poettering <lennart@poettering.net> | 2004-07-16 17:03:11 +0000 | 
|---|---|---|
| committer | Lennart Poettering <lennart@poettering.net> | 2004-07-16 17:03:11 +0000 | 
| commit | 74bbf31c37b327be3c52c8043be72b96b8fee8f4 (patch) | |
| tree | 136ab7a1ef6db68fca3e5b7ac3db9089f339f173 | |
| parent | f2e08d53d03303376da4491559a5af6296bbfea8 (diff) | |
implement alsa source
split off alsa-util.c
git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@76 fefdeb5f-60dc-0310-8127-8f9354f1896f
| -rw-r--r-- | src/Makefile.am | 14 | ||||
| -rw-r--r-- | src/alsa-util.c | 73 | ||||
| -rw-r--r-- | src/alsa-util.h | 14 | ||||
| -rw-r--r-- | src/module-alsa-sink.c | 77 | ||||
| -rw-r--r-- | src/module-alsa-source.c | 212 | ||||
| -rwxr-xr-x | src/polypaudio.run | 16 | ||||
| -rw-r--r-- | src/todo | 5 | 
7 files changed, 344 insertions, 67 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 53134792..09df6284 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,6 +26,7 @@ pkglib_LTLIBRARIES=libiochannel.la \  		libpstream.la \  		libpacket.la \  		liboss-util.la \ +		libalsa-util.la \  		libioline.la \  		libcli.la \  		libprotocol-cli.la \ @@ -42,6 +43,7 @@ pkglib_LTLIBRARIES=libiochannel.la \  		module-cli-protocol-unix.la \  		module-pipe-sink.la \  		module-alsa-sink.la \ +		module-alsa-source.la \  		module-oss.la \  		module-oss-mmap.la \  		module-simple-protocol-tcp.la \ @@ -128,6 +130,11 @@ libpacket_la_LDFLAGS = -avoid-version  liboss_util_la_SOURCES = oss-util.c oss-util.h  liboss_util_la_LDFLAGS = -avoid-version +libalsa_util_la_SOURCES = alsa-util.c alsa-util.h +libalsa_util_la_LDFLAGS = -avoid-version +libalsa_util_la_LIBADD = $(ASOUNDLIB_LIBS) +libalsa_util_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS) +  libioline_la_SOURCES = ioline.c ioline.h  libioline_la_LDFLAGS = -avoid-version  libioline_la_LIBADD = libiochannel.la @@ -203,9 +210,14 @@ module_pipe_sink_la_LIBADD = libiochannel.la  module_alsa_sink_la_SOURCES = module-alsa-sink.c  module_alsa_sink_la_LDFLAGS = -module -avoid-version -module_alsa_sink_la_LIBADD = $(ASOUNDLIB_LIBS) +module_alsa_sink_la_LIBADD = $(ASOUNDLIB_LIBS) libalsa-util.la  module_alsa_sink_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS) +module_alsa_source_la_SOURCES = module-alsa-source.c +module_alsa_source_la_LDFLAGS = -module -avoid-version +module_alsa_source_la_LIBADD = $(ASOUNDLIB_LIBS) libalsa-util.la +module_alsa_source_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS) +  module_oss_la_SOURCES = module-oss.c  module_oss_la_LDFLAGS = -module -avoid-version  module_oss_la_LIBADD = libiochannel.la liboss-util.la diff --git a/src/alsa-util.c b/src/alsa-util.c new file mode 100644 index 00000000..edfd1613 --- /dev/null +++ b/src/alsa-util.c @@ -0,0 +1,73 @@ +#include <asoundlib.h> + +#include "alsa-util.h" +#include "sample.h" + +int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, struct pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *buffer_size) { +    int ret = 0; +    snd_pcm_hw_params_t *hwparams = NULL; +    static const snd_pcm_format_t format_trans[] = { +        [PA_SAMPLE_U8] = SND_PCM_FORMAT_U8, +        [PA_SAMPLE_ALAW] = SND_PCM_FORMAT_A_LAW, +        [PA_SAMPLE_ULAW] = SND_PCM_FORMAT_MU_LAW, +        [PA_SAMPLE_S16LE] = SND_PCM_FORMAT_S16_LE, +        [PA_SAMPLE_S16BE] = SND_PCM_FORMAT_S16_BE, +        [PA_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE, +        [PA_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE, +    }; + +    if (snd_pcm_hw_params_malloc(&hwparams) < 0 || +        snd_pcm_hw_params_any(pcm_handle, hwparams) < 0 || +        snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0 || +        snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[ss->format]) < 0 || +        snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &ss->rate, NULL) < 0 || +        snd_pcm_hw_params_set_channels(pcm_handle, hwparams, ss->channels) < 0 || +        snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, periods, NULL) < 0 ||  +        snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, buffer_size) < 0 ||  +        snd_pcm_hw_params(pcm_handle, hwparams) < 0) { +        ret = -1; +    } + +    if (hwparams) +        snd_pcm_hw_params_free(hwparams); +    return ret; +} + +int pa_create_io_sources(snd_pcm_t *pcm_handle, struct pa_mainloop_api* m, void ***io_sources, unsigned *n_io_sources, void (*cb)(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata), void *userdata) { +    unsigned i; +    struct pollfd *pfds, *ppfd; +    void **ios; +    assert(pcm_handle && m && io_sources && n_io_sources && cb); + +    *n_io_sources = snd_pcm_poll_descriptors_count(pcm_handle); + +    pfds = malloc(sizeof(struct pollfd) * *n_io_sources); +    assert(pfds); +    if (snd_pcm_poll_descriptors(pcm_handle, pfds, *n_io_sources) < 0) { +        free(pfds); +        return -1; +    } +     +    *io_sources = malloc(sizeof(void*) * *n_io_sources); +    assert(io_sources); + +    for (i = 0, ios = *io_sources, ppfd = pfds; i < *n_io_sources; i++, ios++, ppfd++) { +        *ios = m->source_io(m, ppfd->fd, +                            ((ppfd->events & POLLIN) ? PA_MAINLOOP_API_IO_EVENT_INPUT : 0) | +                            ((ppfd->events & POLLOUT) ? PA_MAINLOOP_API_IO_EVENT_OUTPUT : 0), cb, userdata); +        assert(*ios); +    } + +    free(pfds); +    return 0; +} + +void pa_free_io_sources(struct pa_mainloop_api* m, void **io_sources, unsigned n_io_sources) { +    unsigned i; +    void **ios; +    assert(m && io_sources); +     +    for (ios = io_sources, i = 0; i < n_io_sources; i++, ios++) +        m->cancel_io(m, *ios); +    free(io_sources); +} diff --git a/src/alsa-util.h b/src/alsa-util.h new file mode 100644 index 00000000..4468c9d8 --- /dev/null +++ b/src/alsa-util.h @@ -0,0 +1,14 @@ +#ifndef fooalsautilhfoo +#define fooalsautilhfoo + +#include <asoundlib.h> + +#include "sample.h" +#include "mainloop-api.h" + +int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, struct pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *buffer_size); + +int pa_create_io_sources(snd_pcm_t *pcm_handle, struct pa_mainloop_api *m, void ***io_sources, unsigned *n_io_sources, void (*cb)(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata), void *userdata); +void pa_free_io_sources(struct pa_mainloop_api* m, void **io_sources, unsigned n_io_sources); + +#endif diff --git a/src/module-alsa-sink.c b/src/module-alsa-sink.c index 16571a2c..a0b1e548 100644 --- a/src/module-alsa-sink.c +++ b/src/module-alsa-sink.c @@ -11,6 +11,7 @@  #include "modargs.h"  #include "util.h"  #include "sample-util.h" +#include "alsa-util.h"  struct userdata {      snd_pcm_t *pcm_handle; @@ -39,7 +40,7 @@ static const char* const valid_modargs[] = {  static void xrun_recovery(struct userdata *u) {      assert(u); -    fprintf(stderr, "*** XRUN ***\n"); +    fprintf(stderr, "*** ALSA-XRUN (playback) ***\n");      if (snd_pcm_prepare(u->pcm_handle) < 0)          fprintf(stderr, "snd_pcm_prepare() failed\n"); @@ -63,13 +64,10 @@ static void do_write(struct userdata *u) {          assert(memchunk->memblock && memchunk->memblock->data && memchunk->length && memchunk->memblock->length && (memchunk->length % u->frame_size) == 0); -        assert(u->pcm_handle); -        assert(memchunk->memblock); -        assert(memchunk->memblock->data); -        assert(memchunk->length); -        assert(u->frame_size); -                  if ((frames = snd_pcm_writei(u->pcm_handle, memchunk->memblock->data + memchunk->index, memchunk->length / u->frame_size)) < 0) { +            if (frames == -EAGAIN) +                return; +              if (frames == -EPIPE) {                  xrun_recovery(u);                  continue; @@ -80,8 +78,9 @@ static void do_write(struct userdata *u) {          }          if (memchunk == &u->memchunk) { -            memchunk->index += frames * u->frame_size; -            memchunk->length -= frames * u->frame_size; +            size_t l = frames * u->frame_size; +            memchunk->index += l; +            memchunk->length -= l;              if (memchunk->length == 0) {                  pa_memblock_unref(memchunk->memblock); @@ -125,23 +124,11 @@ int pa_module_init(struct pa_core *c, struct pa_module*m) {      struct pa_modargs *ma = NULL;      int ret = -1;      struct userdata *u = NULL; -    snd_pcm_hw_params_t *hwparams;      const char *dev; -    struct pollfd *pfds, *ppfd;      struct pa_sample_spec ss; -    unsigned i, periods, fragsize; +    unsigned periods, fragsize;      snd_pcm_uframes_t buffer_size; -    void ** ios;      size_t frame_size; -    static const snd_pcm_format_t format_trans[] = { -        [PA_SAMPLE_U8] = SND_PCM_FORMAT_U8, -        [PA_SAMPLE_ALAW] = SND_PCM_FORMAT_A_LAW, -        [PA_SAMPLE_ULAW] = SND_PCM_FORMAT_MU_LAW, -        [PA_SAMPLE_S16LE] = SND_PCM_FORMAT_S16_LE, -        [PA_SAMPLE_S16BE] = SND_PCM_FORMAT_S16_BE, -        [PA_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE, -        [PA_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE, -    };      if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {          fprintf(stderr, __FILE__": failed to parse module arguments\n"); @@ -169,20 +156,12 @@ int pa_module_init(struct pa_core *c, struct pa_module*m) {      m->userdata = u;      if (snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) { -        fprintf(stderr, "Error opening PCM device %s\n", dev); +        fprintf(stderr, __FILE__": Error opening PCM device %s\n", dev);          goto fail;      } -    snd_pcm_hw_params_alloca(&hwparams); -    if (snd_pcm_hw_params_any(u->pcm_handle, hwparams) < 0 || -        snd_pcm_hw_params_set_access(u->pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0 || -        snd_pcm_hw_params_set_format(u->pcm_handle, hwparams, format_trans[ss.format]) < 0 || -        snd_pcm_hw_params_set_rate_near(u->pcm_handle, hwparams, &ss.rate, NULL) < 0 || -        snd_pcm_hw_params_set_channels(u->pcm_handle, hwparams, ss.channels) < 0 || -        snd_pcm_hw_params_set_periods_near(u->pcm_handle, hwparams, &periods, NULL) < 0 ||  -        snd_pcm_hw_params_set_buffer_size_near(u->pcm_handle, hwparams, &buffer_size) < 0 ||  -        snd_pcm_hw_params(u->pcm_handle, hwparams) < 0) { -        fprintf(stderr, "Error setting HW params.\n"); +    if (pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &buffer_size) < 0) { +        fprintf(stderr, __FILE__": Failed to set hardware parameters\n");          goto fail;      } @@ -194,28 +173,11 @@ int pa_module_init(struct pa_core *c, struct pa_module*m) {      pa_sink_set_owner(u->sink, m);      u->sink->description = pa_sprintf_malloc("Advanced Linux Sound Architecture PCM on '%s'", dev); -    u->n_io_sources = snd_pcm_poll_descriptors_count(u->pcm_handle); - -    pfds = malloc(sizeof(struct pollfd) * u->n_io_sources); -    assert(pfds); -    if (snd_pcm_poll_descriptors(u->pcm_handle, pfds, u->n_io_sources) < 0) { -        printf("Unable to obtain poll descriptors for playback.\n"); -        free(pfds); +    if (pa_create_io_sources(u->pcm_handle, c->mainloop, &u->io_sources, &u->n_io_sources, io_callback, u) < 0) { +        fprintf(stderr, __FILE__": failed to obtain file descriptors\n");          goto fail;      } -    u->io_sources = malloc(sizeof(void*) * u->n_io_sources); -    assert(u->io_sources); - -    for (i = 0, ios = u->io_sources, ppfd = pfds; i < u->n_io_sources; i++, ios++, ppfd++) { -        *ios = c->mainloop->source_io(c->mainloop, ppfd->fd, -                                      ((ppfd->events & POLLIN) ? PA_MAINLOOP_API_IO_EVENT_INPUT : 0) | -                                      ((ppfd->events & POLLOUT) ? PA_MAINLOOP_API_IO_EVENT_OUTPUT : 0), io_callback, u); -        assert(*ios); -    } - -    free(pfds); -      u->frame_size = frame_size;      u->fragment_size = buffer_size*u->frame_size/periods; @@ -250,14 +212,11 @@ void pa_module_done(struct pa_core *c, struct pa_module*m) {      assert(c && m);      if ((u = m->userdata)) { -        unsigned i; -        void **ios; -         -        pa_sink_free(u->sink); +        if (u->sink) +            pa_sink_free(u->sink); -        for (ios = u->io_sources, i = 0; i < u->n_io_sources; i++, ios++) -            c->mainloop->cancel_io(c->mainloop, *ios); -        free(u->io_sources); +        if (u->io_sources) +            pa_free_io_sources(c->mainloop, u->io_sources, u->n_io_sources);          if (u->pcm_handle) {              snd_pcm_drop(u->pcm_handle); diff --git a/src/module-alsa-source.c b/src/module-alsa-source.c new file mode 100644 index 00000000..cc45254c --- /dev/null +++ b/src/module-alsa-source.c @@ -0,0 +1,212 @@ +#include <assert.h> +#include <stdio.h> +#include <sys/poll.h> + +#include <asoundlib.h> + +#include "module.h" +#include "core.h" +#include "memchunk.h" +#include "sink.h" +#include "modargs.h" +#include "util.h" +#include "sample-util.h" +#include "alsa-util.h" + +struct userdata { +    snd_pcm_t *pcm_handle; +    struct pa_source *source; +    void **io_sources; +    unsigned n_io_sources; + +    size_t frame_size, fragment_size; +    struct pa_memchunk memchunk; +}; + +static const char* const valid_modargs[] = { +    "device", +    "source_name", +    "format", +    "channels", +    "rate", +    "fragments", +    "fragment_size", +    NULL +}; + +#define DEFAULT_SOURCE_NAME "alsa_input" +#define DEFAULT_DEVICE "hw:0,0" + +static void xrun_recovery(struct userdata *u) { +    assert(u); + +    fprintf(stderr, "*** ALSA-XRUN (capture) ***\n"); +     +    if (snd_pcm_prepare(u->pcm_handle) < 0) +        fprintf(stderr, "snd_pcm_prepare() failed\n"); +} + +static void do_read(struct userdata *u) { +    assert(u); + +    for (;;) { +        struct pa_memchunk post_memchunk; +        snd_pcm_sframes_t frames; +        size_t l; +         +        if (!u->memchunk.memblock) { +            u->memchunk.memblock = pa_memblock_new(u->memchunk.length = u->fragment_size); +            u->memchunk.index = 0; +        } +             +        assert(u->memchunk.memblock && u->memchunk.memblock->data && u->memchunk.length && u->memchunk.memblock->length && (u->memchunk.length % u->frame_size) == 0); + +        if ((frames = snd_pcm_readi(u->pcm_handle, u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length / u->frame_size)) < 0) { +            if (frames == -EAGAIN) +                return; +             +            if (frames == -EPIPE) { +                xrun_recovery(u); +                continue; +            } + +            fprintf(stderr, "snd_pcm_readi() failed: %s\n", strerror(-frames)); +            return; +        } + +        l = frames * u->frame_size; +         +        post_memchunk = u->memchunk; +        post_memchunk.length = l; + +        pa_source_post(u->source, &post_memchunk); + +        u->memchunk.index += l; +        u->memchunk.length -= l; +         +        if (u->memchunk.length == 0) { +            pa_memblock_unref(u->memchunk.memblock); +            u->memchunk.memblock = NULL; +            u->memchunk.index = u->memchunk.length = 0; +        } +         +        break; +    } +} + +static void io_callback(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) { +    struct userdata *u = userdata; +    assert(u && a && id); + +    if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN) +        xrun_recovery(u); + +    do_read(u); +} + +int pa_module_init(struct pa_core *c, struct pa_module*m) { +    struct pa_modargs *ma = NULL; +    int ret = -1; +    struct userdata *u = NULL; +    const char *dev; +    struct pa_sample_spec ss; +    unsigned periods, fragsize; +    snd_pcm_uframes_t buffer_size; +    size_t frame_size; +     +    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { +        fprintf(stderr, __FILE__": failed to parse module arguments\n"); +        goto fail; +    } + +    ss = c->default_sample_spec; +    if (pa_modargs_get_sample_spec(ma, &ss) < 0) { +        fprintf(stderr, __FILE__": failed to parse sample specification\n"); +        goto fail; +    } +    frame_size = pa_sample_size(&ss); +     +    periods = 12; +    fragsize = 1024; +    if (pa_modargs_get_value_u32(ma, "fragments", &periods) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &fragsize) < 0) { +        fprintf(stderr, __FILE__": failed to parse buffer metrics\n"); +        goto fail; +    } +    buffer_size = fragsize/frame_size*periods; +     +    u = malloc(sizeof(struct userdata)); +    assert(u); +    memset(u, 0, sizeof(struct userdata)); +    m->userdata = u; +     +    if (snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK) < 0) { +        fprintf(stderr, __FILE__": Error opening PCM device %s\n", dev); +        goto fail; +    } + +    if (pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &buffer_size) < 0) { +        fprintf(stderr, __FILE__": Failed to set hardware parameters\n"); +        goto fail; +    } + +    u->source = pa_source_new(c, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss); +    assert(u->source); + +    u->source->userdata = u; +    pa_source_set_owner(u->source, m); +    u->source->description = pa_sprintf_malloc("Advanced Linux Sound Architecture PCM on '%s'", dev); + +    if (pa_create_io_sources(u->pcm_handle, c->mainloop, &u->io_sources, &u->n_io_sources, io_callback, u) < 0) { +        fprintf(stderr, __FILE__": failed to obtain file descriptors\n"); +        goto fail; +    } + +    u->frame_size = frame_size; +    u->fragment_size = buffer_size*u->frame_size/periods; + +    fprintf(stderr, __FILE__": using %u fragments of size %u bytes.\n", periods, u->fragment_size); + +    u->memchunk.memblock = NULL; +    u->memchunk.index = u->memchunk.length = 0; + +    snd_pcm_start(u->pcm_handle); +     +    ret = 0; + +finish: +     if (ma) +         pa_modargs_free(ma); +     +    return ret; + +fail: +     +    if (u) +        pa_module_done(c, m); + +    goto finish; +} + +void pa_module_done(struct pa_core *c, struct pa_module*m) { +    struct userdata *u; +    assert(c && m); + +    if ((u = m->userdata)) { +        if (u->source) +            pa_source_free(u->source); + +        if (u->io_sources) +            pa_free_io_sources(c->mainloop, u->io_sources, u->n_io_sources); +         +        if (u->pcm_handle) { +            snd_pcm_drop(u->pcm_handle); +            snd_pcm_close(u->pcm_handle); +        } + +        if (u->memchunk.memblock) +            pa_memblock_unref(u->memchunk.memblock); +         +        free(u); +    } +} + diff --git a/src/polypaudio.run b/src/polypaudio.run index 48c3f7ea..2698c538 100755 --- a/src/polypaudio.run +++ b/src/polypaudio.run @@ -1,12 +1,20 @@  #!./polypaudio -F -# Load the CLI module -load module-cli +# Load audio drivers +load module-alsa-sink +load module-alsa-source device=plughw:1,0 +#load module-oss-mmap device="/dev/dsp" + +# Make some devices default +sink_default alsa_output +source_default alsa_input +# Load several protocols  load module-esound-protocol-tcp  load module-simple-protocol-tcp  load module-native-protocol-unix  load module-cli-protocol-unix -load module-alsa-sink -#load module-oss-mmap device="/dev/dsp" +# Load the CLI module +load module-cli + @@ -1,5 +1,4 @@ -- alsa source - +- make the sample spec configurable for oss devices  - prefix modules/libraries with pa_  - rename files  - svn-id and license in every file @@ -33,4 +32,4 @@ drivers:  - python  modules: -- http +- http?  | 
