[polypaudio-commits] r604 - in /trunk/src/modules: alsa-util.c alsa-util.h module-alsa-sink.c module-alsa-source.c

svnmailer-noreply at 0pointer.de svnmailer-noreply at 0pointer.de
Sun Feb 26 09:57:59 PST 2006


Author: ossman
Date: Sun Feb 26 18:57:58 2006
New Revision: 604

URL: http://0pointer.de/cgi-bin/viewcvs.cgi?rev=604&root=polypaudio&view=rev
Log:
Hardware volume support in ALSA modules.

Modified:
    trunk/src/modules/alsa-util.c
    trunk/src/modules/alsa-util.h
    trunk/src/modules/module-alsa-sink.c
    trunk/src/modules/module-alsa-source.c

Modified: trunk/src/modules/alsa-util.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/trunk/src/modules/alsa-util.c?rev=604&root=polypaudio&r1=603&r2=604&view=diff
==============================================================================
--- trunk/src/modules/alsa-util.c (original)
+++ trunk/src/modules/alsa-util.c Sun Feb 26 18:57:58 2006
@@ -128,3 +128,42 @@
         m->io_free(*ios);
     pa_xfree(io_events);
 }
+
+int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev) {
+    int err;
+
+    assert(mixer && dev);
+
+    if ((err = snd_mixer_attach(mixer, dev)) < 0) {
+        pa_log_warn(__FILE__": Unable to attach to mixer %s: %s", dev, snd_strerror(err));
+        return -1;
+    }
+
+    if ((err = snd_mixer_selem_register(mixer, NULL, NULL)) < 0) {
+        pa_log_warn(__FILE__": Unable to register mixer: %s", snd_strerror(err));
+        return -1;
+    }
+
+    if ((err = snd_mixer_load(mixer)) < 0) {
+        pa_log_warn(__FILE__": Unable to load mixer: %s", snd_strerror(err));
+        return -1;
+    }
+
+    return 0;
+}
+
+snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name) {
+    snd_mixer_elem_t *elem;
+    snd_mixer_selem_id_t *sid;
+    snd_mixer_selem_id_alloca(&sid);
+
+    assert(mixer && name);
+
+    snd_mixer_selem_id_set_name(sid, name);
+
+    elem = snd_mixer_find_selem(mixer, sid);
+    if (!elem)
+        pa_log_warn(__FILE__": Cannot find mixer control %s", snd_mixer_selem_id_get_name(sid));
+
+    return elem;
+}

Modified: trunk/src/modules/alsa-util.h
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/trunk/src/modules/alsa-util.h?rev=604&root=polypaudio&r1=603&r2=604&view=diff
==============================================================================
--- trunk/src/modules/alsa-util.h (original)
+++ trunk/src/modules/alsa-util.h Sun Feb 26 18:57:58 2006
@@ -32,4 +32,7 @@
 int pa_create_io_events(snd_pcm_t *pcm_handle, pa_mainloop_api *m, pa_io_event ***io_events, unsigned *n_io_events, void (*cb)(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata), void *userdata);
 void pa_free_io_events(pa_mainloop_api* m, pa_io_event **io_sources, unsigned n_io_sources);
 
+int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev);
+snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name);
+
 #endif

Modified: trunk/src/modules/module-alsa-sink.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/trunk/src/modules/module-alsa-sink.c?rev=604&root=polypaudio&r1=603&r2=604&view=diff
==============================================================================
--- trunk/src/modules/module-alsa-sink.c (original)
+++ trunk/src/modules/module-alsa-sink.c Sun Feb 26 18:57:58 2006
@@ -54,9 +54,12 @@
 
 struct userdata {
     snd_pcm_t *pcm_handle;
+    snd_mixer_t *mixer_handle;
+    snd_mixer_elem_t *mixer_elem;
     pa_sink *sink;
     pa_io_event **io_events;
     unsigned n_io_events;
+    long hw_volume_max, hw_volume_min;
 
     size_t frame_size, fragment_size;
     pa_memchunk memchunk, silence;
@@ -174,6 +177,110 @@
     return r;
 }
 
+static int sink_get_hw_volume_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    long vol;
+    int err;
+
+    assert(u && u->mixer_elem);
+
+    if (snd_mixer_selem_has_playback_volume_joined(u->mixer_elem)) {
+        err = snd_mixer_selem_get_playback_volume(u->mixer_elem, 0, &vol);
+        if (err < 0)
+            goto fail;
+        pa_cvolume_set(&s->hw_volume, s->hw_volume.channels,
+            (vol - u->hw_volume_min) * PA_VOLUME_NORM / (u->hw_volume_max - u->hw_volume_min));
+    } else {
+        int i;
+
+        for (i = 0;i < s->hw_volume.channels;i++) {
+            err = snd_mixer_selem_get_playback_volume(u->mixer_elem, i, &vol);
+            if (err < 0)
+                goto fail;
+            s->hw_volume.values[i] =
+                (vol - u->hw_volume_min) * PA_VOLUME_NORM / (u->hw_volume_max - u->hw_volume_min);
+        }
+    }
+
+    return 0;
+
+fail:
+    pa_log_error(__FILE__": Unable to read volume: %s", snd_strerror(err));
+    s->get_hw_volume = NULL;
+    s->set_hw_volume = NULL;
+    return -1;
+}
+
+static int sink_set_hw_volume_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    int err;
+    pa_volume_t vol;
+
+    assert(u && u->mixer_elem);
+
+    if (snd_mixer_selem_has_playback_volume_joined(u->mixer_elem)) {
+        vol = pa_cvolume_avg(&s->hw_volume) * (u->hw_volume_max - u->hw_volume_min) /
+            PA_VOLUME_NORM + u->hw_volume_min;
+        err = snd_mixer_selem_set_playback_volume_all(u->mixer_elem, vol);
+        if (err < 0)
+            goto fail;
+    } else {
+        int i;
+
+        for (i = 0;i < s->hw_volume.channels;i++) {
+            vol = s->hw_volume.values[i] * (u->hw_volume_max - u->hw_volume_min) /
+                PA_VOLUME_NORM + u->hw_volume_min;
+            err = snd_mixer_selem_set_playback_volume(u->mixer_elem, i, vol);
+            if (err < 0)
+                goto fail;
+        }
+    }
+
+    return 0;
+
+fail:
+    pa_log_error(__FILE__": Unable to set volume: %s", snd_strerror(err));
+    s->get_hw_volume = NULL;
+    s->set_hw_volume = NULL;
+    return -1;
+}
+
+static int sink_get_hw_mute_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    int err, sw;
+
+    assert(u && u->mixer_elem);
+
+    err = snd_mixer_selem_get_playback_switch(u->mixer_elem, 0, &sw);
+    if (err) {
+        pa_log_error(__FILE__": Unable to get switch: %s", snd_strerror(err));
+        s->get_hw_mute = NULL;
+        s->set_hw_mute = NULL;
+        return -1;
+    }
+
+    s->hw_muted = !sw;
+
+    return 0;
+}
+
+static int sink_set_hw_mute_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    int err;
+
+    assert(u && u->mixer_elem);
+
+    err = snd_mixer_selem_set_playback_switch_all(u->mixer_elem, !s->hw_muted);
+    if (err) {
+        pa_log_error(__FILE__": Unable to set switch: %s", snd_strerror(err));
+        s->get_hw_mute = NULL;
+        s->set_hw_mute = NULL;
+        return -1;
+    }
+
+    return 0;
+}
+
 int pa__init(pa_core *c, pa_module*m) {
     pa_modargs *ma = NULL;
     int ret = -1;
@@ -220,10 +327,34 @@
         goto fail;
     }
 
+    if ((err = snd_mixer_open(&u->mixer_handle, 0)) < 0) {
+        pa_log(__FILE__": Error opening mixer: %s", snd_strerror(err));
+        goto fail;
+    }
+
+    if ((pa_alsa_prepare_mixer(u->mixer_handle, dev) < 0) ||
+        !(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "PCM"))) {
+        snd_mixer_close(u->mixer_handle);
+        u->mixer_handle = NULL;
+    }
+
     u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL);
     assert(u->sink);
 
     u->sink->get_latency = sink_get_latency_cb;
+    if (u->mixer_handle) {
+        assert(u->mixer_elem);
+        if (snd_mixer_selem_has_playback_volume(u->mixer_elem)) {
+            u->sink->get_hw_volume = sink_get_hw_volume_cb;
+            u->sink->set_hw_volume = sink_set_hw_volume_cb;
+            snd_mixer_selem_get_playback_volume_range(
+                u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max);
+        }
+        if (snd_mixer_selem_has_playback_switch(u->mixer_elem)) {
+            u->sink->get_hw_mute = sink_get_hw_mute_cb;
+            u->sink->set_hw_mute = sink_set_hw_mute_cb;
+        }
+    }
     u->sink->userdata = u;
     pa_sink_set_owner(u->sink, m);
     u->sink->description = pa_sprintf_malloc("Advanced Linux Sound Architecture PCM on '%s'", dev);
@@ -251,6 +382,12 @@
 finish:
      if (ma)
          pa_modargs_free(ma);
+
+    /* Get initial mixer settings */
+    if (u->sink->get_hw_volume)
+        u->sink->get_hw_volume(u->sink);
+    if (u->sink->get_hw_mute)
+        u->sink->get_hw_mute(u->sink);
     
     return ret;
 
@@ -276,6 +413,9 @@
     
     if (u->io_events)
         pa_free_io_events(c->mainloop, u->io_events, u->n_io_events);
+    
+    if (u->mixer_handle)
+        snd_mixer_close(u->mixer_handle);
     
     if (u->pcm_handle) {
         snd_pcm_drop(u->pcm_handle);

Modified: trunk/src/modules/module-alsa-source.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/trunk/src/modules/module-alsa-source.c?rev=604&root=polypaudio&r1=603&r2=604&view=diff
==============================================================================
--- trunk/src/modules/module-alsa-source.c (original)
+++ trunk/src/modules/module-alsa-source.c Sun Feb 26 18:57:58 2006
@@ -54,9 +54,12 @@
 
 struct userdata {
     snd_pcm_t *pcm_handle;
+    snd_mixer_t *mixer_handle;
+    snd_mixer_elem_t *mixer_elem;
     pa_source *source;
     pa_io_event **io_events;
     unsigned n_io_events;
+    long hw_volume_max, hw_volume_min;
 
     size_t frame_size, fragment_size;
     pa_memchunk memchunk;
@@ -165,6 +168,110 @@
     return pa_bytes_to_usec(frames * u->frame_size, &s->sample_spec);
 }
 
+static int source_get_hw_volume_cb(pa_source *s) {
+    struct userdata *u = s->userdata;
+    long vol;
+    int err;
+
+    assert(u && u->mixer_elem);
+
+    if (snd_mixer_selem_has_capture_volume_joined(u->mixer_elem)) {
+        err = snd_mixer_selem_get_capture_volume(u->mixer_elem, 0, &vol);
+        if (err < 0)
+            goto fail;
+        pa_cvolume_set(&s->hw_volume, s->hw_volume.channels,
+            (vol - u->hw_volume_min) * PA_VOLUME_NORM / (u->hw_volume_max - u->hw_volume_min));
+    } else {
+        int i;
+
+        for (i = 0;i < s->hw_volume.channels;i++) {
+            err = snd_mixer_selem_get_capture_volume(u->mixer_elem, i, &vol);
+            if (err < 0)
+                goto fail;
+            s->hw_volume.values[i] =
+                (vol - u->hw_volume_min) * PA_VOLUME_NORM / (u->hw_volume_max - u->hw_volume_min);
+        }
+    }
+
+    return 0;
+
+fail:
+    pa_log_error(__FILE__": Unable to read volume: %s", snd_strerror(err));
+    s->get_hw_volume = NULL;
+    s->set_hw_volume = NULL;
+    return -1;
+}
+
+static int source_set_hw_volume_cb(pa_source *s) {
+    struct userdata *u = s->userdata;
+    int err;
+    pa_volume_t vol;
+
+    assert(u && u->mixer_elem);
+
+    if (snd_mixer_selem_has_capture_volume_joined(u->mixer_elem)) {
+        vol = pa_cvolume_avg(&s->hw_volume) * (u->hw_volume_max - u->hw_volume_min) /
+            PA_VOLUME_NORM + u->hw_volume_min;
+        err = snd_mixer_selem_set_capture_volume_all(u->mixer_elem, vol);
+        if (err < 0)
+            goto fail;
+    } else {
+        int i;
+
+        for (i = 0;i < s->hw_volume.channels;i++) {
+            vol = s->hw_volume.values[i] * (u->hw_volume_max - u->hw_volume_min) /
+                PA_VOLUME_NORM + u->hw_volume_min;
+            err = snd_mixer_selem_set_capture_volume(u->mixer_elem, i, vol);
+            if (err < 0)
+                goto fail;
+        }
+    }
+
+    return 0;
+
+fail:
+    pa_log_error(__FILE__": Unable to set volume: %s", snd_strerror(err));
+    s->get_hw_volume = NULL;
+    s->set_hw_volume = NULL;
+    return -1;
+}
+
+static int source_get_hw_mute_cb(pa_source *s) {
+    struct userdata *u = s->userdata;
+    int err, sw;
+
+    assert(u && u->mixer_elem);
+
+    err = snd_mixer_selem_get_capture_switch(u->mixer_elem, 0, &sw);
+    if (err) {
+        pa_log_error(__FILE__": Unable to get switch: %s", snd_strerror(err));
+        s->get_hw_mute = NULL;
+        s->set_hw_mute = NULL;
+        return -1;
+    }
+
+    s->hw_muted = !sw;
+
+    return 0;
+}
+
+static int source_set_hw_mute_cb(pa_source *s) {
+    struct userdata *u = s->userdata;
+    int err;
+
+    assert(u && u->mixer_elem);
+
+    err = snd_mixer_selem_set_capture_switch_all(u->mixer_elem, !s->hw_muted);
+    if (err) {
+        pa_log_error(__FILE__": Unable to set switch: %s", snd_strerror(err));
+        s->get_hw_mute = NULL;
+        s->set_hw_mute = NULL;
+        return -1;
+    }
+
+    return 0;
+}
+
 int pa__init(pa_core *c, pa_module*m) {
     pa_modargs *ma = NULL;
     int ret = -1;
@@ -211,11 +318,35 @@
         goto fail;
     }
 
+    if ((err = snd_mixer_open(&u->mixer_handle, 0)) < 0) {
+        pa_log(__FILE__": Error opening mixer: %s", snd_strerror(err));
+        goto fail;
+    }
+
+    if ((pa_alsa_prepare_mixer(u->mixer_handle, dev) < 0) ||
+        !(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Capture"))) {
+        snd_mixer_close(u->mixer_handle);
+        u->mixer_handle = NULL;
+    }
+
     u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL);
     assert(u->source);
 
     u->source->userdata = u;
     u->source->get_latency = source_get_latency_cb;
+    if (u->mixer_handle) {
+        assert(u->mixer_elem);
+        if (snd_mixer_selem_has_capture_volume(u->mixer_elem)) {
+            u->source->get_hw_volume = source_get_hw_volume_cb;
+            u->source->set_hw_volume = source_set_hw_volume_cb;
+            snd_mixer_selem_get_capture_volume_range(
+                u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max);
+        }
+        if (snd_mixer_selem_has_capture_switch(u->mixer_elem)) {
+            u->source->get_hw_mute = source_get_hw_mute_cb;
+            u->source->set_hw_mute = source_set_hw_mute_cb;
+        }
+    }
     pa_source_set_owner(u->source, m);
     u->source->description = pa_sprintf_malloc("Advanced Linux Sound Architecture PCM on '%s'", dev);
 
@@ -239,6 +370,12 @@
 finish:
      if (ma)
          pa_modargs_free(ma);
+
+    /* Get initial mixer settings */
+    if (u->source->get_hw_volume)
+        u->source->get_hw_volume(u->source);
+    if (u->source->get_hw_mute)
+        u->source->get_hw_mute(u->source);
     
     return ret;
 
@@ -264,6 +401,9 @@
     
     if (u->io_events)
         pa_free_io_events(c->mainloop, u->io_events, u->n_io_events);
+
+    if (u->mixer_handle)
+        snd_mixer_close(u->mixer_handle);
     
     if (u->pcm_handle) {
         snd_pcm_drop(u->pcm_handle);




More information about the pulseaudio-commits mailing list