예제 #1
0
/* Called from main context */
int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t save) {
    int r;

    pa_source_output_assert_ref(o);
    pa_assert_ctl_context();
    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
    pa_assert(o->source);
    pa_source_assert_ref(dest);

    if (dest == o->source)
        return 0;

    if (!pa_source_output_may_move_to(o, dest))
        return -PA_ERR_NOTSUPPORTED;

    pa_source_output_ref(o);

    if ((r = pa_source_output_start_move(o)) < 0) {
        pa_source_output_unref(o);
        return r;
    }

    if ((r = pa_source_output_finish_move(o, dest, save)) < 0) {
        pa_source_output_fail_move(o);
        pa_source_output_unref(o);
        return r;
    }

    pa_source_output_unref(o);

    return 0;
}
예제 #2
0
static void source_update_requested_latency_cb(pa_source *s) {
    struct userdata *u;

    pa_source_assert_ref(s);
    u = s->userdata;
    pa_assert(u);

    u->block_usec = pa_source_get_requested_latency_within_thread(s);
}
/* Called from I/O thread context */
static void voip_source_update_requested_latency(pa_source *s) {
    struct userdata *u;

    pa_source_assert_ref(s);
    pa_assert_se(u = s->userdata);

    /* Just hand this one over to the master source */
    pa_source_output_set_requested_latency_within_thread(
            u->hw_source_output,
            voice_source_get_requested_latency(s, u->raw_source));
}
예제 #4
0
static void source_update_requested_latency_cb(pa_source *s) {
    struct userdata *u;

    pa_source_assert_ref(s);
    u = s->userdata;
    pa_assert(u);

    u->block_usec = pa_source_get_requested_latency_within_thread(s);
    if (u->block_usec == (pa_usec_t)-1)
        u->block_usec = u->source->thread_info.max_latency;
}
/* Called from main context */
static int source_set_state_cb(pa_source *s, pa_source_state_t state) {
    struct userdata *u;

    pa_source_assert_ref(s);
    pa_assert_se(u = s->userdata);

    if (!PA_SOURCE_IS_LINKED(state) ||
        !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
        return 0;

    pa_source_output_cork(u->source_output, state == PA_SOURCE_SUSPENDED);
    return 0;
}
static void source_update_requested_latency_cb(pa_source *s) {
    struct userdata *u;

    pa_source_assert_ref(s);
    pa_assert_se(u = s->userdata);

    u->block_usec = pa_source_get_requested_latency_within_thread(s);

    if (u->block_usec == (pa_usec_t) -1)
        u->block_usec = s->thread_info.max_latency;

    pa_log_debug("new block msec = %lu", (unsigned long) (u->block_usec / PA_USEC_PER_MSEC));
}
예제 #7
0
/* Called from main context */
static void source_get_mute_cb(pa_source *s) {
    struct userdata *u;

    pa_source_assert_ref(s);
    pa_assert_se(u = s->userdata);

    if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) ||
        !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
        return;

    /* FIXME, no volume control in source_output, get the info from the master */
    pa_source_get_mute(u->source_output->source, TRUE);
}
/* Called from main context */
static int raw_source_set_state(pa_source *s, pa_source_state_t state) {
    struct userdata *u;
    int ret;
    ENTER();

    pa_source_assert_ref(s);
    pa_assert_se(u = s->userdata);

    ret = voice_source_set_state(s, u->voip_source, state);

    pa_log_debug("(%p): called with %d", (void *)s, state);

    return ret;
}
예제 #9
0
/* Called from I/O thread context */
static void source_update_requested_latency_cb(pa_source *s) {
    struct userdata *u;

    pa_source_assert_ref(s);
    pa_assert_se(u = s->userdata);

    if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state) ||
        !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->thread_info.state))
        return;

    /* Just hand this one over to the master source */
    pa_source_output_set_requested_latency_within_thread(
            u->source_output,
            pa_source_get_requested_latency_within_thread(s));
}
예제 #10
0
/* Generic source state change logic. Used by raw_source and voice_source */
int voice_source_set_state(pa_source *s, pa_source *other, pa_source_state_t state) {
    struct userdata *u;

    pa_source_assert_ref(s);
    pa_assert_se(u = s->userdata);
    if (!other) {
        pa_log_debug("other source not initialized or freed");
        return 0;
    }
    pa_source_assert_ref(other);

    if (u->hw_source_output) {
        if (pa_source_output_get_state(u->hw_source_output) == PA_SOURCE_OUTPUT_RUNNING) {
            if (state == PA_SOURCE_SUSPENDED &&
                pa_source_get_state(other) == PA_SOURCE_SUSPENDED &&
                pa_atomic_load(&u->cmt_connection.ul_state) != CMT_UL_ACTIVE) {
                pa_source_output_cork(u->hw_source_output, TRUE);
                pa_log_debug("hw_source_output corked");
            }
        }
        else if (pa_source_output_get_state(u->hw_source_output) == PA_SOURCE_OUTPUT_CORKED) {
            if (PA_SOURCE_IS_OPENED(state) ||
                PA_SOURCE_IS_OPENED(pa_source_get_state(other)) ||
                pa_atomic_load(&u->cmt_connection.ul_state) == CMT_UL_ACTIVE) {
                pa_source_output_cork(u->hw_source_output, FALSE);
                pa_log_debug("hw_source_output uncorked");
            }
        }
    }
    if (pa_atomic_load(&u->cmt_connection.ul_state) != CMT_UL_ACTIVE && 
        !PA_SOURCE_IS_OPENED(pa_source_get_state(u->voip_source)))
    {
        voice_aep_ear_ref_loop_reset(u);
    }
    return 0;
}
/* Called from main context */
static int voip_source_set_state(pa_source *s, pa_source_state_t state) {
    struct userdata *u;
    int ret = 0;

    pa_source_assert_ref(s);
    pa_assert_se(u = s->userdata);

    ret = voice_source_set_state(s, u->raw_source, state);

    /* TODO: Check if we still need to fiddle with PROP_MIXER_TUNING_MODE */
    if (s->state != PA_SOURCE_RUNNING && state == PA_SOURCE_RUNNING) {
        meego_algorithm_hook_fire(u->hooks[HOOK_CALL_BEGIN], s);
    }

    pa_log_debug("(%p) called with %d", (void *)s, state);
    return ret;
}
예제 #12
0
static void source_get_volume_cb(pa_source *s) {
    struct userdata *u;

    pa_source_assert_ref(s);
    pa_assert_se(u = s->userdata);

    if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) ||
        !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
        return;

    /* FIXME, no volume control in source_output, get the info from the master */
    pa_source_get_volume(u->source_output->source, TRUE);

    if (pa_cvolume_equal(&s->volume,&u->source_output->source->volume))
        /* no change */
        return;

    s->volume = u->source_output->source->volume;
    pa_source_set_soft_volume(s, NULL);
}
예제 #13
0
/* Called from main context */
pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) {
    pa_source_output_assert_ref(o);
    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
    pa_source_assert_ref(dest);

    if (dest == o->source)
        return TRUE;

    if (!pa_source_output_may_move(o))
        return FALSE;

    if (pa_idxset_size(dest->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
        pa_log_warn("Failed to move source output: too many outputs per source.");
        return FALSE;
    }

    if (o->may_move_to)
        if (!o->may_move_to(o, dest))
            return FALSE;

    return TRUE;
}
예제 #14
0
pa_source *pa_droid_source_new(pa_module *m,
                                 pa_modargs *ma,
                                 const char *driver,
                                 pa_droid_card_data *card_data,
                                 pa_droid_mapping *am,
                                 pa_card *card) {

    struct userdata *u = NULL;
    char *thread_name = NULL;
    pa_source_new_data data;
    const char *module_id = NULL;
    /* const char *tmp; */
    uint32_t sample_rate;
    uint32_t alternate_sample_rate;
    audio_devices_t dev_in;
    pa_sample_spec sample_spec;
    pa_channel_map channel_map;
    bool namereg_fail = false;
    pa_droid_config_audio *config = NULL; /* Only used when source is created without card */
    uint32_t source_buffer = 0;
    char audio_source[32];
    int ret;

    audio_format_t hal_audio_format = 0;
    audio_channel_mask_t hal_channel_mask = 0;

    pa_assert(m);
    pa_assert(ma);
    pa_assert(driver);

    /* When running under card use hw module name for source by default. */
    if (card && ma)
        module_id = am->input->module->name;
    else
        module_id = pa_modargs_get_value(ma, "module_id", DEFAULT_MODULE_ID);

    sample_spec = m->core->default_sample_spec;
    channel_map = m->core->default_channel_map;

    if (pa_modargs_get_sample_spec_and_channel_map(ma, &sample_spec, &channel_map, PA_CHANNEL_MAP_AIFF) < 0) {
        pa_log("Failed to parse sample specification and channel map.");
        goto fail;
    }

    alternate_sample_rate = m->core->alternate_sample_rate;
    if (pa_modargs_get_alternate_sample_rate(ma, &alternate_sample_rate) < 0) {
        pa_log("Failed to parse alternate sample rate.");
        goto fail;
    }

    if (pa_modargs_get_value_u32(ma, "source_buffer", &source_buffer) < 0) {
        pa_log("Failed to parse source_buffer. Needs to be integer >= 0.");
        goto fail;
    }

    u = pa_xnew0(struct userdata, 1);
    u->core = m->core;
    u->module = m;
    u->card = card;
    u->rtpoll = pa_rtpoll_new();
    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);

    /* Enabled routing changes by default. */
    u->routing_changes_enabled = true;

    if (card_data) {
        pa_assert(card);
        u->card_data = card_data;
        pa_assert_se((u->hw_module = pa_droid_hw_module_get(u->core, NULL, card_data->module_id)));
    } else {
        /* Stand-alone source */

        if (!(config = pa_droid_config_load(ma)))
            goto fail;

        /* Ownership of config transfers to hw_module if opening of hw module succeeds. */
        if (!(u->hw_module = pa_droid_hw_module_get(u->core, config, module_id)))
            goto fail;
    }

    if (!pa_convert_format(sample_spec.format, CONV_FROM_PA, &hal_audio_format)) {
        pa_log("Sample spec format %u not supported.", sample_spec.format);
        goto fail;
    }

    for (int i = 0; i < channel_map.channels; i++) {
        audio_channel_mask_t c;
        if (!pa_convert_input_channel(channel_map.map[i], CONV_FROM_PA, &c)) {
            pa_log("Failed to convert channel map.");
            goto fail;
        }
        hal_channel_mask |= c;
    }

    struct audio_config config_in = {
        .sample_rate = sample_spec.rate,
        .channel_mask = hal_channel_mask,
        .format = hal_audio_format
    };

    /* Default routing */
    /* FIXME So while setting routing through stream with HALv2 API fails, creation of stream
     * requires HALv2 style device to work properly. So until that oddity is resolved we always
     * set AUDIO_DEVICE_IN_BUILTIN_MIC as initial device here. */
#if 0
    pa_assert_se(pa_string_convert_input_device_str_to_num("AUDIO_DEVICE_IN_BUILTIN_MIC", &dev_in));

    if ((tmp = pa_modargs_get_value(ma, "input_devices", NULL))) {
        audio_devices_t tmp_dev;

        if (parse_device_list(tmp, &tmp_dev) && tmp_dev)
            dev_in = tmp_dev;

        pa_log_debug("Set initial devices %s", tmp);
    }
#else
    pa_log_info("FIXME: Setting AUDIO_DEVICE_IN_BUILTIN_MIC as initial device.");
    dev_in = AUDIO_DEVICE_IN_BUILTIN_MIC;
#endif
    pa_droid_hw_module_lock(u->hw_module);
    ret = u->hw_module->device->open_input_stream(u->hw_module->device,
                                                  u->hw_module->stream_in_id,
                                                  dev_in,
                                                  &config_in,
                                                  &u->stream);
    /* On some devices the first call will fail if the config parameters are
     * not supported, but it'll automatically set the right ones, expecting
     * the caller to call it again, so let's try at least one more time */
    if (!u->stream)
        ret = u->hw_module->device->open_input_stream(u->hw_module->device,
                                                      u->hw_module->stream_in_id,
                                                      dev_in,
                                                      &config_in,
                                                      &u->stream);

    u->hw_module->stream_in_id++;
    pa_droid_hw_module_unlock(u->hw_module);

    if (ret < 0) {
        pa_log("Failed to open input stream.");
        goto fail;
    }

    if ((sample_rate = u->stream->common.get_sample_rate(&u->stream->common)) != sample_spec.rate) {
        pa_log_warn("Requested sample rate %u but got %u instead.", sample_spec.rate, sample_rate);
        sample_spec.rate = sample_rate;
    }

    u->buffer_size = u->stream->common.get_buffer_size(&u->stream->common);
    if (source_buffer) {
        if (source_buffer < u->buffer_size)
            pa_log_warn("Requested buffer size %u less than HAL reported buffer size (%u).", source_buffer, u->buffer_size);
        else if (source_buffer % u->buffer_size) {
            uint32_t trunc = (source_buffer / u->buffer_size) * u->buffer_size;
            pa_log_warn("Requested buffer size %u not multiple of HAL buffer size (%u). Using buffer size %u", source_buffer, u->buffer_size, trunc);
            u->buffer_size = trunc;
        } else {
            pa_log_info("Using requested buffer size %u.", source_buffer);
            u->buffer_size = source_buffer;
        }
    }

    pa_log_info("Created Android stream with device: %u sample rate: %u channel mask: %u format: %u buffer size: %u",
            dev_in,
            sample_rate,
            config_in.channel_mask,
            config_in.format,
            u->buffer_size);

    /* Setting audio source to MIC by default */
    pa_snprintf(audio_source, sizeof(audio_source), "%s=%u", AUDIO_PARAMETER_STREAM_INPUT_SOURCE, AUDIO_SOURCE_MIC);
    u->stream->common.set_parameters(&u->stream->common, audio_source);
    pa_log_debug("Setting audio source to AUDIO_SOURCE_MIC by default");

    pa_source_new_data_init(&data);
    data.driver = driver;
    data.module = m;
    data.card = card;

    source_set_name(ma, &data, module_id);

    /* We need to give pa_modargs_get_value_boolean() a pointer to a local
     * variable instead of using &data.namereg_fail directly, because
     * data.namereg_fail is a bitfield and taking the address of a bitfield
     * variable is impossible. */
    namereg_fail = data.namereg_fail;
    if (pa_modargs_get_value_boolean(ma, "namereg_fail", &namereg_fail) < 0) {
        pa_log("Failed to parse namereg_fail argument.");
        pa_source_new_data_done(&data);
        goto fail;
    }
    data.namereg_fail = namereg_fail;

    pa_source_new_data_set_sample_spec(&data, &sample_spec);
    pa_source_new_data_set_channel_map(&data, &channel_map);
    pa_source_new_data_set_alternate_sample_rate(&data, alternate_sample_rate);

    if (am)
        pa_droid_add_ports(data.ports, am, card);

    u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE);
    pa_source_new_data_done(&data);

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

    u->source->userdata = u;

    u->source->parent.process_msg = source_process_msg;

    source_set_mute_control(u);

    u->source->set_port = source_set_port_cb;

    pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
    pa_source_set_rtpoll(u->source, u->rtpoll);

    /* Disable rewind for droid source */
    pa_source_set_max_rewind(u->source, 0);

    thread_name = pa_sprintf_malloc("droid-source-%s", module_id);
    if (!(u->thread = pa_thread_new(thread_name, thread_func, u))) {
        pa_log("Failed to create thread.");
        goto fail;
    }
    pa_xfree(thread_name);
    thread_name = NULL;

    pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->buffer_size, &sample_spec));
    pa_log_debug("Set fixed latency %" PRIu64 " usec", pa_bytes_to_usec(u->buffer_size, &sample_spec));

    if (u->source->active_port)
        source_set_port_cb(u->source, u->source->active_port);

    pa_source_put(u->source);

    return u->source;

fail:
    pa_xfree(thread_name);

    if (config)
        pa_xfree(config);

    if (u)
        userdata_free(u);

    return NULL;
}

void pa_droid_source_free(pa_source *s) {
    struct userdata *u;

    pa_source_assert_ref(s);
    pa_assert_se(u = s->userdata);

    userdata_free(u);
}

static void userdata_free(struct userdata *u) {

    if (u->source)
        pa_source_unlink(u->source);

    if (u->thread) {
        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
        pa_thread_free(u->thread);
    }

    pa_thread_mq_done(&u->thread_mq);

    if (u->source)
        pa_source_unref(u->source);

    if (u->memchunk.memblock)
        pa_memblock_unref(u->memchunk.memblock);

    if (u->hw_module && u->stream) {
        pa_droid_hw_module_lock(u->hw_module);
        u->hw_module->device->close_input_stream(u->hw_module->device, u->stream);
        pa_droid_hw_module_unlock(u->hw_module);
    }

    // Stand alone source
    if (u->hw_module)
        pa_droid_hw_module_unref(u->hw_module);

    pa_xfree(u);
}
예제 #15
0
/* Called from main context */
int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t save) {
    pa_resampler *new_resampler;

    pa_source_output_assert_ref(o);
    pa_assert_ctl_context();
    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
    pa_assert(!o->source);
    pa_source_assert_ref(dest);

    if (!pa_source_output_may_move_to(o, dest))
        return -1;

    if (o->thread_info.resampler &&
        pa_sample_spec_equal(pa_resampler_input_sample_spec(o->thread_info.resampler), &dest->sample_spec) &&
        pa_channel_map_equal(pa_resampler_input_channel_map(o->thread_info.resampler), &dest->channel_map))

        /* Try to reuse the old resampler if possible */
        new_resampler = o->thread_info.resampler;

    else if ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ||
             !pa_sample_spec_equal(&o->sample_spec, &dest->sample_spec) ||
             !pa_channel_map_equal(&o->channel_map, &dest->channel_map)) {

        /* Okey, we need a new resampler for the new source */

        if (!(new_resampler = pa_resampler_new(
                      o->core->mempool,
                      &dest->sample_spec, &dest->channel_map,
                      &o->sample_spec, &o->channel_map,
                      o->requested_resample_method,
                      ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
                      ((o->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
                      (o->core->disable_remixing || (o->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
            pa_log_warn("Unsupported resampling operation.");
            return -PA_ERR_NOTSUPPORTED;
        }
    } else
        new_resampler = NULL;

    if (o->moving)
        o->moving(o, dest);

    o->source = dest;
    o->save_source = save;
    pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL);

    if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED)
        o->source->n_corked++;

    /* Replace resampler */
    if (new_resampler != o->thread_info.resampler) {
        if (o->thread_info.resampler)
            pa_resampler_free(o->thread_info.resampler);
        o->thread_info.resampler = new_resampler;

        pa_memblockq_free(o->thread_info.delay_memblockq);

        o->thread_info.delay_memblockq = pa_memblockq_new(
                0,
                MEMBLOCKQ_MAXLENGTH,
                0,
                pa_frame_size(&o->source->sample_spec),
                0,
                1,
                0,
                &o->source->silence);
        o->actual_resample_method = new_resampler ? pa_resampler_get_method(new_resampler) : PA_RESAMPLER_INVALID;
    }

    pa_source_update_status(dest);

    pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0);

    pa_log_debug("Successfully moved source output %i to %s.", o->index, dest->name);

    /* Notify everyone */
    pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], o);
    pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);

    return 0;
}