Hello community,

here is the log from the commit of package apulse for openSUSE:Factory checked 
in at 2017-05-10 20:53:41
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/apulse (Old)
 and      /work/SRC/openSUSE:Factory/.apulse.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "apulse"

Wed May 10 20:53:41 2017 rev:7 rq:494104 version:0.1.10

Changes:
--------
--- /work/SRC/openSUSE:Factory/apulse/apulse.changes    2017-03-12 
20:04:13.163894268 +0100
+++ /work/SRC/openSUSE:Factory/.apulse.new/apulse.changes       2017-05-10 
20:53:42.983763620 +0200
@@ -1,0 +2,13 @@
+Tue May  9 16:45:47 UTC 2017 - [email protected]
+
+- Update to version 0.1.10 (changes since 0.1.8):
+  * Fix the PA_SAMPLE_S16NE volume scaling bug.
+  * Fix video stuttering in Firefox by calling write callback as
+    soon as minreq bytes become available in write buffer.
+  * Make error messages from do_connect_pcm friendlier.
+  * Per-stream volumes.
+  * Implement source and sink info introspection functions.
+  * Implement timed events.
+  * Fix buffer xruns by increasing buffer sizes.
+
+-------------------------------------------------------------------

Old:
----
  apulse-0.1.8.tar.gz

New:
----
  apulse-0.1.10.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ apulse.spec ++++++
--- /var/tmp/diff_new_pack.99Xrpk/_old  2017-05-10 20:53:44.143599952 +0200
+++ /var/tmp/diff_new_pack.99Xrpk/_new  2017-05-10 20:53:44.147599388 +0200
@@ -18,7 +18,7 @@
 
 %define __provides_exclude_from ^%{_libdir}/apulse/.*.so.*$
 Name:           apulse
-Version:        0.1.8
+Version:        0.1.10
 Release:        0
 Summary:        PulseAudio emulation for ALSA
 License:        MIT

++++++ apulse-0.1.8.tar.gz -> apulse-0.1.10.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apulse-0.1.8/README.md new/apulse-0.1.10/README.md
--- old/apulse-0.1.8/README.md  2017-03-08 17:38:10.000000000 +0100
+++ new/apulse-0.1.10/README.md 2017-04-18 17:55:19.000000000 +0200
@@ -3,9 +3,31 @@
 
 PulseAudio emulation for ALSA.
 
-Project is in stale state since its proclamation. The main objective,
-working Skype test call, is reached. I don't have any plans for further
-development.
+The program provides an alternative partial implementation of the PulseAudio
+API. It consists of a loader script and a number of shared libraries with the
+same names as from original PulseAudio, so applications could dynamically load
+them and think they are talking to PulseAudio. Internally, no separate sound
+mixing daemon is used. Instead, apulse relies on ALSA's `dmix`, `dsnoop`, and
+`plug` plugins to handle multiple sound sources and capture streams running at
+the same time. `dmix` plugin muxes multiple playback streams; `dsnoop` plugin
+allow multiple applications to capture from a single microphone; and `plug`
+plugin transparently converts audio between various sample formats, sample 
rates
+and channel numbers. For more than a decade now, ALSA comes with these plugins
+enabled and configured by default.
+
+`apulse` wasn't designed to be a drop-in replacement of PulseAudio. It's
+pointless, since that will be just reimplementation of original PulseAudio, 
with
+the same client-daemon architecture, required by the complete feature
+set. Instead, only parts of the API that are crucial to specific applications
+are implemented. That's why there is a loader script, named `apulse`. It 
updates
+value of `LD_LIBRARY_PATH` environment variable to point also to the directory
+where apulse's libraries are installed, making them available to the
+application.
+
+Name comes from names of both ALSA and PulseAudio. As `aoss` was a 
compatibility
+layer between OSS programs and ALSA, `apulse` was designed to be compatibility
+layer between PulseAudio applications and ALSA.
+
 
 Install
 =======
@@ -35,20 +57,40 @@
 If libraries are installed to a regular library path, you don't need run 
applications
 through `apulse` wrapper.
 
-Note you need to select build type to be `Release`, otherwise it will output 
enourmous
-amount of debug text to the stdout.
 
 Usage
 =====
 
 ```
-$ apulse <program-name> [parameters]
+$ apulse <program-name> [program-parameters]
 ```
 
 Environment variables `APULSE_CAPTURE_DEVICE` and `APULSE_PLAYBACK_DEVICE` can 
be used
 to configure capture and playback devices. Try `hw:0,0`, `plughw:0,0` and the 
like.
 Refer to the ALSA user guide for a full list of device names.
 
+System directory versus separate directory installations
+--------------------------------------------------------
+
+By default, libraries from `apulse` are installed into a separate directory, in
+order to hide them from all applications.
+
+Most applications in the wild, that support both PulseAudio and ALSA, try to
+autodetect which sound system is used. First, applications try to start with
+PulseAudio. Original client libraries fail early if no PulseAudio daemon is
+running or can be started. Then they switch to ALSA. Decision is made once, at
+the beginning. It works fine with PulseAudio, but doesn't work with
+`apulse`. Latter has no daemons, it happily says that everything is fine, and
+it's capable of playing audio. Applications then try to call more functions, 
and
+eventually touch unimplemented parts, often with crashes. So, libraries are
+hidden, and become visible only when a program is called through `apulse`
+wrapper script.
+
+It's possible to install apulse libraries to `/usr/lib`. Wrapper script won't
+be required, but then all applications will try to use PulseAudio API, despite
+they can use ALSA.
+
+
 License
 =======
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apulse-0.1.8/src/apulse-context.c 
new/apulse-0.1.10/src/apulse-context.c
--- old/apulse-0.1.8/src/apulse-context.c       2017-03-08 17:38:10.000000000 
+0100
+++ new/apulse-0.1.10/src/apulse-context.c      2017-04-18 17:55:19.000000000 
+0200
@@ -132,9 +132,27 @@
     return 8;   // PA headers say "8" is the protocol version used in 
PulseAudio 0.9
 }
 
-static void
-pa_context_get_sink_info_by_name_impl(pa_operation *op)
+static pa_sink_info
+pai_fill_default_sink_info(void)
 {
+    static pa_proplist *proplist = NULL;
+
+    if (!proplist) {
+        // TODO: free memory
+        proplist = pa_proplist_new();
+    }
+
+    static pa_sink_port_info sink_port = {
+        .name = "ALSA sink",
+        .description = "ALSA sink",
+        .priority = 1,
+        .available = PA_PORT_AVAILABLE_YES,
+    };
+
+    static pa_sink_port_info *sink_ports[] = {
+        &sink_port,
+    };
+
     // TODO: real data
     pa_sink_info info = {
         .name = "default_sink_name",
@@ -162,23 +180,32 @@
         },
         .mute = 0,
         .monitor_source = 0,
-        .monitor_source_name = "monitor_source_name",
+        .monitor_source_name = NULL,
         .latency = 100000,
         .driver = "apulse",
         .flags = 0,
-        .proplist = NULL,
+        .proplist = proplist,
         .configured_latency = 100000,
         .base_volume = PA_VOLUME_NORM,
         .state = PA_SINK_RUNNING,
         .n_volume_steps = 0,
-        .card = PA_INVALID_INDEX,
-        .n_ports = 0,
-        .ports = NULL,
-        .active_port = NULL,
+        .card = 0,
+        .n_ports = 1,
+        .ports = sink_ports,
+        .active_port = &sink_port,
     };
 
+    return info;
+}
+
+static void
+pa_context_get_sink_info_by_name_impl(pa_operation *op)
+{
+    pa_sink_info info = pai_fill_default_sink_info();
+
     if (op->sink_info_cb) {
-        op->sink_info_cb(op->c, &info, 0, op->cb_userdata);
+        if (strcmp(op->char_ptr_arg_1, info.name) == 0)
+            op->sink_info_cb(op->c, &info, 0, op->cb_userdata);
         op->sink_info_cb(op->c, NULL, 1, op->cb_userdata);
     }
 
@@ -205,6 +232,192 @@
 }
 
 static void
+pa_context_get_sink_info_list_impl(pa_operation *op)
+{
+    pa_sink_info info = pai_fill_default_sink_info();
+
+    if (op->sink_info_cb) {
+        op->sink_info_cb(op->c, &info, 0, op->cb_userdata);
+        op->sink_info_cb(op->c, NULL, 1, op->cb_userdata);
+    }
+
+    pa_operation_done(op);
+}
+
+APULSE_EXPORT
+pa_operation *
+pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void 
*userdata)
+{
+    trace_info_f("F %s c=%p, cb=%p, userdata=%p\n", __func__, c, cb, userdata);
+
+    pa_operation *op = pa_operation_new(c->mainloop_api, 
pa_context_get_sink_info_list_impl);
+    op->c = c;
+    op->sink_info_cb = cb;
+    op->cb_userdata = userdata;
+
+    pa_operation_launch(op);
+    return op;
+}
+
+static pa_source_info
+pai_fill_default_source_info(void)
+{
+    static pa_proplist *proplist = NULL;
+
+    if (!proplist) {
+        // TODO: free memory
+        proplist = pa_proplist_new();
+    }
+
+    static pa_source_port_info source_port = {
+        .name = "ALSA source",
+        .description = "ALSA source",
+        .priority = 1,
+        .available = PA_PORT_AVAILABLE_YES,
+    };
+
+    static pa_source_port_info *source_ports[] = {
+        &source_port,
+    };
+
+    // TODO: real data
+    pa_source_info info = {
+        .name = "default_source_name",
+        .index = 0,
+        .description = "default_source_name",
+        .sample_spec = {
+            .format = PA_SAMPLE_S16LE,
+            .rate = 44100,
+            .channels = 2,
+        },
+        .channel_map = {
+            .channels = 2,
+            .map = {
+                PA_CHANNEL_POSITION_FRONT_LEFT,
+                PA_CHANNEL_POSITION_FRONT_RIGHT,
+            },
+        },
+        .owner_module = PA_INVALID_INDEX,
+        .volume = {
+            .channels = 2,
+            .values = {
+                PA_VOLUME_NORM,
+                PA_VOLUME_NORM,
+            },
+        },
+        .mute = 0,
+        .monitor_of_sink = 0,
+        .monitor_of_sink_name = NULL,
+        .latency = 100000, // TODO: where to get latency figures?
+        .driver = "apulse",
+        .flags = 0,
+        .proplist = proplist,
+        .configured_latency = 100000,
+        .base_volume = PA_VOLUME_NORM,
+        .state = PA_SOURCE_RUNNING,
+        .n_volume_steps = 0,
+        .card = PA_INVALID_INDEX,
+        .n_ports = 1,
+        .ports = source_ports,
+        .active_port = &source_port,
+        .n_formats = 0,
+        .formats = NULL,
+    };
+
+    return info;
+}
+
+static void
+pa_context_get_source_info_list_impl(pa_operation *op)
+{
+    pa_source_info info = pai_fill_default_source_info();
+
+    if (op->source_info_cb) {
+        op->source_info_cb(op->c, &info, 0, op->cb_userdata);
+        op->source_info_cb(op->c, NULL, 1, op->cb_userdata);
+    }
+
+    pa_operation_done(op);
+}
+
+APULSE_EXPORT
+pa_operation *
+pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void 
*userdata)
+{
+    trace_info_f("F %s c=%p, cb=%p, userdata=%p\n", __func__, c, cb, userdata);
+
+    pa_operation *op = pa_operation_new(c->mainloop_api, 
pa_context_get_source_info_list_impl);
+    op->c = c;
+    op->source_info_cb = cb;
+    op->cb_userdata = userdata;
+
+    pa_operation_launch(op);
+    return op;
+}
+
+static void
+pa_context_get_source_info_by_name_impl(pa_operation *op)
+{
+    pa_source_info info = pai_fill_default_source_info();
+
+    if (op->source_info_cb) {
+        if (strcmp(op->char_ptr_arg_1, info.name) == 0)
+            op->source_info_cb(op->c, &info, 0, op->cb_userdata);
+        op->source_info_cb(op->c, NULL, 1, op->cb_userdata);
+    }
+
+    free(op->char_ptr_arg_1);
+    pa_operation_done(op);
+}
+
+APULSE_EXPORT
+pa_operation *
+pa_context_get_source_info_by_name(pa_context *c, const char *name, 
pa_source_info_cb_t cb,
+                                   void *userdata)
+{
+    trace_info_f("F %s c=%p, name=%s, cb=%p, userdata=%p\n", __func__, c, 
name, cb, userdata);
+
+    pa_operation *op = pa_operation_new(c->mainloop_api, 
pa_context_get_source_info_by_name_impl);
+    op->c = c;
+    op->char_ptr_arg_1 = strdup(name ? name : "");
+    op->source_info_cb = cb;
+    op->cb_userdata = userdata;
+
+    pa_operation_launch(op);
+    return op;
+}
+
+static void
+pa_context_get_sink_info_by_index_impl(pa_operation *op)
+{
+    pa_sink_info info = pai_fill_default_sink_info();
+
+    if (op->sink_info_cb) {
+        if (op->int_arg_1 == info.index)
+            op->sink_info_cb(op->c, &info, 0, op->cb_userdata);
+        op->sink_info_cb(op->c, NULL, 1, op->cb_userdata);
+    }
+
+    pa_operation_done(op);
+}
+
+APULSE_EXPORT
+pa_operation *
+pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, 
pa_sink_info_cb_t cb, void *userdata)
+{
+    trace_info_f("F %s c=%p, idx=%u, cb=%p, userdata=%p\n", __func__, c, idx, 
cb, userdata);
+
+    pa_operation *op = pa_operation_new(c->mainloop_api, 
pa_context_get_sink_info_by_index_impl);
+    op->c = c;
+    op->int_arg_1 = idx;
+    op->sink_info_cb = cb;
+    op->cb_userdata = userdata;
+
+    pa_operation_launch(op);
+    return op;
+}
+
+static void
 pa_context_get_sink_input_info_impl(pa_operation *op)
 {
     uint32_t idx = op->int_arg_1;
@@ -300,7 +513,6 @@
     c->streams_ht = g_hash_table_new(g_direct_hash, g_direct_equal);
 
     for (uint32_t k = 0; k < PA_CHANNELS_MAX; k++) {
-        c->sink_volume[k] = PA_VOLUME_NORM;
         c->source_volume[k] = PA_VOLUME_NORM;
     }
 
@@ -323,9 +535,15 @@
 pa_time_event *
 pa_context_rttime_new(pa_context *c, pa_usec_t usec, pa_time_event_cb_t cb, 
void *userdata)
 {
-    trace_info_z("Z %s c=%p, usec=%" PRIu64 " cb=%p, userdata=%p\n", __func__, 
c, (uint64_t)usec,
+    trace_info_f("F %s c=%p, usec=%" PRIu64 " cb=%p, userdata=%p\n", __func__, 
c, (uint64_t)usec,
                  cb, userdata);
-    return NULL;
+
+    struct timeval when = {
+        .tv_sec = usec / PA_USEC_PER_SEC,
+        .tv_usec = usec % PA_USEC_PER_SEC,
+    };
+
+    return c->mainloop_api->time_new(c->mainloop_api, &when, cb, userdata);
 }
 
 APULSE_EXPORT
@@ -342,12 +560,23 @@
 static void
 pa_context_set_sink_input_volume_impl(pa_operation *op)
 {
-    memset(&op->c->sink_volume, 0, sizeof(op->c->sink_volume));
+    uint32_t idx = op->int_arg_1;
+    pa_stream *s = g_hash_table_lookup(op->c->streams_ht, 
GINT_TO_POINTER(idx));
+
+    if (!s) {
+        // Can't find a stream with selected index.
+        if (op->context_success_cb)
+            op->context_success_cb(op->c, 0, op->cb_userdata);
+
+        return;
+    }
+
+    memset(s->volume, 0, sizeof(s->volume));
 
     const uint32_t channels = MIN(op->pa_cvolume_arg_1.channels, 
PA_CHANNELS_MAX);
 
     for (uint32_t k = 0; k < channels; k++)
-        op->c->sink_volume[k] = op->pa_cvolume_arg_1.values[k];
+        s->volume[k] = op->pa_cvolume_arg_1.values[k];
 
     if (op->context_success_cb)
         op->context_success_cb(op->c, 1, op->cb_userdata);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apulse-0.1.8/src/apulse-mainloop.c 
new/apulse-0.1.10/src/apulse-mainloop.c
--- old/apulse-0.1.8/src/apulse-mainloop.c      2017-03-08 17:38:10.000000000 
+0100
+++ new/apulse-0.1.10/src/apulse-mainloop.c     2017-04-18 17:55:19.000000000 
+0200
@@ -65,7 +65,7 @@
     trace_info_f("F %s\n", __func__);
 
     pa_mainloop *ml = e->mainloop;
-    g_queue_remove(ml->queue, e);
+    g_queue_remove(ml->deferred_events_queue, e);
     g_slice_free(pa_defer_event, e);
     pa_mainloop_wakeup(ml);
 }
@@ -82,7 +82,7 @@
     de->cb = cb;
     de->userdata = userdata;
     de->mainloop = ml;
-    g_queue_push_tail(ml->queue, de);
+    g_queue_push_tail(ml->deferred_events_queue, de);
 
     pa_mainloop_wakeup(ml);
     return de;
@@ -154,34 +154,92 @@
     trace_info_z("Z %s\n", __func__);
 }
 
-static
-void
-ml_api_time_free(pa_time_event* e)
+static void
+ml_api_time_free(pa_time_event *e)
 {
-    trace_info_z("Z %s\n", __func__);
+    trace_info_f("F %s e=%p\n", __func__, e);
+
+    pa_mainloop *ml = e->mainloop;
+    g_queue_remove(ml->timed_events_queue, e);
+
+    if (e->destroy_cb)
+        e->destroy_cb(&ml->api, e, e->userdata);
+
+    g_slice_free(pa_time_event, e);
+    pa_mainloop_wakeup(ml);
 }
 
-static
-pa_time_event *
+/// Comparator function for |timed_events_queue|. Orders events by value of 
|when| parameter.
+static gint
+time_event_comparator(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+    const pa_time_event *te_a = a;
+    const pa_time_event *te_b = b;
+
+    // First, try to compare seconds.
+    if (te_a->when.tv_sec < te_b->when.tv_sec)
+        return -1;
+
+    if (te_a->when.tv_sec > te_b->when.tv_sec)
+        return 1;
+
+    // If we got here, tv_sec fields are equal.
+
+    // Then, compare microseconds.
+    if (te_a->when.tv_usec < te_b->when.tv_usec)
+        return -1;
+
+    if (te_a->when.tv_usec > te_b->when.tv_usec)
+        return 1;
+
+    // Timestamps are equal.
+    return 0;
+}
+
+static pa_time_event *
 ml_api_time_new(pa_mainloop_api *a, const struct timeval *tv, 
pa_time_event_cb_t cb, void *userdata)
 {
-    trace_info_z("Z %s\n", __func__);
+    trace_info_f("F %s a=%p, tv=%p {%ld, %ld}, cb=%p, userdata=%p\n", 
__func__, a, tv,
+                 tv ? tv->tv_sec : 0, tv ? tv->tv_usec : 0, cb, userdata);
+
+    pa_mainloop *ml = a->userdata;
+    pa_time_event *te = g_slice_new0(pa_time_event);
+    te->enabled = 1;
+    te->when = tv ? *tv : (struct timeval){};
+    te->cb = cb;
+    te->userdata = userdata;
+    te->mainloop = ml;
 
-    return NULL;
+    g_queue_insert_sorted(ml->timed_events_queue, te, time_event_comparator, 
NULL);
+
+    pa_mainloop_wakeup(ml);
+    return te;
 }
 
-static
-void
-ml_api_time_restart(pa_time_event* e, const struct timeval *tv)
+static void
+ml_api_time_restart(pa_time_event *e, const struct timeval *tv)
 {
-    trace_info_z("Z %s\n", __func__);
+    trace_info_f("F %s e=%p, tv=%p {%ld, %ld}\n", __func__, e, tv, tv ? 
tv->tv_sec : 0,
+                 tv ? tv->tv_usec : 0);
+
+    pa_mainloop *ml = e->mainloop;
+
+    g_queue_remove(ml->timed_events_queue, e);
+
+    e->enabled = 1;
+    e->when = tv ? *tv : (struct timeval){};
+
+    g_queue_insert_sorted(ml->timed_events_queue, e, time_event_comparator, 
NULL);
+
+    pa_mainloop_wakeup(ml);
 }
 
-static
-void
+static void
 ml_api_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb)
 {
-    trace_info_z("Z %s\n", __func__);
+    trace_info_f("F %s e=%p, cb=%p\n", __func__, e, cb);
+
+    e->destroy_cb = cb;
 }
 
 static void
@@ -224,6 +282,12 @@
     }
 }
 
+static long
+microseconds_till_event(pa_usec_t now, const struct timeval *event_when)
+{
+    return (uint64_t)event_when->tv_sec * 1000 * 1000 + event_when->tv_usec - 
now;
+}
+
 APULSE_EXPORT
 int
 pa_mainloop_dispatch(pa_mainloop *m)
@@ -271,11 +335,20 @@
         m->fds[0].revents = 0;
     }
 
-    pa_defer_event *de = g_queue_pop_head(m->queue);
+    pa_usec_t now = pa_rtclock_now();
+    pa_time_event *te = g_queue_peek_head(m->timed_events_queue);
+    while (te && microseconds_till_event(now, &te->when) <= 0) {
+        if (te->cb && te->enabled)
+            te->cb(&m->api, te, &te->when, te->userdata);
+        g_queue_pop_head(m->timed_events_queue);
+        te = g_queue_peek_head(m->timed_events_queue);
+    }
+
+    pa_defer_event *de = g_queue_pop_head(m->deferred_events_queue);
     while (de) {
         if (de->cb)
             de->cb(&m->api, de, de->userdata);
-        de = g_queue_pop_head(m->queue);
+        de = g_queue_pop_head(m->deferred_events_queue);
     }
 
     return cnt;
@@ -287,7 +360,8 @@
 {
     trace_info_f("F %s m=%p\n", __func__, m);
 
-    g_queue_free(m->queue);
+    g_queue_free(m->deferred_events_queue);
+    g_queue_free(m->timed_events_queue);
     g_hash_table_unref(m->events_ht);
     close(m->wakeup_pipe[0]);
     close(m->wakeup_pipe[1]);
@@ -365,7 +439,8 @@
     m->api.defer_set_destroy = ml_api_defer_set_destroy;
     m->api.quit              = ml_api_quit;
 
-    m->queue = g_queue_new();
+    m->deferred_events_queue = g_queue_new();
+    m->timed_events_queue = g_queue_new();
     m->events_ht = g_hash_table_new(g_direct_hash, g_direct_equal);
     m->recreate_fds = 1;
 
@@ -382,11 +457,33 @@
 {
     trace_info_f("F %s m=%p\n", __func__, m);
 
+    long int timeout = m->timeout;
+
+    pa_time_event *te = g_queue_peek_head(m->timed_events_queue);
+    if (te) {
+        pa_usec_t now = pa_rtclock_now();
+        long int msecs_till_next_event = microseconds_till_event(now, 
&te->when) / PA_USEC_PER_MSEC;
+
+        // Ensure delay is non-negative, even if event is already expired.
+        msecs_till_next_event = MAX(msecs_till_next_event, 0);
+
+        if (timeout < 0) {
+            // poll() call was supposed to wait for indefinite period of time.
+            timeout = msecs_till_next_event;
+
+        } else {
+            timeout = MIN(timeout, msecs_till_next_event);
+        }
+
+        // |timeout| value should fit int limits.
+        timeout = MIN(timeout, INT32_MAX);
+    }
+
     int ret;
     if (m->poll_func) {
-        ret = m->poll_func(m->fds, m->nfds, m->timeout, m->poll_func_userdata);
+        ret = m->poll_func(m->fds, m->nfds, timeout, m->poll_func_userdata);
     } else {
-        ret = poll(m->fds, m->nfds, m->timeout);
+        ret = poll(m->fds, m->nfds, timeout);
     }
 
     return ret;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apulse-0.1.8/src/apulse-stream.c 
new/apulse-0.1.10/src/apulse-stream.c
--- old/apulse-0.1.8/src/apulse-stream.c        2017-03-08 17:38:10.000000000 
+0100
+++ new/apulse-0.1.10/src/apulse-stream.c       2017-04-18 17:55:19.000000000 
+0200
@@ -30,17 +30,6 @@
 
 #define HAVE_SND_PCM_AVAIL  SND_LIB_VERSION >= MAKE_SND_LIB_VERSION(1, 0, 18)
 
-#define CHECK_A(funcname, params)                                           \
-    do {                                                                    \
-        int errcode___ = funcname params;                                   \
-        if (errcode___ < 0) {                                               \
-            trace_error("%s, " #funcname ", %s\n", __func__,                \
-                        snd_strerror(errcode___));                          \
-            goto err;                                                       \
-        }                                                                   \
-    } while (0)
-
-
 static
 void
 deh_stream_state_changed(pa_mainloop_api *api, pa_defer_event *de, void 
*userdata)
@@ -78,7 +67,7 @@
     snd_pcm_sframes_t   frame_count;
     size_t              frame_size = pa_frame_size(&s->ss);
     char                buf[16 * 1024];
-    const size_t        buf_size = pa_find_multiple_of(sizeof(buf), 
frame_size);
+    const size_t        buf_size = pa_find_multiple_of(sizeof(buf), 
frame_size, 0);
     int                 paused = g_atomic_int_get(&s->paused);
 
     if (events & (PA_IO_EVENT_INPUT | PA_IO_EVENT_OUTPUT)) {
@@ -102,6 +91,64 @@
                 ret = snd_pcm_recover(s->ph, frame_count, 1);
             } while (ret == -1 && errno == EINTR && cnt < 5);
 
+            switch (snd_pcm_state(s->ph)) {
+            case SND_PCM_STATE_OPEN:
+                // Highly unlikely device will be here in this state. But if 
it is, there is nothing
+                // can be done.
+                trace_error(
+                    "Stream '%s' of context '%s' have its associated PCM 
device in "
+                    "SND_PCM_STATE_OPEN state. Reconfiguration is required, 
but is not possible at "
+                    "the moment. Giving up.",
+                    s->name ? s->name : "", s->c->name ? s->c->name : "");
+                break;
+
+            case SND_PCM_STATE_SETUP:
+                // There is configuration, but device is not prepared and not 
started.
+                snd_pcm_prepare(s->ph);
+                snd_pcm_start(s->ph);
+                break;
+
+            case SND_PCM_STATE_PREPARED:
+                // Device prepared, but not started.
+                snd_pcm_start(s->ph);
+                break;
+
+            case SND_PCM_STATE_RUNNING:
+                // That's the expected state.
+                break;
+
+            case SND_PCM_STATE_XRUN:
+                trace_error(
+                    "Stream '%s' of context '%s' have its associated device in 
SND_PCM_STATE_XRUN "
+                    "state even after xrun recovery.",
+                    s->name ? s->name : "", s->c->name ? s->c->name : "");
+                break;
+
+            case SND_PCM_STATE_DRAINING:
+                trace_error(
+                    "Stream '%s' of context '%s' have its associated device in 
"
+                    "SND_PCM_STATE_DRAINING state, which is highly unusual.",
+                    s->name ? s->name : "", s->c->name ? s->c->name : "");
+                break;
+
+            case SND_PCM_STATE_PAUSED:
+                // Resume from paused state.
+                snd_pcm_pause(s->ph, 0);
+                break;
+
+            case SND_PCM_STATE_SUSPENDED:
+                // Resume from suspended state.
+                snd_pcm_resume(s->ph);
+                break;
+
+            case SND_PCM_STATE_DISCONNECTED:
+                trace_error(
+                    "Stream '%s' of context '%s' have its associated device in 
"
+                    "SND_PCM_STATE_DISCONNECTED state. Giving up.",
+                    s->name ? s->name : "", s->c->name ? s->c->name : "");
+                break;
+            }
+
 #if HAVE_SND_PCM_AVAIL
             frame_count = snd_pcm_avail(s->ph);
 #else
@@ -128,13 +175,14 @@
         } else {
             size_t writable_size = pa_stream_writable_size(s);
 
-            if (s->write_cb && writable_size > 0)
-                s->write_cb(s, writable_size, s->write_cb_userdata);
+            // Ask client for data, but only if we are ready for at least 
|minreq| bytes.
+            if (s->write_cb && writable_size >= s->buffer_attr.minreq)
+                s->write_cb(s, s->buffer_attr.minreq, s->write_cb_userdata);
 
             size_t bytecnt = MIN(buf_size, frame_count * frame_size);
             bytecnt = ringbuffer_read(s->rb, buf, bytecnt);
 
-            pa_apply_volume_multiplier(buf, bytecnt, s->c->sink_volume, 
&s->ss);
+            pa_apply_volume_multiplier(buf, bytecnt, s->volume, &s->ss);
 
             if (bytecnt == 0) {
                 // application is not ready yet, play silence
@@ -182,52 +230,174 @@
 {
     snd_pcm_hw_params_t *hw_params;
     snd_pcm_sw_params_t *sw_params;
-    int dir;
-    unsigned int rate;
-    const char *dev_name;
+    int errcode;
+    const char *device_name;
+    const char *direction_name;
 
     switch (stream_direction) {
     default:
     case SND_PCM_STREAM_PLAYBACK:
-        dev_name = getenv("APULSE_PLAYBACK_DEVICE");
-        CHECK_A(snd_pcm_open, (&s->ph, dev_name ? dev_name : "default", 
stream_direction, 0));
+        device_name = getenv("APULSE_PLAYBACK_DEVICE");
+        direction_name = "playback";
         break;
     case SND_PCM_STREAM_CAPTURE:
-        dev_name = getenv("APULSE_CAPTURE_DEVICE");
-        CHECK_A(snd_pcm_open, (&s->ph, dev_name ? dev_name : "default", 
stream_direction, 0));
+        device_name = getenv("APULSE_CAPTURE_DEVICE");
+        direction_name = "capture";
         break;
     }
 
-    CHECK_A(snd_pcm_hw_params_malloc, (&hw_params));
-    CHECK_A(snd_pcm_hw_params_any, (s->ph, hw_params));
-    CHECK_A(snd_pcm_hw_params_set_access, (s->ph, hw_params, 
SND_PCM_ACCESS_RW_INTERLEAVED));
-    CHECK_A(snd_pcm_hw_params_set_format, (s->ph, hw_params, 
pa_format_to_alsa(s->ss.format)));
-    CHECK_A(snd_pcm_hw_params_set_rate_resample, (s->ph, hw_params, 1));
-    rate = s->ss.rate;
-    dir = 0;
-    CHECK_A(snd_pcm_hw_params_set_rate_near, (s->ph, hw_params, &rate, &dir));
-    CHECK_A(snd_pcm_hw_params_set_channels, (s->ph, hw_params, 
s->ss.channels));
-
-    unsigned int period_time = 20 * 1000;
-    dir = 1;
-    CHECK_A(snd_pcm_hw_params_set_period_time_near, (s->ph, hw_params, 
&period_time, &dir));
-
-    unsigned int buffer_time = 4 * period_time;
-    dir = 1;
-    CHECK_A(snd_pcm_hw_params_set_buffer_time_near, (s->ph, hw_params, 
&buffer_time, &dir));
-    CHECK_A(snd_pcm_hw_params, (s->ph, hw_params));
+    if (device_name == NULL)
+        device_name = "default";
+
+    char *device_description = g_strdup_printf("%s device \"%s\"", 
direction_name, device_name);
+    if (!device_description) {
+        trace_error("%s: can't allocate memory for device description 
string\n", __func__);
+        goto fatal_error;
+    }
+
+    errcode = snd_pcm_open(&s->ph, device_name, stream_direction, 0);
+    if (errcode != 0) {
+        trace_error("%s: can't open %s. Error code %d (%s)\n", __func__, 
device_description,
+                    errcode, snd_strerror(errcode));
+        goto fatal_error;
+    }
+
+    errcode = snd_pcm_hw_params_malloc(&hw_params);
+    if (errcode != 0) {
+        trace_error("%s: can't allocate memory for hw parameters for %s. Error 
code %d (%s)\n",
+                    __func__, device_description, errcode, 
snd_strerror(errcode));
+        goto fatal_error;
+    }
+
+    errcode = snd_pcm_hw_params_any(s->ph, hw_params);
+    if (errcode != 0) {
+        trace_error("%s: can't get initial hw parameters for %s. Error code %d 
(%s)\n", __func__,
+                    device_description, errcode, snd_strerror(errcode));
+        goto fatal_error;
+    }
+
+    errcode = snd_pcm_hw_params_set_access(s->ph, hw_params, 
SND_PCM_ACCESS_RW_INTERLEAVED);
+    if (errcode != 0) {
+        trace_error("%s: can't select interleaved mode for %s. Error code %d 
(%s)\n", __func__,
+                    device_description, errcode, snd_strerror(errcode));
+        // TODO: is it worth to support non-interleaved mode?
+        goto fatal_error;
+    }
+
+    errcode = snd_pcm_hw_params_set_format(s->ph, hw_params, 
pa_format_to_alsa(s->ss.format));
+    if (errcode != 0) {
+        snd_pcm_format_t alsa_format = pa_format_to_alsa(s->ss.format);
+        trace_error("%s: can't set sample format %d (\"%s\") for %s. Error 
code %d (%s)\n",
+                    __func__, alsa_format, snd_pcm_format_name(alsa_format), 
device_description,
+                    errcode, snd_strerror(errcode));
+        goto fatal_error;
+    }
+
+    errcode = snd_pcm_hw_params_set_rate_resample(s->ph, hw_params, 1);
+    if (errcode != 0) {
+        trace_error("%s: can't enable rate resample for %s. Error code %d 
(%s)\n", __func__,
+                    device_description, errcode, snd_strerror(errcode));
+        // This is not a fatal error. Audio speed will be wrong, but there 
will be something.
+        // And it sounds funny.
+    }
+
+    unsigned int rate = s->ss.rate;
+    int dir = 0;
+
+    errcode = snd_pcm_hw_params_set_rate_near(s->ph, hw_params, &rate, &dir);
+    if (errcode != 0) {
+        trace_error("%s: can't set sample rate for %s. Error code %d (%s)\n", 
__func__,
+                    device_description, errcode, snd_strerror(errcode));
+        goto fatal_error;
+    }
+
+    trace_info_f("%s: demanded %d Hz sample rate, got %d Hz for %s, dir = 
%d\n", __func__,
+                 (int)s->ss.rate, (int)rate, device_description, dir);
+
+    if (rate != s->ss.rate)
+        trace_error("%s: actual sample rate, %d Hz, differs from required %d 
Hz\n", __func__,
+                    (int)rate, (int)s->ss.rate);
+
+    errcode = snd_pcm_hw_params_set_channels(s->ph, hw_params, s->ss.channels);
+    if (errcode != 0) {
+        trace_error("%s: can't set channel count to %d for %s. Error code %d 
(%s)\n", __func__,
+                    (int)s->ss.channels, device_description, errcode, 
snd_strerror(errcode));
+        // TODO: channel count handling?
+        goto fatal_error;
+    }
+
+    const size_t frame_size = pa_frame_size(&s->ss);
+    snd_pcm_uframes_t requested_period_size = s->buffer_attr.minreq / 
frame_size;
+    snd_pcm_uframes_t period_size = requested_period_size;
+    dir = 1;  // Prefer larger period sizes, if exact is not possible.
+    errcode = snd_pcm_hw_params_set_period_size_near(s->ph, hw_params, 
&period_size, &dir);
+    if (errcode != 0) {
+        trace_error("%s: can't set period size to %d frames for %s. Error code 
%d (%s)\n", __func__,
+                    (int)requested_period_size, device_description, errcode, 
snd_strerror(errcode));
+        goto fatal_error;
+    }
+
+    trace_info_f("%s: requested period size of %d frames, got %d frames for 
%s\n", __func__,
+                 (int)requested_period_size, (int)period_size, 
device_description);
+
+    // Set up buffer size. Ensure it's at least four times larger than a 
period size.
+    snd_pcm_uframes_t requested_buffer_size = s->buffer_attr.tlength / 
frame_size;
+    snd_pcm_uframes_t buffer_size = MAX(requested_buffer_size, 4 * 
period_size);
+    errcode = snd_pcm_hw_params_set_buffer_size_near(s->ph, hw_params, 
&buffer_size);
+    if (errcode != 0) {
+        trace_error(
+            "%s: can't set buffer size to %d frames for %s. Error code %d 
(%s)\n",
+            __func__, (int)buffer_size, device_description, errcode, 
snd_strerror(errcode));
+        goto fatal_error;
+    }
+
+    trace_info_f("%s: requested buffer size of %d frames, got %d frames for 
%s\n", __func__,
+                 (int)requested_buffer_size, (int)buffer_size, 
device_description);
+
+    errcode = snd_pcm_hw_params(s->ph, hw_params);
+    if (errcode != 0) {
+        trace_error("%s: can't apply configured hw parameter block for %s\n", 
__func__,
+                    device_description);
+        goto fatal_error;
+    }
+
     snd_pcm_hw_params_free(hw_params);
 
-    CHECK_A(snd_pcm_sw_params_malloc, (&sw_params));
-    CHECK_A(snd_pcm_sw_params_current, (s->ph, sw_params));
+    errcode = snd_pcm_sw_params_malloc(&sw_params);
+    if (errcode != 0) {
+        trace_error("%s: can't allocate memory for sw parameters for %s\n", 
__func__,
+                    device_description);
+        goto fatal_error;
+    }
+
+    errcode = snd_pcm_sw_params_current(s->ph, sw_params);
+    if (errcode != 0) {
+        trace_error("%s: can't acquire current sw parameters for %s\n", 
__func__,
+                    device_description);
+        goto fatal_error;
+    }
+
+    errcode = snd_pcm_sw_params_set_avail_min(s->ph, sw_params, period_size);
+    if (errcode != 0) {
+        trace_error("%s: can't set avail min for %s\n", __func__, 
device_description);
+        goto fatal_error;
+    }
 
-    const snd_pcm_uframes_t period_size = (uint64_t)period_time * rate / (1000 
* 1000);
-    CHECK_A(snd_pcm_sw_params_set_avail_min, (s->ph, sw_params, period_size));
     // no period event requested
-    CHECK_A(snd_pcm_sw_params, (s->ph, sw_params));
+
+    errcode = snd_pcm_sw_params(s->ph, sw_params);
+    if (errcode != 0) {
+        trace_error("%s: can't apply sw parameters for %s\n", __func__, 
device_description);
+        goto fatal_error;
+    }
+
     snd_pcm_sw_params_free(sw_params);
 
-    CHECK_A(snd_pcm_prepare, (s->ph));
+    errcode = snd_pcm_prepare(s->ph);
+    if (errcode != 0) {
+        trace_error("%s: can't prepare PCM device to use for %s\n", __func__, 
device_description);
+        goto fatal_error;
+    }
 
     int nfds = snd_pcm_poll_descriptors_count(s->ph);
     struct pollfd *fds = calloc(nfds, sizeof(struct pollfd));
@@ -248,8 +418,19 @@
     pa_stream_ref(s);
     s->c->mainloop_api->defer_new(s->c->mainloop_api, 
deh_stream_first_readwrite_callback, s);
 
+    g_free(device_description);
     return 0;
-err:
+
+fatal_error:
+    trace_error(
+        "%s: failed to open ALSA device. Apulse does no resampling or format 
conversion, leaving "
+        "that task to ALSA plugins. Ensure that selected device is capable of 
playing a particular "
+        "sample format at a particular rate. They have to be supported by 
either hardware "
+        "directly, or by \"plug\" and \"dmix\" ALSA plugins which will perform 
required "
+        "conversions on CPU.\n",
+        __func__);
+
+    g_free(device_description);
     return -1;
 }
 
@@ -257,13 +438,16 @@
 int
 pa_stream_begin_write(pa_stream *p, void **data, size_t *nbytes)
 {
-    trace_info_f("F %s p=%p\n", __func__, p);
+    trace_info_f("F %s p=%p nbytes=%p(%" PRIu64 ")\n", __func__, p, nbytes,
+                 (uint64_t)(nbytes ? *nbytes : 0));
 
     free(p->write_buffer);
 
     if (*nbytes == (size_t)-1)
         *nbytes = 8192;
 
+    *nbytes = pa_find_multiple_of(*nbytes, pa_frame_size(&p->ss), 0);
+
     p->write_buffer = malloc(*nbytes);
 
     if (!p->write_buffer)
@@ -286,6 +470,72 @@
     return 0;
 }
 
+static void
+stream_adjust_buffer_attrs(pa_stream *s, const pa_buffer_attr *attr)
+{
+    pa_buffer_attr *ba = &s->buffer_attr;
+    const size_t frame_size = pa_frame_size(&s->ss);
+
+    if (attr) {
+        *ba = *attr;
+    } else {
+        // If client passed NULL, all parameters have default values.
+        ba->maxlength = (uint32_t)-1;
+        ba->tlength = (uint32_t)-1;
+        ba->prebuf = (uint32_t)-1;
+        ba->minreq = (uint32_t)-1;
+        ba->fragsize = (uint32_t)-1;
+    }
+
+    // Adjust default values.
+    // Overall buffer length.
+    if (ba->maxlength == (uint32_t)-1)
+        ba->maxlength = 4 * 1024 * 1024;
+
+    if (ba->maxlength == 0)
+        ba->maxlength = frame_size;
+
+    // Target length of a buffer.
+    if (ba->tlength == (uint32_t)-1)
+        ba->tlength = pa_usec_to_bytes(2 * 1000 * 1000, &s->ss);
+
+    if (ba->tlength == 0)
+        ba->tlength = frame_size;
+
+    ba->tlength = MIN(ba->tlength, ba->maxlength);
+
+    // Minimum request (playback).
+    if (ba->minreq == (uint32_t)-1) {
+        ba->minreq = pa_usec_to_bytes(20 * 1000, &s->ss);
+        ba->minreq = MIN(ba->minreq, ba->tlength / 4);
+    }
+
+    if (ba->minreq == 0)
+        ba->minreq = frame_size;
+
+    // Fragment size (recording).
+    if (ba->fragsize == (uint32_t)-1) {
+        ba->fragsize = pa_usec_to_bytes(20 * 1000, &s->ss);
+    }
+
+    if (ba->fragsize == 0)
+        ba->fragsize = frame_size;
+
+    // Pre-buffering.
+    if (ba->prebuf == (uint32_t)-1)
+        ba->prebuf = ba->tlength - ba->minreq;
+
+    if (ba->prebuf > ba->tlength - ba->minreq)
+        ba->prebuf = ba->tlength - ba->minreq;
+
+    // Ensure values are all multiple of |frame_size|.
+    ba->maxlength = pa_find_multiple_of(ba->maxlength, frame_size, 1);
+    ba->tlength = pa_find_multiple_of(ba->tlength, frame_size, 1);
+    ba->prebuf = pa_find_multiple_of(ba->prebuf, frame_size, 1);
+    ba->minreq = pa_find_multiple_of(ba->minreq, frame_size, 1);
+    ba->fragsize = pa_find_multiple_of(ba->fragsize, frame_size, 1);
+}
+
 APULSE_EXPORT
 int
 pa_stream_connect_playback(pa_stream *s, const char *dev, const pa_buffer_attr 
*attr,
@@ -298,15 +548,7 @@
     g_free(s_attr);
 
     s->direction = PA_STREAM_PLAYBACK;
-    if (attr) {
-        s->buffer_attr = *attr;
-    } else {
-        s->buffer_attr.maxlength = (uint32_t)-1;
-        s->buffer_attr.tlength = (uint32_t)-1;
-        s->buffer_attr.prebuf = (uint32_t)-1;
-        s->buffer_attr.minreq = (uint32_t)-1;
-        s->buffer_attr.fragsize = (uint32_t)-1;
-    }
+    stream_adjust_buffer_attrs(s, attr);
 
     if (do_connect_pcm(s, SND_PCM_STREAM_PLAYBACK) != 0)
         goto err;
@@ -596,6 +838,9 @@
     s->rb = ringbuffer_new(72 * 1024);    // TODO: figure out size
     s->peek_buffer = malloc(s->rb->end - s->rb->start);
 
+    for (uint32_t k = 0; k < PA_CHANNELS_MAX; k++)
+        s->volume[k] = PA_VOLUME_NORM;
+
     return s;
 }
 
@@ -759,7 +1004,7 @@
     if (writable_size < limit)
         writable_size = 0;
 
-    return pa_find_multiple_of(writable_size, pa_frame_size(&s->ss));
+    return pa_find_multiple_of(writable_size, pa_frame_size(&s->ss), 0);
 }
 
 APULSE_EXPORT
@@ -769,7 +1014,7 @@
     trace_info_f("F %s s=%p\n", __func__, s);
 
     size_t readable_size = ringbuffer_readable_size(s->rb);
-    return pa_find_multiple_of(readable_size, pa_frame_size(&s->ss));
+    return pa_find_multiple_of(readable_size, pa_frame_size(&s->ss), 0);
 }
 
 APULSE_EXPORT
@@ -810,15 +1055,7 @@
     g_free(s_attr);
 
     s->direction = PA_STREAM_RECORD;
-    if (attr) {
-        s->buffer_attr = *attr;
-    } else {
-        s->buffer_attr.maxlength = (uint32_t)-1;
-        s->buffer_attr.tlength = (uint32_t)-1;
-        s->buffer_attr.prebuf = (uint32_t)-1;
-        s->buffer_attr.minreq = (uint32_t)-1;
-        s->buffer_attr.fragsize = (uint32_t)-1;
-    }
+    stream_adjust_buffer_attrs(s, attr);
 
     if (do_connect_pcm(s, SND_PCM_STREAM_CAPTURE) != 0)
         goto err;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apulse-0.1.8/src/apulse.h 
new/apulse-0.1.10/src/apulse.h
--- old/apulse-0.1.8/src/apulse.h       2017-03-08 17:38:10.000000000 +0100
+++ new/apulse-0.1.10/src/apulse.h      2017-04-18 17:55:19.000000000 +0200
@@ -46,7 +46,6 @@
     int                     next_stream_idx;
     GHashTable             *streams_ht;
     pa_volume_t             source_volume[PA_CHANNELS_MAX];
-    pa_volume_t             sink_volume[PA_CHANNELS_MAX];
 };
 
 struct pa_io_event {
@@ -62,8 +61,9 @@
 
 struct pa_mainloop {
     pa_mainloop_api     api;
-    GQueue             *queue;
-    GHashTable         *events_ht;
+    GQueue             *deferred_events_queue;
+    GQueue             *timed_events_queue;
+    GHashTable         *events_ht;  ///< a set of (pa_io_event *)
     struct pollfd      *fds;
     nfds_t              nfds;
     int                 recreate_fds; ///< 1 if fds array needs to be 
recreated from events_ht
@@ -114,6 +114,7 @@
     size_t                  peek_buffer_data_len;
     void                   *write_buffer;
     volatile int            paused;
+    pa_volume_t             volume[PA_CHANNELS_MAX];
 };
 
 struct pa_operation {
@@ -123,6 +124,7 @@
     pa_sink_info_cb_t       sink_info_cb;
     pa_context_success_cb_t context_success_cb;
     pa_server_info_cb_t     server_info_cb;
+    pa_source_info_cb_t     source_info_cb;
     void                   *cb_userdata;
 
     pa_mainloop_api *api;
@@ -148,6 +150,14 @@
     pa_mainloop            *mainloop;
 };
 
+struct pa_time_event {
+    int                         enabled;
+    struct timeval              when;
+    pa_time_event_cb_t          cb;
+    void                       *userdata;
+    pa_mainloop                *mainloop;
+    pa_time_event_destroy_cb_t  destroy_cb;
+};
 
 pa_operation *
 pa_operation_new(pa_mainloop_api *api, void (*handler)(pa_operation *op));
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apulse-0.1.8/src/notimplemented.c 
new/apulse-0.1.10/src/notimplemented.c
--- old/apulse-0.1.8/src/notimplemented.c       2017-03-08 17:38:10.000000000 
+0100
+++ new/apulse-0.1.10/src/notimplemented.c      2017-04-18 17:55:19.000000000 
+0200
@@ -860,20 +860,6 @@
 }
 
 APULSE_EXPORT
-pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, 
pa_sink_info_cb_t cb, void *userdata)
-{
-    trace_info_z("Z %s\n", __func__);
-    return NULL;
-}
-
-APULSE_EXPORT
-pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t 
cb, void *userdata)
-{
-    trace_info_z("Z %s\n", __func__);
-    return NULL;
-}
-
-APULSE_EXPORT
 pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, 
const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata)
 {
     trace_info_z("Z %s\n", __func__);
@@ -927,20 +913,6 @@
 {
     trace_info_z("Z %s\n", __func__);
     return NULL;
-}
-
-APULSE_EXPORT
-pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char 
*name, pa_source_info_cb_t cb, void *userdata)
-{
-    trace_info_z("Z %s\n", __func__);
-    return NULL;
-}
-
-APULSE_EXPORT
-pa_operation* pa_context_get_source_info_list(pa_context *c, 
pa_source_info_cb_t cb, void *userdata)
-{
-    trace_info_z("Z %s\n", __func__);
-    return NULL;
 }
 
 APULSE_EXPORT
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apulse-0.1.8/src/trace.c 
new/apulse-0.1.10/src/trace.c
--- old/apulse-0.1.8/src/trace.c        2017-03-08 17:38:10.000000000 +0100
+++ new/apulse-0.1.10/src/trace.c       2017-04-18 17:55:19.000000000 +0200
@@ -62,17 +62,31 @@
 void
 trace_error(const char *fmt, ...)
 {
+    static int stdout_tested = 0;
+    static int stdout_is_a_tty = 0;
+
     pthread_mutex_lock(&lock);
+
+    if (!stdout_tested) {
+        stdout_is_a_tty = isatty(1);
+        stdout_tested = 1;
+    }
+
     va_list args;
     fprintf(stderr, "[apulse] [error] ");
     va_start(args, fmt);
     vfprintf(stderr, fmt, args);
     va_end(args);
 
-    fprintf(stdout, "[apulse] [error] ");
-    va_start(args, fmt);
-    vfprintf(stdout, fmt, args);
-    va_end(args);
+    if (!stdout_is_a_tty) {
+        // If stdout is redirected to a file, make sure it also gets error 
messages.
+        // That helps to figure out where errors were.
+        fprintf(stdout, "[apulse] [error] ");
+        va_start(args, fmt);
+        vfprintf(stdout, fmt, args);
+        va_end(args);
+    }
+
     pthread_mutex_unlock(&lock);
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apulse-0.1.8/src/util.c new/apulse-0.1.10/src/util.c
--- old/apulse-0.1.8/src/util.c 2017-03-08 17:38:10.000000000 +0100
+++ new/apulse-0.1.10/src/util.c        2017-04-18 17:55:19.000000000 +0200
@@ -84,9 +84,13 @@
 }
 
 size_t
-pa_find_multiple_of(size_t number, size_t multiple_of)
+pa_find_multiple_of(size_t number, size_t multiple_of, int 
towards_larger_numbers)
 {
-    return number - (number % multiple_of);
+    if (multiple_of == 0)
+        return number;
+
+    size_t n = towards_larger_numbers ? (number + multiple_of - 1) : number;
+    return n - (n % multiple_of);
 }
 
 void
@@ -131,10 +135,10 @@
     case PA_SAMPLE_S16NE:
         while (p < last) {
             for (uint32_t k = 0; k < channels && p < last; k++) {
-                uint16_t sample;
+                int16_t sample;
                 memcpy(&sample, p, sizeof(sample));
                 float sample_scaled = sample * fvol[k];
-                sample = CLAMP(sample_scaled, 0.0, 65535.0);
+                sample = CLAMP(sample_scaled, -32768.0, 32767.0);
                 memcpy(p, &sample, sizeof(sample));
                 p += sizeof(sample);
             }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apulse-0.1.8/src/util.h new/apulse-0.1.10/src/util.h
--- old/apulse-0.1.8/src/util.h 2017-03-08 17:38:10.000000000 +0100
+++ new/apulse-0.1.10/src/util.h        2017-04-18 17:55:19.000000000 +0200
@@ -37,7 +37,7 @@
 
 /// finds largest number that is less or equal than |number| and multiple of 
|multiple_of|.
 size_t
-pa_find_multiple_of(size_t number, size_t multiple_of);
+pa_find_multiple_of(size_t number, size_t multiple_of, int 
towards_larger_numbers);
 
 void
 pa_apply_volume_multiplier(void *buf, size_t sz, const pa_volume_t 
volume[PA_CHANNELS_MAX],


Reply via email to