static void run_basic_tests(struct userdata *u) {

    /* Subscribe to updates. Note that "alg_b" wants full updates. */
    meego_parameter_request_updates("alg_a", (pa_hook_cb_t)parameters_changed_cb, PA_HOOK_NORMAL, FALSE, &u->alg_a);
    meego_parameter_request_updates("alg_b", (pa_hook_cb_t)parameters_changed_cb, PA_HOOK_NORMAL, TRUE,  &u->alg_b);
    meego_parameter_request_updates("alg_c", (pa_hook_cb_t)parameters_changed_cb, PA_HOOK_NORMAL, FALSE, &u->alg_c);

    disable_algs(u);

    /* Shuffle the modes around a bit to verify correct parameters and status enums */
    switch_mode(u, "mode_a");
    verify(&u->alg_a, "mode_a", "set_a1_parameters", MEEGO_PARAM_UPDATE);
    verify(&u->alg_b, "mode_a", "set_b1_parameters", MEEGO_PARAM_UPDATE);

    switch_mode(u, "mode_b");
    verify(&u->alg_a, "mode_b", "set_a2_parameters", MEEGO_PARAM_UPDATE);
    verify(&u->alg_b, "mode_b", NULL, MEEGO_PARAM_MODE_CHANGE);

    switch_mode(u, "mode_c");
    verify(&u->alg_a, "mode_c", "set_a3_parameters", MEEGO_PARAM_UPDATE);
    verify(&u->alg_b, "mode_c", "set_b2_parameters", MEEGO_PARAM_UPDATE);

    switch_mode(u, "mode_d");
    verify(&u->alg_a, "mode_d", NULL, MEEGO_PARAM_DISABLE);
    verify(&u->alg_b, "mode_d", NULL, MEEGO_PARAM_DISABLE);
    verify(&u->alg_c, "mode_d", "set_c1_parameters", MEEGO_PARAM_UPDATE);

    switch_mode(u, "mode_c");
    verify(&u->alg_a, "mode_c", NULL, MEEGO_PARAM_ENABLE);
    verify(&u->alg_b, "mode_c", NULL, MEEGO_PARAM_ENABLE);
    verify(&u->alg_c, "mode_c", NULL, MEEGO_PARAM_DISABLE);

    disable_algs(u); /* -> mode_reset2 */

    /* Stop updates to verify that it's really working */
    meego_parameter_stop_updates("alg_a", (pa_hook_cb_t)parameters_changed_cb, &u->alg_a);
    meego_parameter_stop_updates("alg_b", (pa_hook_cb_t)parameters_changed_cb, &u->alg_b);
    meego_parameter_stop_updates("alg_c", (pa_hook_cb_t)parameters_changed_cb, &u->alg_c);

    /* This mode switch should now have no effect */
    switch_mode(u, "mode_a");
    verify(&u->alg_a, "mode_reset2", NULL, MEEGO_PARAM_DISABLE);
    verify(&u->alg_b, "mode_reset2", NULL, MEEGO_PARAM_DISABLE);
    verify(&u->alg_c, "mode_reset2", NULL, MEEGO_PARAM_DISABLE);

    /* This should cause updates to mode_a */
    meego_parameter_request_updates("alg_a", (pa_hook_cb_t)parameters_changed_cb, PA_HOOK_NORMAL, FALSE, &u->alg_a);
    meego_parameter_request_updates("alg_b", (pa_hook_cb_t)parameters_changed_cb, PA_HOOK_NORMAL, TRUE,  &u->alg_b);
    meego_parameter_request_updates("alg_c", (pa_hook_cb_t)parameters_changed_cb, PA_HOOK_NORMAL, FALSE, &u->alg_c);

    verify(&u->alg_a, "mode_a", "set_a1_parameters", MEEGO_PARAM_UPDATE);
    verify(&u->alg_b, "mode_a", "set_b1_parameters", MEEGO_PARAM_UPDATE);
    verify(&u->alg_c, "mode_a", NULL, MEEGO_PARAM_DISABLE);

    meego_parameter_stop_updates("alg_a", (pa_hook_cb_t)parameters_changed_cb, &u->alg_a);
    meego_parameter_stop_updates("alg_b", (pa_hook_cb_t)parameters_changed_cb, &u->alg_b);
    meego_parameter_stop_updates("alg_c", (pa_hook_cb_t)parameters_changed_cb, &u->alg_c);

    disable_algs(u);
}
static void run_modifier_tests(struct userdata *u) {
    meego_parameter_modifier modifier_a;
    meego_parameter_modifier modifier_b;
    meego_parameter_modifier modifier_c;

    modifier_a.mode_name = "mode_a";
    modifier_a.algorithm_name = "alg_a";
    modifier_a.get_parameters = get_parameters_cb;
    modifier_a.userdata = &u->alg_a;

    modifier_b.mode_name = "mode_b";
    modifier_b.algorithm_name = "alg_b";
    modifier_b.get_parameters = failing_get_parameters_cb;
    modifier_b.userdata = &u->alg_b;

    /* This modifier doesn't have base parameters in the file system */
    modifier_c.mode_name = "mode_c";
    modifier_c.algorithm_name = "alg_c";
    modifier_c.get_parameters = get_parameters_cb;
    modifier_c.userdata = &u->alg_c;

    disable_algs(u);

    meego_parameter_request_updates("alg_a", (pa_hook_cb_t)parameters_changed_cb, PA_HOOK_NORMAL, FALSE, &u->alg_a);
    meego_parameter_request_updates("alg_b", (pa_hook_cb_t)parameters_changed_cb, PA_HOOK_NORMAL, TRUE,  &u->alg_b);
    meego_parameter_request_updates("alg_c", (pa_hook_cb_t)parameters_changed_cb, PA_HOOK_NORMAL, FALSE, &u->alg_c);

    switch_mode(u, "mode_a");
    verify(&u->alg_a, "mode_a", "set_a1_parameters", MEEGO_PARAM_UPDATE);
    verify(&u->alg_b, "mode_a", "set_b1_parameters", MEEGO_PARAM_UPDATE);

    /* Register the modifiers. This should instantly trigger the modified update for "alg_a" since we're in "mode_a" */
    meego_parameter_register_modifier(&modifier_a);
    meego_parameter_register_modifier(&modifier_b);

    verify(&u->alg_a, "mode_a", "set_a1_parameters-modified", MEEGO_PARAM_UPDATE);
    verify(&u->alg_b, "mode_a", "set_b1_parameters", MEEGO_PARAM_UPDATE); /* Unchanged */

    switch_mode(u, "mode_b");
    verify(&u->alg_a, "mode_b", "set_a2_parameters", MEEGO_PARAM_UPDATE);
    verify(&u->alg_b, "mode_b", NULL, MEEGO_PARAM_MODE_CHANGE); /* The modifier fails, so this is the expected result */

    switch_mode(u, "mode_c");
    verify(&u->alg_a, "mode_c", "set_a3_parameters", MEEGO_PARAM_UPDATE);
    verify(&u->alg_b, "mode_c", "set_b2_parameters", MEEGO_PARAM_UPDATE);

    /* Let's unregister the modifiers and see that things work as before */
    meego_parameter_unregister_modifier(&modifier_a);
    meego_parameter_unregister_modifier(&modifier_b);

    switch_mode(u, "mode_a");
    verify(&u->alg_a, "mode_a", "set_a1_parameters", MEEGO_PARAM_UPDATE);
    verify(&u->alg_b, "mode_a", "set_b1_parameters", MEEGO_PARAM_UPDATE);

    /* Try the modifier that has no base parameters */
    meego_parameter_register_modifier(&modifier_c);

    switch_mode(u, "mode_c");
    verify(&u->alg_a, "mode_c", "set_a3_parameters", MEEGO_PARAM_UPDATE);
    verify(&u->alg_b, "mode_c", "set_b2_parameters", MEEGO_PARAM_UPDATE);
    verify(&u->alg_c, "mode_c", "modified", MEEGO_PARAM_UPDATE); /* Withóut the modifier this would be disabled */

    /* Now register a modifier when we're not in the affected mode */
    meego_parameter_register_modifier(&modifier_a);

    /* Switch to the affected mode and verify successful modification */
    switch_mode(u, "mode_a");
    verify(&u->alg_a, "mode_a", "set_a1_parameters-modified", MEEGO_PARAM_UPDATE);
    verify(&u->alg_b, "mode_a", "set_b1_parameters", MEEGO_PARAM_UPDATE);

    meego_parameter_unregister_modifier(&modifier_a);

    meego_parameter_stop_updates("alg_a", (pa_hook_cb_t)parameters_changed_cb, &u->alg_a);
    meego_parameter_stop_updates("alg_b", (pa_hook_cb_t)parameters_changed_cb, &u->alg_b);
    meego_parameter_stop_updates("alg_c", (pa_hook_cb_t)parameters_changed_cb, &u->alg_c);

    meego_parameter_unregister_modifier(&modifier_c);

    disable_algs(u);
}
int pa__init(pa_module*m) {
    pa_modargs *ma = NULL;
    struct userdata *u;
    const char *master_sink_name;
    const char *master_source_name;
    const char *raw_sink_name;
    const char *raw_source_name;
    const char *voice_sink_name;
    const char *voice_source_name;
    const char *max_hw_frag_size_str;
    int max_hw_frag_size = 3840;
    pa_sink *master_sink;
    pa_source *master_source;

    pa_assert(m);

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

    master_sink_name = pa_modargs_get_value(ma, "master_sink", NULL);
    master_source_name = pa_modargs_get_value(ma, "master_source", NULL);

    raw_sink_name = pa_modargs_get_value(ma, "raw_sink_name", "sink.voice.raw");
    raw_source_name = pa_modargs_get_value(ma, "raw_source_name", "source.voice.raw");
    voice_sink_name = pa_modargs_get_value(ma, "voice_sink_name", "sink.voice");
    voice_source_name = pa_modargs_get_value(ma, "voice_source_name", "source.voice");
    max_hw_frag_size_str = pa_modargs_get_value(ma, "max_hw_frag_size", "3840");

    pa_log_debug("Got arguments: master_sink=\"%s\" master_source=\"%s\" "
                 "raw_sink_name=\"%s\" raw_source_name=\"%s\" max_hw_frag_size=\"%s\".",
                 master_sink_name, master_source_name,
                 raw_sink_name, raw_source_name,
                 max_hw_frag_size_str);

    if (!(master_sink = pa_namereg_get(m->core, master_sink_name, PA_NAMEREG_SINK))) {
        pa_log("Master sink \"%s\" not found", master_sink_name);
        goto fail;
    }

    if (!(master_source = pa_namereg_get(m->core, master_source_name, PA_NAMEREG_SOURCE))) {
        pa_log("Master source \"%s\" not found", master_source_name);
        goto fail;
    }

    if (master_sink->sample_spec.format != master_source->sample_spec.format &&
        master_sink->sample_spec.rate != master_source->sample_spec.rate &&
        master_sink->sample_spec.channels != master_source->sample_spec.channels) {
        pa_log("Master source and sink must have same sample spec");
        goto fail;
    }

    if (pa_atoi(max_hw_frag_size_str, &max_hw_frag_size) < 0 ||
        max_hw_frag_size < 960 ||
        max_hw_frag_size > 128*960) {
        pa_log("Bad value for max_hw_frag_size: %s", max_hw_frag_size_str);
        goto fail;
    }

    m->userdata = u = pa_xnew0(struct userdata, 1);
    u->modargs = ma;
    u->core = m->core;
    u->module = m;
    u->master_sink = master_sink;
    u->master_source = master_source;

    set_hooks(u);

    u->mainloop_handler = voice_mainloop_handler_new(u);

    u->ul_timing_advance = 500; // = 500 micro seconds, seems to be a good default value

    pa_channel_map_init_mono(&u->mono_map);
    pa_channel_map_init_stereo(&u->stereo_map);

    u->hw_sample_spec.format = PA_SAMPLE_S16NE;
    u->hw_sample_spec.rate = VOICE_SAMPLE_RATE_HW_HZ;
    u->hw_sample_spec.channels = 2;

    u->hw_mono_sample_spec.format = PA_SAMPLE_S16NE;
    u->hw_mono_sample_spec.rate = VOICE_SAMPLE_RATE_HW_HZ;
    u->hw_mono_sample_spec.channels = 1;

    u->aep_sample_spec.format = PA_SAMPLE_S16NE;
    u->aep_sample_spec.rate = VOICE_SAMPLE_RATE_AEP_HZ;
    u->aep_sample_spec.channels = 1;
    pa_channel_map_init_mono(&u->aep_channel_map);
    // The result is rounded down incorrectly thus +1
    u->aep_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_AEP_USECS+1, &u->aep_sample_spec);
    u->aep_hw_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_AEP_USECS+1, &u->hw_sample_spec);
    u->hw_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_MASTER_USECS+1, &u->hw_sample_spec);
    u->hw_fragment_size_max = max_hw_frag_size;
    if (0 != (u->hw_fragment_size_max % u->hw_fragment_size))
        u->hw_fragment_size_max += u->hw_fragment_size - (u->hw_fragment_size_max % u->hw_fragment_size);
    u->aep_hw_mono_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_AEP_USECS+1, &u->hw_mono_sample_spec);
    u->hw_mono_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_MASTER_USECS+1, &u->hw_mono_sample_spec);

    u->voice_ul_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_CMT_USECS+1, &u->aep_sample_spec);
    pa_silence_memchunk_get(&u->core->silence_cache,
                            u->core->mempool,
                            &u->aep_silence_memchunk,
                            & u->aep_sample_spec,
                            u->aep_fragment_size);

    voice_memchunk_pool_load(u);

    if (voice_init_raw_sink(u, raw_sink_name))
        goto fail;

    u->call_state_tracker = pa_call_state_tracker_get(m->core);

    pa_atomic_store(&u->mixer_state, PROP_MIXER_TUNING_PRI);
    pa_call_state_tracker_set_active(u->call_state_tracker, FALSE);
    u->alt_mixer_compensation = PA_VOLUME_NORM;

    if (voice_init_hw_sink_input(u))
        goto fail;

    /* This must be set before calling pa_sink_put(), because pa_sink_put() has
     * assertion
     * "!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) || s->flat_sink_input".
     */
    u->raw_sink->flat_sink_input = u->hw_sink_input;

    /* This must be called before calling voice_init_voip_sink(), because
     * pa_sink_input_new() has assertion
     * "PA_SINK_IS_LINKED(pa_sink_get_state(data->sink))". */
    pa_sink_put(u->raw_sink);

    /* This must be called before calling voice_init_aep_sink_input(), because
     * the flat volume logic will otherwise mess up the aep sink input's volume
     * when pa_sink_input_put(u->hw_sink_input) is called. */
    pa_sink_input_put(u->hw_sink_input);

    if (voice_init_voip_sink(u, voice_sink_name))
        goto fail;

    if (voice_init_aep_sink_input(u))
        goto fail;

    u->sink_temp_buff = pa_xmalloc(2*u->hw_fragment_size_max);
    u->sink_temp_buff_len = 2*u->hw_fragment_size_max;

    if (voice_init_raw_source(u, raw_source_name))
        goto fail;
    pa_source_put(u->raw_source);

    if (voice_init_voip_source(u, voice_source_name))
        goto fail;
    pa_source_put(u->voip_source);

    if (voice_init_hw_source_output(u))
        goto fail;

    /* TODO: Guess we should use max_hw_frag_size here */
    u->hw_source_memblockq = // 8 * 5ms = 40ms
        pa_memblockq_new(0, 2*u->hw_fragment_size_max, 0, pa_frame_size(&u->hw_sample_spec), 0, 0, 0, NULL);

    u->ul_memblockq =
        pa_memblockq_new(0, 2*u->voice_ul_fragment_size, 0, pa_frame_size(&u->aep_sample_spec), 0, 0, 0, NULL);

    u->dl_sideinfo_queue = pa_queue_new();

    u->ul_deadline = 0;

    u->linear_q15_master_volume_L = INT16_MAX;
    u->linear_q15_master_volume_R = INT16_MAX;

    voice_aep_ear_ref_init(u);

    if (voice_convert_init(u))
        goto fail;

    /* IHF mode is the default and this initialization is consistent with it. */
    u->active_mic_channel = MIC_CH0;

    meego_parameter_request_updates("voice", (pa_hook_cb_t)voice_parameter_cb, PA_HOOK_NORMAL, FALSE, u);
    meego_parameter_request_updates("alsa", (pa_hook_cb_t)alsa_parameter_cb, PA_HOOK_NORMAL, FALSE, u);
    meego_parameter_request_updates("aep", (pa_hook_cb_t)aep_parameter_cb, PA_HOOK_LATE, FALSE, u);

    /*         aep-s-i                                            */
    /* voip-sink ---\                 hw-sink-input               */
    /*                > optimized mix -------------> master-sink  */
    /*                |                                           */
    /*             raw-sink                                       */

    /*                                                  */
    /* voip-src  <---       hw-source-output            */
    /*                < mux <------------- master-src   */
    /*  raw-src  <---                                   */

    u->voip_sink->flat_sink_input = u->aep_sink_input;

    pa_sink_put(u->voip_sink);

    pa_source_output_put(u->hw_source_output);
    pa_sink_input_put(u->aep_sink_input);

    u->sink_subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SINK_INPUT, master_sink_volume_subscribe_cb, u);

    u->previous_master_source_state = pa_source_get_state(u->master_source);
    u->source_change_subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SOURCE, master_source_state_subscribe_cb, u);
    return 0;

fail:
    pa__done(m);
    return -1;
}