static int parse_pos(const char *pos, double *f) {

    if (pa_atod(pos, f) < 0) {
        pa_log_warn("Failed to parse hpos/vpos property '%s'.", pos);
        return -1;
    }

    if (*f < 0.0 || *f > 1.0) {
        pa_log_debug("Property hpos/vpos out of range %0.2f", *f);

        *f = PA_CLAMP(*f, 0.0, 1.0);
    }

    return 0;
}
Esempio n. 2
0
int pa__init(pa_module*m) {
    struct userdata *u;
    pa_sample_spec ss;
    pa_channel_map map;
    pa_modargs *ma;
    char *t;
    pa_sink *master;
    pa_sink_input_new_data sink_input_data;
    pa_sink_new_data sink_data;
    const char *plugin, *label;
    LADSPA_Descriptor_Function descriptor_func;
    const char *e, *cdata;
    const LADSPA_Descriptor *d;
    unsigned long input_port, output_port, p, j, n_control;
    unsigned c;
    pa_bool_t *use_default = NULL;

    pa_assert(m);

    pa_assert_cc(sizeof(LADSPA_Data) == sizeof(float));

    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;
    }

    ss = master->sample_spec;
    ss.format = PA_SAMPLE_FLOAT32;
    map = master->channel_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;
    }

    if (!(plugin = pa_modargs_get_value(ma, "plugin", NULL))) {
        pa_log("Missing LADSPA plugin name");
        goto fail;
    }

    if (!(label = pa_modargs_get_value(ma, "label", NULL))) {
        pa_log("Missing LADSPA plugin label");
        goto fail;
    }

    cdata = pa_modargs_get_value(ma, "control", NULL);

    u = pa_xnew0(struct userdata, 1);
    u->module = m;
    m->userdata = u;
    u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL);

    if (!(e = getenv("LADSPA_PATH")))
        e = LADSPA_PATH;

    /* FIXME: This is not exactly thread safe */
    t = pa_xstrdup(lt_dlgetsearchpath());
    lt_dlsetsearchpath(e);
    m->dl = lt_dlopenext(plugin);
    lt_dlsetsearchpath(t);
    pa_xfree(t);

    if (!m->dl) {
        pa_log("Failed to load LADSPA plugin: %s", lt_dlerror());
        goto fail;
    }

    if (!(descriptor_func = (LADSPA_Descriptor_Function) pa_load_sym(m->dl, NULL, "ladspa_descriptor"))) {
        pa_log("LADSPA module lacks ladspa_descriptor() symbol.");
        goto fail;
    }

    for (j = 0;; j++) {

        if (!(d = descriptor_func(j))) {
            pa_log("Failed to find plugin label '%s' in plugin '%s'.", label, plugin);
            goto fail;
        }

        if (strcmp(d->Label, label) == 0)
            break;
    }

    u->descriptor = d;

    pa_log_debug("Module: %s", plugin);
    pa_log_debug("Label: %s", d->Label);
    pa_log_debug("Unique ID: %lu", d->UniqueID);
    pa_log_debug("Name: %s", d->Name);
    pa_log_debug("Maker: %s", d->Maker);
    pa_log_debug("Copyright: %s", d->Copyright);

    input_port = output_port = (unsigned long) -1;
    n_control = 0;

    for (p = 0; p < d->PortCount; p++) {

        if (LADSPA_IS_PORT_INPUT(d->PortDescriptors[p]) && LADSPA_IS_PORT_AUDIO(d->PortDescriptors[p])) {

            if (strcmp(d->PortNames[p], "Input") == 0) {
                pa_assert(input_port == (unsigned long) -1);
                input_port = p;
            } else {
                pa_log("Found audio input port on plugin we cannot handle: %s", d->PortNames[p]);
                goto fail;
            }

        } else if (LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p]) && LADSPA_IS_PORT_AUDIO(d->PortDescriptors[p])) {

            if (strcmp(d->PortNames[p], "Output") == 0) {
                pa_assert(output_port == (unsigned long) -1);
                output_port = p;
            } else {
                pa_log("Found audio output port on plugin we cannot handle: %s", d->PortNames[p]);
                goto fail;
            }

        } else if (LADSPA_IS_PORT_INPUT(d->PortDescriptors[p]) && LADSPA_IS_PORT_CONTROL(d->PortDescriptors[p]))
            n_control++;
        else {
            pa_assert(LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p]) && LADSPA_IS_PORT_CONTROL(d->PortDescriptors[p]));
            pa_log_debug("Ignored control output port \"%s\".", d->PortNames[p]);
        }
    }

    if ((input_port == (unsigned long) -1) || (output_port == (unsigned long) -1)) {
        pa_log("Failed to identify input and output ports. "
               "Right now this module can only deal with plugins which provide an 'Input' and an 'Output' audio port. "
               "Patches welcome!");
        goto fail;
    }

    u->block_size = pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss);

    u->input = (LADSPA_Data*) pa_xnew(uint8_t, (unsigned) u->block_size);
    if (LADSPA_IS_INPLACE_BROKEN(d->Properties))
        u->output = (LADSPA_Data*) pa_xnew(uint8_t, (unsigned) u->block_size);
    else
        u->output = u->input;

    u->channels = ss.channels;

    for (c = 0; c < ss.channels; c++) {
        if (!(u->handle[c] = d->instantiate(d, ss.rate))) {
            pa_log("Failed to instantiate plugin %s with label %s for channel %i", plugin, d->Label, c);
            goto fail;
        }

        d->connect_port(u->handle[c], input_port, u->input);
        d->connect_port(u->handle[c], output_port, u->output);
    }

    if (!cdata && n_control > 0) {
        pa_log("This plugin requires specification of %lu control parameters.", n_control);
        goto fail;
    }

    if (n_control > 0) {
        const char *state = NULL;
        char *k;
        unsigned long h;

        u->control = pa_xnew(LADSPA_Data, (unsigned) n_control);
        use_default = pa_xnew(pa_bool_t, (unsigned) n_control);
        p = 0;

        while ((k = pa_split(cdata, ",", &state)) && p < n_control) {
            double f;

            if (*k == 0) {
                use_default[p++] = TRUE;
                pa_xfree(k);
                continue;
            }

            if (pa_atod(k, &f) < 0) {
                pa_log("Failed to parse control value '%s'", k);
                pa_xfree(k);
                goto fail;
            }

            pa_xfree(k);

            use_default[p] = FALSE;
            u->control[p++] = (LADSPA_Data) f;
        }

        /* The previous loop doesn't take the last control value into account
           if it is left empty, so we do it here. */
        if (*cdata == 0 || cdata[strlen(cdata) - 1] == ',') {
            if (p < n_control)
                use_default[p] = TRUE;
            p++;
        }

        if (p > n_control || k) {
            pa_log("Too many control values passed, %lu expected.", n_control);
            pa_xfree(k);
            goto fail;
        }

        if (p < n_control) {
            pa_log("Not enough control values passed, %lu expected, %lu passed.", n_control, p);
            goto fail;
        }

        h = 0;
        for (p = 0; p < d->PortCount; p++) {
            LADSPA_PortRangeHintDescriptor hint = d->PortRangeHints[p].HintDescriptor;

            if (!LADSPA_IS_PORT_CONTROL(d->PortDescriptors[p]))
                continue;

            if (LADSPA_IS_PORT_OUTPUT(d->PortDescriptors[p])) {
                for (c = 0; c < ss.channels; c++)
                    d->connect_port(u->handle[c], p, &u->control_out);
                continue;
            }

            pa_assert(h < n_control);

            if (use_default[h]) {
                LADSPA_Data lower, upper;

                if (!LADSPA_IS_HINT_HAS_DEFAULT(hint)) {
                    pa_log("Control port value left empty but plugin defines no default.");
                    goto fail;
                }

                lower = d->PortRangeHints[p].LowerBound;
                upper = d->PortRangeHints[p].UpperBound;

                if (LADSPA_IS_HINT_SAMPLE_RATE(hint)) {
                    lower *= (LADSPA_Data) ss.rate;
                    upper *= (LADSPA_Data) ss.rate;
                }

                switch (hint & LADSPA_HINT_DEFAULT_MASK) {

                    case LADSPA_HINT_DEFAULT_MINIMUM:
                        u->control[h] = lower;
                        break;

                    case LADSPA_HINT_DEFAULT_MAXIMUM:
                        u->control[h] = upper;
                        break;

                    case LADSPA_HINT_DEFAULT_LOW:
                        if (LADSPA_IS_HINT_LOGARITHMIC(hint))
                            u->control[h] = (LADSPA_Data) exp(log(lower) * 0.75 + log(upper) * 0.25);
                        else
                            u->control[h] = (LADSPA_Data) (lower * 0.75 + upper * 0.25);
                        break;

                    case LADSPA_HINT_DEFAULT_MIDDLE:
                        if (LADSPA_IS_HINT_LOGARITHMIC(hint))
                            u->control[h] = (LADSPA_Data) exp(log(lower) * 0.5 + log(upper) * 0.5);
                        else
                            u->control[h] = (LADSPA_Data) (lower * 0.5 + upper * 0.5);
                        break;

                    case LADSPA_HINT_DEFAULT_HIGH:
                        if (LADSPA_IS_HINT_LOGARITHMIC(hint))
                            u->control[h] = (LADSPA_Data) exp(log(lower) * 0.25 + log(upper) * 0.75);
                        else
                            u->control[h] = (LADSPA_Data) (lower * 0.25 + upper * 0.75);
                        break;

                    case LADSPA_HINT_DEFAULT_0:
                        u->control[h] = 0;
                        break;

                    case LADSPA_HINT_DEFAULT_1:
                        u->control[h] = 1;
                        break;

                    case LADSPA_HINT_DEFAULT_100:
                        u->control[h] = 100;
                        break;

                    case LADSPA_HINT_DEFAULT_440:
                        u->control[h] = 440;
                        break;

                    default:
                        pa_assert_not_reached();
                }
            }

            if (LADSPA_IS_HINT_INTEGER(hint))
                u->control[h] = roundf(u->control[h]);

            pa_log_debug("Binding %f to port %s", u->control[h], d->PortNames[p]);

            for (c = 0; c < ss.channels; c++)
                d->connect_port(u->handle[c], p, &u->control[h]);

            h++;
        }

        pa_assert(h == n_control);
    }

    if (d->activate)
        for (c = 0; c < u->channels; c++)
            d->activate(u->handle[c]);

    /* 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.ladspa", 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.ladspa.module", plugin);
    pa_proplist_sets(sink_data.proplist, "device.ladspa.label", d->Label);
    pa_proplist_sets(sink_data.proplist, "device.ladspa.name", d->Name);
    pa_proplist_sets(sink_data.proplist, "device.ladspa.maker", d->Maker);
    pa_proplist_sets(sink_data.proplist, "device.ladspa.copyright", d->Copyright);
    pa_proplist_setf(sink_data.proplist, "device.ladspa.unique_id", "%lu", (unsigned long) d->UniqueID);

    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, "LADSPA Plugin %s on %s", d->Name, z ? z : master->name);
    }

    u->sink = pa_sink_new(m->core, &sink_data,
                          PA_SINK_HW_MUTE_CTRL|PA_SINK_HW_VOLUME_CTRL|PA_SINK_DECIBEL_VOLUME|
                          (master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY)));
    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;
    u->sink->set_volume = sink_set_volume_cb;
    u->sink->set_mute = sink_set_mute_cb;
    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;
    sink_input_data.sink = master;
    pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "LADSPA Stream");
    pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
    pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
    pa_sink_input_new_data_set_channel_map(&sink_input_data, &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->may_move_to = sink_input_may_move_to_cb;
    u->sink_input->moving = sink_input_moving_cb;
    u->sink_input->volume_changed = sink_input_volume_changed_cb;
    u->sink_input->mute_changed = sink_input_mute_changed_cb;
    u->sink_input->userdata = u;

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

    pa_modargs_free(ma);

    pa_xfree(use_default);

    return 0;

fail:
    if (ma)
        pa_modargs_free(ma);

    pa_xfree(use_default);

    pa__done(m);

    return -1;
}
void voice_update_parameters(struct userdata *u)
{
  pa_sink *sink;
  const char *s;
  double tmp_d, old_d;
  int tmp, old;
  size_t nbytes;
  const void *data;

  ENTER();

  sink = voice_get_original_master_sink(u);

  if (!sink)
  {
      pa_log_warn("Original master sink not found, parameters not updated.");
      return;
  }

  u->updating_parameters = TRUE;

  if (!pa_proplist_get(sink->proplist, "x-maemo.xprot.parameters.left", &data, &nbytes))
    xprot_change_params(u->xprot, data, nbytes, 0);

  if (!pa_proplist_get(sink->proplist,"x-maemo.xprot.parameters.right", &data, &nbytes))
      xprot_change_params(u->xprot, data, nbytes, 1);

  s = voice_pa_proplist_gets(sink->proplist, "x-maemo.cmt.ul_timing_advance");
  old = u->ul_timing_advance;
  if (!pa_atoi(s, &tmp) && tmp > -5000 && tmp < 5000)
    u->ul_timing_advance = tmp;
  pa_log_debug("cmt_ul_timing_advance \"%s\" %d %d", s, u->ul_timing_advance, old);

  s = voice_pa_proplist_gets(sink->proplist, "x-maemo.alt_mixer_compensation");
  /* CHECKME */
  old_d = u->alt_mixer_compensation;
  if (!pa_atod(s, &tmp_d) && tmp_d > 0.0 && tmp_d <= 60.0) /* < 60.0 ? */
    u->alt_mixer_compensation = pa_sw_volume_from_dB(tmp_d);
  pa_log_debug("alt_mixer_compensation \"%s\" %d %f", s, u->alt_mixer_compensation, old_d);

  s = voice_pa_proplist_gets(sink->proplist, "x-maemo.ear_ref_padding");
  old = u->ear_ref.loop_padding_usec ;
  if (!pa_atoi(s, &tmp) && tmp > -10000 && tmp < 199999)
    u->ear_ref.loop_padding_usec  = tmp;
  pa_log_debug("ear_ref_padding \"%s\" %d %d", s, u->ear_ref.loop_padding_usec, old);

  voice_parse_aep_steps(u, voice_pa_proplist_gets(sink->proplist, "x-maemo.audio_aep_mb_steps"));

  s = voice_pa_proplist_gets(sink->proplist, "x-maemo.nrec");
  u->nrec_enable = pa_parse_boolean(s);

  if (u->master_source &&
      pa_proplist_gets(u->master_source->proplist, "bluetooth.nrec"))
  {
    /* WTF ?!? */
    u->sidetone_enable = pa_parse_boolean(s) && u->nrec_enable;
  }

  if (!pa_proplist_get(sink->proplist, "x-maemo.aep.switches",
                       (const void **)&data, &nbytes) )
  {
    uint16_t *as = (uint16_t *)data;

    aep_switches.field_0 = as[0];
    aep_switches.field_2 = as[1];
    aep_switches.field_4 = 0;

    if ( aep_switches.field_0 & 0x400 )
      aep_switches.field_4 = 0x30;

    if (aep_switches.field_0 & 1)
      aep_switches.field_4 |= 0x300u;

    aep_switches.field_4 |= 0x1800u;
  }

  if (!pa_proplist_get(sink->proplist, "x-maemo.aep.parameters", &data, &nbytes))
  {
    const char *argv[7] =
    {
      "../execute/d4gnt560",
      "b-ai-1n------0---u",
      "/dev/null",
      "/dev/null",
      "/dev/null",
      "/dev/null",
      "/dev/null",
    };

    if (strlen(aep_runtime_switch) >= strlen(argv[1]))
      argv[1] = aep_runtime_switch;

    fprintf(stderr, "AEP runtime switch %s\n", argv[1]);

    current_aep_tuning = (void *)data;

    init_main(7, argv);
    voice_aep_ear_ref_loop_reset(u);
  }

  sidetone_write_parameters(u);

  if (!pa_proplist_get(sink->proplist, "x-maemo.wb_meq.parameters", &data, &nbytes))
    iir_eq_change_params(u->wb_mic_iir_eq, data, nbytes);

  if (!pa_proplist_get(sink->proplist, "x-maemo.nb_meq.parameters", &data, &nbytes) )
    iir_eq_change_params(u->nb_mic_iir_eq, data, nbytes);

  if (!pa_proplist_get(sink->proplist,"x-maemo.wb_eeq.parameters", &data, &nbytes))
    fir_eq_change_params(u->wb_ear_iir_eq, data, nbytes);

  if (!pa_proplist_get(sink->proplist, "x-maemo.nb_eeq.parameters", &data, &nbytes))
    iir_eq_change_params(u->nb_ear_iir_eq, data, nbytes);

  u->aep_enable = voice_pa_proplist_get_bool(sink->proplist, "x-maemo.aep");
  u->wb_meq_enable = voice_pa_proplist_get_bool(sink->proplist, "x-maemo.wb_meq");
  u->wb_eeq_enable = voice_pa_proplist_get_bool(sink->proplist, "x-maemo.wb_eeq");
  u->nb_meq_enable = voice_pa_proplist_get_bool(sink->proplist, "x-maemo.nb_meq");
  u->nb_eeq_enable = voice_pa_proplist_get_bool(sink->proplist, "x-maemo.nb_eeq");
  u->xprot->displ_limit = voice_pa_proplist_get_bool(sink->proplist, "x-maemo.xprot.displacement");
  u->xprot->temp_limit = voice_pa_proplist_get_bool(sink->proplist, "x-maemo.xprot.temperature");
  u->xprot_enable = u->xprot->displ_limit || u->xprot->temp_limit;

  u->updating_parameters = FALSE;
}