void
pa_policy_context_add_property_action(struct pa_policy_context_rule *rule,
                                      int                         lineno,
                                      enum pa_policy_object_type  obj_type,
                                      enum pa_classify_method     obj_classify,
                                      const char                 *obj_name,
                                      const char                 *prop_name,
                                      enum pa_policy_value_type   value_type,
                                      ...                     /* value_arg */)
{
    union pa_policy_context_action *action;
    struct pa_policy_set_property  *setprop;
    va_list                         value_arg;

    action  = pa_xmalloc0(sizeof(*action));
    setprop = &action->setprop; 
    
    setprop->type   = pa_policy_set_property;
    setprop->lineno = lineno;

    setprop->object.type = obj_type;
    match_setup(&setprop->object.match, obj_classify, obj_name, NULL);

    setprop->property = pa_xstrdup(prop_name);

    va_start(value_arg, value_type);
    value_setup(&setprop->value, value_type, value_arg);
    va_end(value_arg);

    append_action(&rule->actions, action);
}
static
struct pa_policy_context_variable *add_variable(struct pa_policy_context *ctx,
                                                const char *name)
{
    struct pa_policy_context_variable *var;
    struct pa_policy_context_variable *last;

    for (last = (struct pa_policy_context_variable *)&ctx->variables;
         last->next != NULL;
         last = last->next)
    {
        var = last->next;

        if (!strcmp(name, var->name))
            return var;
    }

    var = pa_xmalloc0(sizeof(*var));

    var->name  = pa_xstrdup(name);
    var->value = pa_xstrdup("");

    last->next = var;

    pa_log_debug("created context variable '%s'", var->name);

    return var;
}
Exemple #3
0
pa_asyncq *pa_asyncq_new(unsigned size) {
    pa_asyncq *l;

    if (!size)
        size = ASYNCQ_SIZE;

    pa_assert(pa_is_power_of_two(size));

    l = pa_xmalloc0(PA_ALIGN(sizeof(pa_asyncq)) + (sizeof(pa_atomic_ptr_t) * size));

    l->size = size;

    PA_LLIST_HEAD_INIT(struct localq, l->localq);
    l->last_localq = NULL;
    l->waiting_for_post = FALSE;

    if (!(l->read_fdsem = pa_fdsem_new())) {
        pa_xfree(l);
        return NULL;
    }

    if (!(l->write_fdsem = pa_fdsem_new())) {
        pa_fdsem_free(l->read_fdsem);
        pa_xfree(l);
        return NULL;
    }

    return l;
}
static
struct pa_policy_activity_variable *get_activity_variable(struct userdata *u, struct pa_policy_context *ctx,
                                                          const char *device)
{
    struct pa_policy_activity_variable *var;
    struct pa_policy_activity_variable *last;

    for (last = (struct pa_policy_activity_variable *)&ctx->activities;
         last->next != NULL;
         last = last->next)
    {
        var = last->next;

        if (!strcmp(device, var->device))
            return var;
    }

    var = pa_xmalloc0(sizeof(*var));

    var->device = pa_xstrdup(device);
    var->userdata = u;
    var->default_state = -1;

    last->next = var;

    pa_log_debug("created context activity variable '%s'", var->device);

    return var;
}
Exemple #5
0
pa_flist *pa_flist_new_with_name(unsigned size, const char *name) {
    pa_flist *l;
    unsigned i;
    pa_assert(name);

    if (!size)
        size = FLIST_SIZE;

    l = pa_xmalloc0(sizeof(pa_flist) + sizeof(pa_flist_elem) * size);

    l->name = pa_xstrdup(name);
    l->size = size;

    while (1 << l->tag_shift < (int) size)
        l->tag_shift++;
    l->index_mask = (1 << l->tag_shift) - 1;
    l->tag_mask = INT_MAX - l->index_mask;

    pa_atomic_store(&l->stored, -1);
    pa_atomic_store(&l->empty, -1);
    for (i=0; i < size; i++) {
        stack_push(l, &l->empty, &l->table[i]);
    }
    return l;
}
static struct pa_policy_context_rule *
add_rule(struct pa_policy_context_rule    **rules,
         enum pa_classify_method            method,
         const char                        *arg)
{
    struct pa_policy_context_rule *rule = pa_xmalloc0(sizeof(*rule));
    struct pa_policy_context_rule *last;
    enum pa_classify_method        method_status;

    if (!match_setup(&rule->match, method, arg, &method_status)) {
        pa_log("%s: invalid rule definition (method %s)",
               __FUNCTION__, pa_classify_method_str(method_status));
        pa_xfree(rule);
        return NULL;
    };

    for (last = (struct pa_policy_context_rule *)rules;
         last->next != NULL;
         last = last->next)
        ;

    last->next = rule;

    return rule;
}
static void handle_new_sink_input(struct userdata      *u,
                                  struct pa_sink_input *sinp)
{
    struct      pa_policy_group *group = NULL;
    struct      pa_sink_input_ext *ext;
    uint32_t    idx;
    const char *sinp_name;
    uint32_t    flags;

    if (sinp && u) {
        idx  = sinp->index;
        sinp_name = sink_input_ext_get_name(sinp->proplist);
        pa_assert_se((group = get_group_or_classify(u, sinp, &flags)));

        ext = pa_xmalloc0(sizeof(struct pa_sink_input_ext));
        ext->local.route = (flags & PA_POLICY_LOCAL_ROUTE) ? TRUE : FALSE;
        ext->local.mute  = (flags & PA_POLICY_LOCAL_MUTE ) ? TRUE : FALSE;
        pa_index_hash_add(u->hsi, idx, ext);

        pa_policy_context_register(u, pa_policy_object_sink_input, sinp_name, sinp);
        pa_policy_group_insert_sink_input(u, group->name, sinp, flags);

        pa_log_debug("new sink_input %s (idx=%u) (group=%s)", sinp_name, idx, group->name);
    }
}
struct pa_policy_context *pa_policy_context_new(struct userdata *u)
{
    struct pa_policy_context *ctx;

    ctx = pa_xmalloc0(sizeof(*ctx));

    return ctx;
}
Exemple #9
0
pa_srbchannel* pa_srbchannel_new(pa_mainloop_api *m, pa_mempool *p) {
    int capacity;
    int readfd;
    struct srbheader *srh;

    pa_srbchannel* sr = pa_xmalloc0(sizeof(pa_srbchannel));
    sr->mainloop = m;
    sr->memblock = pa_memblock_new_pool(p, -1);
    if (!sr->memblock)
        goto fail;

    srh = pa_memblock_acquire(sr->memblock);
    pa_zero(*srh);

    sr->rb_read.memory = (uint8_t*) srh + PA_ALIGN(sizeof(*srh));
    srh->readbuf_offset = sr->rb_read.memory - (uint8_t*) srh;

    capacity = (pa_memblock_get_length(sr->memblock) - srh->readbuf_offset) / 2;

    sr->rb_write.memory = PA_ALIGN_PTR(sr->rb_read.memory + capacity);
    srh->writebuf_offset = sr->rb_write.memory - (uint8_t*) srh;

    capacity = PA_MIN(capacity, srh->writebuf_offset - srh->readbuf_offset);

    pa_log_debug("SHM block is %d bytes, ringbuffer capacity is 2 * %d bytes",
        (int) pa_memblock_get_length(sr->memblock), capacity);

    srh->capacity = sr->rb_read.capacity = sr->rb_write.capacity = capacity;

    sr->rb_read.count = &srh->read_count;
    sr->rb_write.count = &srh->write_count;

    sr->sem_read = pa_fdsem_new_shm(&srh->read_semdata);
    if (!sr->sem_read)
        goto fail;

    sr->sem_write = pa_fdsem_new_shm(&srh->write_semdata);
    if (!sr->sem_write)
        goto fail;

    readfd = pa_fdsem_get(sr->sem_read);

#ifdef DEBUG_SRBCHANNEL
    pa_log("Enabling io event on fd %d", readfd);
#endif

    sr->read_event = m->io_new(m, readfd, PA_IO_EVENT_INPUT, semread_cb, sr);
    m->io_enable(sr->read_event, PA_IO_EVENT_INPUT);

    return sr;

fail:
    pa_srbchannel_free(sr);

    return NULL;
}
void voice_memchunk_pool_load(struct userdata *u) {
    int i;

    pa_assert(0 == offsetof(voice_memchunk_pool, chunk));
    pa_atomic_ptr_store(&u->memchunk_pool, NULL);

    voice_memchunk_pool_table = pa_xmalloc0(sizeof(voice_memchunk_pool)*VOICE_MEMCHUNK_POOL_SIZE);
    pa_assert(voice_memchunk_pool_table);

    for (i = 0; i<VOICE_MEMCHUNK_POOL_SIZE; i++)
        voice_memchunk_pool_free(u, (pa_memchunk *)&voice_memchunk_pool_table[i]);
}
Exemple #11
0
pa_idxset* pa_idxset_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func) {
    pa_idxset *s;

    s = pa_xmalloc0(PA_ALIGN(sizeof(pa_idxset)) + NBUCKETS*2*sizeof(struct idxset_entry*));

    s->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;
    s->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;

    s->current_index = 0;
    s->n_entries = 0;
    s->iterate_list_head = s->iterate_list_tail = NULL;

    return s;
}
Exemple #12
0
pa_flist *pa_flist_new(unsigned size) {
    pa_flist *l;

    if (!size)
        size = FLIST_SIZE;

    pa_assert(pa_is_power_of_two(size));

    l = pa_xmalloc0(PA_ALIGN(sizeof(pa_flist)) + (sizeof(pa_atomic_ptr_t) * size));

    l->size = size;

    pa_atomic_store(&l->read_idx, 0);
    pa_atomic_store(&l->write_idx, 0);
    pa_atomic_store(&l->length, 0);

    return l;
}
Exemple #13
0
pa_srbchannel* pa_srbchannel_new_from_template(pa_mainloop_api *m, pa_srbchannel_template *t)
{
    int temp;
    struct srbheader *srh;
    pa_srbchannel* sr = pa_xmalloc0(sizeof(pa_srbchannel));

    sr->mainloop = m;
    sr->memblock = t->memblock;
    pa_memblock_ref(sr->memblock);
    srh = pa_memblock_acquire(sr->memblock);

    sr->rb_read.capacity = sr->rb_write.capacity = srh->capacity;
    sr->rb_read.count = &srh->read_count;
    sr->rb_write.count = &srh->write_count;

    sr->rb_read.memory = (uint8_t*) srh + srh->readbuf_offset;
    sr->rb_write.memory = (uint8_t*) srh + srh->writebuf_offset;

    sr->sem_read = pa_fdsem_open_shm(&srh->read_semdata, t->readfd);
    if (!sr->sem_read)
        goto fail;

    sr->sem_write = pa_fdsem_open_shm(&srh->write_semdata, t->writefd);
    if (!sr->sem_write)
        goto fail;

    pa_srbchannel_swap(sr);
    temp = t->readfd; t->readfd = t->writefd; t->writefd = temp;

#ifdef DEBUG_SRBCHANNEL
    pa_log("Enabling io event on fd %d", t->readfd);
#endif

    sr->read_event = m->io_new(m, t->readfd, PA_IO_EVENT_INPUT, semread_cb, sr);
    m->io_enable(sr->read_event, PA_IO_EVENT_INPUT);

    return sr;

fail:
    pa_srbchannel_free(sr);

    return NULL;
}
void
pa_policy_context_set_default_action(struct pa_policy_context_rule *rule,
                                     int lineno,
                                     struct userdata *u,
                                     const char *activity_group,
                                     int default_state)
{
    union pa_policy_context_action *action;
    struct pa_policy_set_default   *setdef;

    action = pa_xmalloc0(sizeof(*action));
    setdef = &action->setdef;

    setdef->type   = pa_policy_set_default;
    setdef->lineno = lineno;

    pa_assert_se((setdef->var = get_activity_variable(u, u->context, activity_group)));
    setdef->default_state = default_state;

    append_action(&rule->actions, action);
}
void
pa_policy_context_delete_property_action(struct pa_policy_context_rule *rule,
                                         int                      lineno,
                                         enum pa_policy_object_type obj_type,
                                         enum pa_classify_method  obj_classify,
                                         const char              *obj_name,
                                         const char              *prop_name)
{
    union pa_policy_context_action *action;
    struct pa_policy_del_property  *delprop;

    action  = pa_xmalloc0(sizeof(*action));
    delprop = &action->delprop; 

    delprop->type   = pa_policy_delete_property;
    delprop->lineno = lineno;

    delprop->object.type = obj_type;
    match_setup(&delprop->object.match, obj_classify, obj_name, NULL);

    delprop->property = pa_xstrdup(prop_name);

    append_action(&rule->actions, action);
}
int pa__init(pa_module*m) {
    struct userdata *u;
    pa_sample_spec ss, sink_input_ss;
    pa_channel_map map, sink_input_map;
    pa_modargs *ma;
    pa_sink *master=NULL;
    pa_sink_input_new_data sink_input_data;
    pa_sink_new_data sink_data;
    bool use_volume_sharing = true;
    bool force_flat_volume = false;
    pa_memchunk silence;

    const char *hrir_file;
    unsigned i, j, found_channel_left, found_channel_right;
    float *hrir_data;

    pa_sample_spec hrir_ss;
    pa_channel_map hrir_map;

    pa_sample_spec hrir_temp_ss;
    pa_memchunk hrir_temp_chunk, hrir_temp_chunk_resampled;
    pa_resampler *resampler;

    size_t hrir_copied_length, hrir_total_length;

    hrir_temp_chunk.memblock = NULL;
    hrir_temp_chunk_resampled.memblock = NULL;

    pa_assert(m);

    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
        pa_log("Failed to parse module arguments.");
        goto fail;
    }

    if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK))) {
        pa_log("Master sink not found");
        goto fail;
    }

    pa_assert(master);

    u = pa_xnew0(struct userdata, 1);
    u->module = m;
    m->userdata = u;

    /* Initialize hrir and input buffer */
    /* this is the hrir file for the left ear! */
    if (!(hrir_file = pa_modargs_get_value(ma, "hrir", NULL))) {
        pa_log("The mandatory 'hrir' module argument is missing.");
        goto fail;
    }

    if (pa_sound_file_load(master->core->mempool, hrir_file, &hrir_temp_ss, &hrir_map, &hrir_temp_chunk, NULL) < 0) {
        pa_log("Cannot load hrir file.");
        goto fail;
    }

    /* sample spec / map of hrir */
    hrir_ss.format = PA_SAMPLE_FLOAT32;
    hrir_ss.rate = master->sample_spec.rate;
    hrir_ss.channels = hrir_temp_ss.channels;

    /* sample spec of sink */
    ss = hrir_ss;
    map = hrir_map;
    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
        pa_log("Invalid sample format specification or channel map");
        goto fail;
    }
    ss.format = PA_SAMPLE_FLOAT32;
    hrir_ss.rate = ss.rate;
    u->channels = ss.channels;

    if (pa_modargs_get_value_boolean(ma, "use_volume_sharing", &use_volume_sharing) < 0) {
        pa_log("use_volume_sharing= expects a boolean argument");
        goto fail;
    }

    if (pa_modargs_get_value_boolean(ma, "force_flat_volume", &force_flat_volume) < 0) {
        pa_log("force_flat_volume= expects a boolean argument");
        goto fail;
    }

    if (use_volume_sharing && force_flat_volume) {
        pa_log("Flat volume can't be forced when using volume sharing.");
        goto fail;
    }

    /* sample spec / map of sink input */
    pa_channel_map_init_stereo(&sink_input_map);
    sink_input_ss.channels = 2;
    sink_input_ss.format = PA_SAMPLE_FLOAT32;
    sink_input_ss.rate = ss.rate;

    u->sink_fs = pa_frame_size(&ss);
    u->fs = pa_frame_size(&sink_input_ss);

    /* Create sink */
    pa_sink_new_data_init(&sink_data);
    sink_data.driver = __FILE__;
    sink_data.module = m;
    if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
        sink_data.name = pa_sprintf_malloc("%s.vsurroundsink", master->name);
    pa_sink_new_data_set_sample_spec(&sink_data, &ss);
    pa_sink_new_data_set_channel_map(&sink_data, &map);
    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
    pa_proplist_sets(sink_data.proplist, "device.vsurroundsink.name", sink_data.name);

    if (pa_modargs_get_proplist(ma, "sink_properties", sink_data.proplist, PA_UPDATE_REPLACE) < 0) {
        pa_log("Invalid properties");
        pa_sink_new_data_done(&sink_data);
        goto fail;
    }

    if ((u->auto_desc = !pa_proplist_contains(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) {
        const char *z;

        z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
        pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Virtual Surround Sink %s on %s", sink_data.name, z ? z : master->name);
    }

    u->sink = pa_sink_new(m->core, &sink_data, (master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY))
                                               | (use_volume_sharing ? PA_SINK_SHARE_VOLUME_WITH_MASTER : 0));
    pa_sink_new_data_done(&sink_data);

    if (!u->sink) {
        pa_log("Failed to create sink.");
        goto fail;
    }

    u->sink->parent.process_msg = sink_process_msg_cb;
    u->sink->set_state = sink_set_state_cb;
    u->sink->update_requested_latency = sink_update_requested_latency_cb;
    u->sink->request_rewind = sink_request_rewind_cb;
    pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
    if (!use_volume_sharing) {
        pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
        pa_sink_enable_decibel_volume(u->sink, true);
    }
    /* Normally this flag would be enabled automatically be we can force it. */
    if (force_flat_volume)
        u->sink->flags |= PA_SINK_FLAT_VOLUME;
    u->sink->userdata = u;

    pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);

    /* Create sink input */
    pa_sink_input_new_data_init(&sink_input_data);
    sink_input_data.driver = __FILE__;
    sink_input_data.module = m;
    pa_sink_input_new_data_set_sink(&sink_input_data, master, false);
    sink_input_data.origin_sink = u->sink;
    pa_proplist_setf(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Virtual Surround Sink Stream from %s", pa_proplist_gets(u->sink->proplist, PA_PROP_DEVICE_DESCRIPTION));
    pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
    pa_sink_input_new_data_set_sample_spec(&sink_input_data, &sink_input_ss);
    pa_sink_input_new_data_set_channel_map(&sink_input_data, &sink_input_map);

    pa_sink_input_new(&u->sink_input, m->core, &sink_input_data);
    pa_sink_input_new_data_done(&sink_input_data);

    if (!u->sink_input)
        goto fail;

    u->sink_input->pop = sink_input_pop_cb;
    u->sink_input->process_rewind = sink_input_process_rewind_cb;
    u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
    u->sink_input->update_max_request = sink_input_update_max_request_cb;
    u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb;
    u->sink_input->update_sink_fixed_latency = sink_input_update_sink_fixed_latency_cb;
    u->sink_input->kill = sink_input_kill_cb;
    u->sink_input->attach = sink_input_attach_cb;
    u->sink_input->detach = sink_input_detach_cb;
    u->sink_input->state_change = sink_input_state_change_cb;
    u->sink_input->moving = sink_input_moving_cb;
    u->sink_input->volume_changed = use_volume_sharing ? NULL : sink_input_volume_changed_cb;
    u->sink_input->mute_changed = sink_input_mute_changed_cb;
    u->sink_input->userdata = u;

    u->sink->input_to_master = u->sink_input;

    pa_sink_input_get_silence(u->sink_input, &silence);
    u->memblockq = pa_memblockq_new("module-virtual-surround-sink memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0, &sink_input_ss, 1, 1, 0, &silence);
    pa_memblock_unref(silence.memblock);

    /* resample hrir */
    resampler = pa_resampler_new(u->sink->core->mempool, &hrir_temp_ss, &hrir_map, &hrir_ss, &hrir_map,
                                 PA_RESAMPLER_SRC_SINC_BEST_QUALITY, PA_RESAMPLER_NO_REMAP);

    u->hrir_samples = hrir_temp_chunk.length / pa_frame_size(&hrir_temp_ss) * hrir_ss.rate / hrir_temp_ss.rate;
    if (u->hrir_samples > 64) {
        u->hrir_samples = 64;
        pa_log("The (resampled) hrir contains more than 64 samples. Only the first 64 samples will be used to limit processor usage.");
    }

    hrir_total_length = u->hrir_samples * pa_frame_size(&hrir_ss);
    u->hrir_channels = hrir_ss.channels;

    u->hrir_data = (float *) pa_xmalloc(hrir_total_length);
    hrir_copied_length = 0;

    /* add silence to the hrir until we get enough samples out of the resampler */
    while (hrir_copied_length < hrir_total_length) {
        pa_resampler_run(resampler, &hrir_temp_chunk, &hrir_temp_chunk_resampled);
        if (hrir_temp_chunk.memblock != hrir_temp_chunk_resampled.memblock) {
            /* Silence input block */
            pa_silence_memblock(hrir_temp_chunk.memblock, &hrir_temp_ss);
        }

        if (hrir_temp_chunk_resampled.memblock) {
            /* Copy hrir data */
            hrir_data = (float *) pa_memblock_acquire(hrir_temp_chunk_resampled.memblock);

            if (hrir_total_length - hrir_copied_length >= hrir_temp_chunk_resampled.length) {
                memcpy(u->hrir_data + hrir_copied_length, hrir_data, hrir_temp_chunk_resampled.length);
                hrir_copied_length += hrir_temp_chunk_resampled.length;
            } else {
                memcpy(u->hrir_data + hrir_copied_length, hrir_data, hrir_total_length - hrir_copied_length);
                hrir_copied_length = hrir_total_length;
            }

            pa_memblock_release(hrir_temp_chunk_resampled.memblock);
            pa_memblock_unref(hrir_temp_chunk_resampled.memblock);
            hrir_temp_chunk_resampled.memblock = NULL;
        }
    }

    pa_resampler_free(resampler);

    pa_memblock_unref(hrir_temp_chunk.memblock);
    hrir_temp_chunk.memblock = NULL;

    if (hrir_map.channels < map.channels) {
        pa_log("hrir file does not have enough channels!");
        goto fail;
    }

    normalize_hrir(u);

    /* create mapping between hrir and input */
    u->mapping_left = (unsigned *) pa_xnew0(unsigned, u->channels);
    u->mapping_right = (unsigned *) pa_xnew0(unsigned, u->channels);
    for (i = 0; i < map.channels; i++) {
        found_channel_left = 0;
        found_channel_right = 0;

        for (j = 0; j < hrir_map.channels; j++) {
            if (hrir_map.map[j] == map.map[i]) {
                u->mapping_left[i] = j;
                found_channel_left = 1;
            }

            if (hrir_map.map[j] == mirror_channel(map.map[i])) {
                u->mapping_right[i] = j;
                found_channel_right = 1;
            }
        }

        if (!found_channel_left) {
            pa_log("Cannot find mapping for channel %s", pa_channel_position_to_string(map.map[i]));
            goto fail;
        }
        if (!found_channel_right) {
            pa_log("Cannot find mapping for channel %s", pa_channel_position_to_string(mirror_channel(map.map[i])));
            goto fail;
        }
    }

    u->input_buffer = pa_xmalloc0(u->hrir_samples * u->sink_fs);
    u->input_buffer_offset = 0;

    pa_sink_put(u->sink);
    pa_sink_input_put(u->sink_input);

    pa_modargs_free(ma);
    return 0;

fail:
    if (hrir_temp_chunk.memblock)
        pa_memblock_unref(hrir_temp_chunk.memblock);

    if (hrir_temp_chunk_resampled.memblock)
        pa_memblock_unref(hrir_temp_chunk_resampled.memblock);

    if (ma)
        pa_modargs_free(ma);

    pa__done(m);

    return -1;
}
static int handle_response(struct userdata *u) {
    pa_assert(u);

    switch (u->state) {

        case STATE_AUTH:
            pa_assert(u->read_length == sizeof(int32_t));

            /* Process auth data */
            if (!*(int32_t*) u->read_data) {
                pa_log("Authentication failed: %s", pa_cstrerror(errno));
                return -1;
            }

            /* Request latency data */
            pa_assert(!u->write_data);
            *(int32_t*) (u->write_data = pa_xmalloc(u->write_length = sizeof(int32_t))) = ESD_PROTO_LATENCY;

            u->write_index = 0;
            u->state = STATE_LATENCY;

            /* Space for next response */
            pa_assert(u->read_length >= sizeof(int32_t));
            u->read_index = 0;
            u->read_length = sizeof(int32_t);

            break;

        case STATE_LATENCY: {
            int32_t *p;
            pa_assert(u->read_length == sizeof(int32_t));

            /* Process latency info */
            u->latency = (pa_usec_t) ((double) (*(int32_t*) u->read_data) * 1000000 / 44100);
            if (u->latency > 10000000) {
                pa_log_warn("Invalid latency information received from server");
                u->latency = 0;
            }

            /* Create stream */
            pa_assert(!u->write_data);
            p = u->write_data = pa_xmalloc0(u->write_length = sizeof(int32_t)*3+ESD_NAME_MAX);
            *(p++) = ESD_PROTO_STREAM_PLAY;
            *(p++) = u->format;
            *(p++) = u->rate;
            pa_strlcpy((char*) p, "PulseAudio Tunnel", ESD_NAME_MAX);

            u->write_index = 0;
            u->state = STATE_PREPARE;

            /* Don't read any further */
            pa_xfree(u->read_data);
            u->read_data = NULL;
            u->read_index = u->read_length = 0;

            break;
        }

        default:
            pa_assert_not_reached();
    }

    return 0;
}