Description: <short summary of the patch>
 TODO: Put a short summary on the line above and replace this paragraph
 with a longer explanation of this change. Complete the meta-information
 with other relevant fields (see below for details). To make it easier, the
 information below has been extracted from the changelog. Adjust it or drop
 it.
 .
 apulse (0.1.9-4) unstable; urgency=medium
 .
   [ KatolaZ ]
   * back to quilt
 .
   [ Vincenzo (KatolaZ) Nicosia ]
Author: Vincenzo (KatolaZ) Nicosia <katolaz@freaknet.org>

---
The information above should follow the Patch Tagging Guidelines, please
checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here
are templates for supplementary fields that you might want to add:

Origin: <vendor|upstream|other>, <url of original patch>
Bug: <url in upstream bugtracker>
Bug-Debian: https://bugs.debian.org/<bugnumber>
Bug-Ubuntu: https://launchpad.net/bugs/<bugnumber>
Forwarded: <no|not-needed|url proving that it has been forwarded>
Reviewed-By: <name and email of someone who approved the patch>
Last-Update: 2017-12-15

--- apulse-0.1.9.orig/README.md
+++ apulse-0.1.9/README.md
@@ -3,9 +3,31 @@ About
 
 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 @@ cmake -DAPULSEPATH=/usr/lib -DCMAKE_INST
 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
 =======
 
--- apulse-0.1.9.orig/src/apulse-context.c
+++ apulse-0.1.9/src/apulse-context.c
@@ -132,9 +132,27 @@ pa_context_get_server_protocol_version(p
     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 @@ pa_context_get_sink_info_by_name_impl(pa
         },
         .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 @@ pa_context_get_sink_info_by_name(pa_cont
 }
 
 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 @@ pa_context_new_with_proplist(pa_mainloop
     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;
     }
 
@@ -342,12 +554,23 @@ pa_context_set_sink_input_mute(pa_contex
 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);
--- apulse-0.1.9.orig/src/apulse-stream.c
+++ apulse-0.1.9/src/apulse-stream.c
@@ -124,7 +124,7 @@ data_available_for_stream(pa_mainloop_ap
             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
@@ -776,6 +776,9 @@ pa_stream_new_with_proplist(pa_context *
     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;
 }
 
--- apulse-0.1.9.orig/src/apulse.h
+++ apulse-0.1.9/src/apulse.h
@@ -46,7 +46,6 @@ struct pa_context {
     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 {
@@ -114,6 +113,7 @@ struct pa_stream {
     size_t                  peek_buffer_data_len;
     void                   *write_buffer;
     volatile int            paused;
+    pa_volume_t             volume[PA_CHANNELS_MAX];
 };
 
 struct pa_operation {
@@ -123,6 +123,7 @@ struct pa_operation {
     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;
--- apulse-0.1.9.orig/src/notimplemented.c
+++ apulse-0.1.9/src/notimplemented.c
@@ -860,20 +860,6 @@ uint32_t pa_stream_get_monitor_stream(pa
 }
 
 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 @@ pa_operation* pa_context_set_sink_port_b
 {
     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
