Merge pull request #64 from jusa/jb38419
Update droid source's input stream handling.
This commit is contained in:
commit
d53995823d
7 changed files with 482 additions and 379 deletions
|
|
@ -108,10 +108,13 @@ uint32_t conversion_table_default_audio_source[][2] = {
|
||||||
#if defined(HAVE_ENUM_AUDIO_DEVICE_IN_FM_RX) && defined(HAVE_ENUM_AUDIO_SOURCE_FM_RX)
|
#if defined(HAVE_ENUM_AUDIO_DEVICE_IN_FM_RX) && defined(HAVE_ENUM_AUDIO_SOURCE_FM_RX)
|
||||||
{ AUDIO_DEVICE_IN_FM_RX, AUDIO_SOURCE_FM_RX },
|
{ AUDIO_DEVICE_IN_FM_RX, AUDIO_SOURCE_FM_RX },
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(HAVE_ENUM_AUDIO_DEVICE_IN_FM_TUNER) && defined(HAVE_ENUM_AUDIO_SOURCE_FM_TUNER)
|
||||||
|
{ AUDIO_DEVICE_IN_FM_TUNER, AUDIO_SOURCE_FM_TUNER },
|
||||||
|
#endif
|
||||||
#if defined(HAVE_ENUM_AUDIO_DEVICE_IN_FM_RX_A2DP) && defined(HAVE_ENUM_AUDIO_SOURCE_FM_RX_A2DP)
|
#if defined(HAVE_ENUM_AUDIO_DEVICE_IN_FM_RX_A2DP) && defined(HAVE_ENUM_AUDIO_SOURCE_FM_RX_A2DP)
|
||||||
{ AUDIO_DEVICE_IN_FM_RX_A2DP, AUDIO_SOURCE_FM_RX_A2DP },
|
{ AUDIO_DEVICE_IN_FM_RX_A2DP, AUDIO_SOURCE_FM_RX_A2DP },
|
||||||
#endif
|
#endif
|
||||||
{ AUDIO_DEVICE_IN_ALL, AUDIO_SOURCE_DEFAULT }
|
{ AUDIO_DEVICE_IN_ALL, AUDIO_SOURCE_DEFAULT }
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Output devices */
|
/* Output devices */
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,8 @@ struct droid_quirk {
|
||||||
|
|
||||||
struct droid_quirk valid_quirks[] = {
|
struct droid_quirk valid_quirks[] = {
|
||||||
{ "input_atoi", QUIRK_INPUT_ATOI },
|
{ "input_atoi", QUIRK_INPUT_ATOI },
|
||||||
{ "set_parameters", QUIRK_SET_PARAMETERS }
|
{ "set_parameters", QUIRK_SET_PARAMETERS },
|
||||||
|
{ "close_input", QUIRK_CLOSE_INPUT }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pa_droid_quirks {
|
struct pa_droid_quirks {
|
||||||
|
|
@ -139,6 +140,9 @@ static const char * const droid_combined_auto_inputs[2] = { "primary", NULL
|
||||||
static void droid_config_free(pa_droid_config_audio *config);
|
static void droid_config_free(pa_droid_config_audio *config);
|
||||||
static void droid_port_free(pa_droid_port *p);
|
static void droid_port_free(pa_droid_port *p);
|
||||||
|
|
||||||
|
static pa_droid_stream *get_primary_output(pa_droid_hw_module *hw);
|
||||||
|
static int input_stream_set_route(pa_droid_stream *s, audio_devices_t device);
|
||||||
|
|
||||||
static bool string_convert_num_to_str(const struct string_conversion *list, const uint32_t value, const char **to_str) {
|
static bool string_convert_num_to_str(const struct string_conversion *list, const uint32_t value, const char **to_str) {
|
||||||
pa_assert(list);
|
pa_assert(list);
|
||||||
pa_assert(to_str);
|
pa_assert(to_str);
|
||||||
|
|
@ -1561,14 +1565,21 @@ void pa_droid_add_card_ports(pa_card_profile *cp, pa_hashmap *ports, pa_droid_ma
|
||||||
add_ports(core, cp, ports, am, NULL);
|
add_ports(core, cp, ports, am, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void log_active_quirks(pa_droid_quirks *quirks) {
|
void pa_droid_quirk_log(pa_droid_hw_module *hw) {
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
|
||||||
if (quirks) {
|
pa_assert(hw);
|
||||||
pa_log_debug("Enabled quirks:");
|
|
||||||
for (i = 0; i < sizeof(valid_quirks) / sizeof(struct droid_quirk); i++)
|
if (hw->quirks) {
|
||||||
if (quirks->enabled[i])
|
for (i = 0; i < sizeof(valid_quirks) / sizeof(struct droid_quirk); i++) {
|
||||||
pa_log_debug(" %s", valid_quirks[i].name);
|
if (hw->quirks->enabled[i]) {
|
||||||
|
pa_log_debug("Enabled quirks:");
|
||||||
|
for (i = 0; i < sizeof(valid_quirks) / sizeof(struct droid_quirk); i++)
|
||||||
|
if (hw->quirks->enabled[i])
|
||||||
|
pa_log_debug(" %s", valid_quirks[i].name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1597,12 +1608,13 @@ static pa_droid_quirks *set_default_quirks(pa_droid_quirks *q) {
|
||||||
q->enabled[QUIRK_SET_PARAMETERS] = true;
|
q->enabled[QUIRK_SET_PARAMETERS] = true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
log_active_quirks(q);
|
q = get_quirks(q);
|
||||||
|
q->enabled[QUIRK_CLOSE_INPUT] = true;
|
||||||
|
|
||||||
return q;
|
return q;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pa_droid_parse_quirks(pa_droid_hw_module *hw, const char *quirks) {
|
bool pa_droid_quirk_parse(pa_droid_hw_module *hw, const char *quirks) {
|
||||||
char *quirk = NULL;
|
char *quirk = NULL;
|
||||||
char *d;
|
char *d;
|
||||||
const char *state = NULL;
|
const char *state = NULL;
|
||||||
|
|
@ -1636,8 +1648,6 @@ bool pa_droid_parse_quirks(pa_droid_hw_module *hw, const char *quirks) {
|
||||||
pa_xfree(quirk);
|
pa_xfree(quirk);
|
||||||
}
|
}
|
||||||
|
|
||||||
log_active_quirks(hw->quirks);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
|
@ -1666,11 +1676,14 @@ static pa_droid_hw_module *droid_hw_module_open(pa_core *core, pa_droid_config_a
|
||||||
pa_droid_hw_module *hw = NULL;
|
pa_droid_hw_module *hw = NULL;
|
||||||
struct hw_module_t *hwmod = NULL;
|
struct hw_module_t *hwmod = NULL;
|
||||||
audio_hw_device_t *device = NULL;
|
audio_hw_device_t *device = NULL;
|
||||||
|
int h;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
pa_assert(core);
|
pa_assert(core);
|
||||||
pa_assert(module_id);
|
pa_assert(module_id);
|
||||||
|
|
||||||
|
pa_log_info("Droid hw module %s", VERSION);
|
||||||
|
|
||||||
if (!config) {
|
if (!config) {
|
||||||
pa_log("No configuration provided for opening module with id %s", module_id);
|
pa_log("No configuration provided for opening module with id %s", module_id);
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
@ -1722,6 +1735,9 @@ static pa_droid_hw_module *droid_hw_module_open(pa_core *core, pa_droid_config_a
|
||||||
hw->inputs = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
|
hw->inputs = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
|
||||||
hw->quirks = set_default_quirks(hw->quirks);
|
hw->quirks = set_default_quirks(hw->quirks);
|
||||||
|
|
||||||
|
for (h = 0; h < PA_DROID_HOOK_MAX; h++)
|
||||||
|
pa_hook_init(&hw->hooks[h], hw);
|
||||||
|
|
||||||
pa_assert_se(pa_shared_set(core, hw->shared_name, hw) >= 0);
|
pa_assert_se(pa_shared_set(core, hw->shared_name, hw) >= 0);
|
||||||
|
|
||||||
return hw;
|
return hw;
|
||||||
|
|
@ -1762,10 +1778,15 @@ pa_droid_hw_module *pa_droid_hw_module_ref(pa_droid_hw_module *hw) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void droid_hw_module_close(pa_droid_hw_module *hw) {
|
static void droid_hw_module_close(pa_droid_hw_module *hw) {
|
||||||
|
int h;
|
||||||
|
|
||||||
pa_assert(hw);
|
pa_assert(hw);
|
||||||
|
|
||||||
pa_log_info("Closing hw module %s.%s (%s)", AUDIO_HARDWARE_MODULE_ID, hw->enabled_module->name, DROID_DEVICE_STRING);
|
pa_log_info("Closing hw module %s.%s (%s)", AUDIO_HARDWARE_MODULE_ID, hw->enabled_module->name, DROID_DEVICE_STRING);
|
||||||
|
|
||||||
|
for (h = 0; h < PA_DROID_HOOK_MAX; h++)
|
||||||
|
pa_hook_done(&hw->hooks[h]);
|
||||||
|
|
||||||
if (hw->config)
|
if (hw->config)
|
||||||
droid_config_free(hw->config);
|
droid_config_free(hw->config);
|
||||||
|
|
||||||
|
|
@ -1906,48 +1927,104 @@ static pa_droid_stream *droid_stream_new(pa_droid_hw_module *module) {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool stream_config_fill(audio_devices_t devices,
|
||||||
|
pa_sample_spec *sample_spec,
|
||||||
|
pa_channel_map *channel_map,
|
||||||
|
struct audio_config *config) {
|
||||||
|
audio_format_t hal_audio_format = 0;
|
||||||
|
audio_channel_mask_t hal_channel_mask = 0;
|
||||||
|
bool voicecall_record = false;
|
||||||
|
|
||||||
|
pa_assert(sample_spec);
|
||||||
|
pa_assert(channel_map);
|
||||||
|
pa_assert(config);
|
||||||
|
|
||||||
|
#if AUDIO_API_VERSION_MAJ >= 2
|
||||||
|
devices &= ~AUDIO_DEVICE_BIT_IN;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (devices & AUDIO_DEVICE_IN_VOICE_CALL)
|
||||||
|
voicecall_record = true;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (devices & AUDIO_DEVICE_IN_ALL) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < channel_map->channels; i++) {
|
||||||
|
audio_channel_mask_t c;
|
||||||
|
if (!pa_convert_output_channel(channel_map->map[i], CONV_FROM_PA, &c)) {
|
||||||
|
pa_log("Failed to convert channel map.");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
hal_channel_mask |= c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (voicecall_record) {
|
||||||
|
pa_channel_map_init_mono(channel_map);
|
||||||
|
sample_spec->channels = 1;
|
||||||
|
/* Only allow recording both downlink and uplink. */
|
||||||
|
#if defined(QCOM_HARDWARE)
|
||||||
|
#if (ANDROID_VERSION_MAJOR <= 4) && defined(HAVE_ENUM_AUDIO_CHANNEL_IN_VOICE_CALL_MONO)
|
||||||
|
hal_channel_mask = AUDIO_CHANNEL_IN_VOICE_CALL_MONO;
|
||||||
|
#else
|
||||||
|
hal_channel_mask = AUDIO_CHANNEL_IN_MONO;
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
hal_channel_mask = AUDIO_CHANNEL_IN_VOICE_UPLINK | AUDIO_CHANNEL_IN_VOICE_DNLINK;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(config, 0, sizeof(*config));
|
||||||
|
config->sample_rate = sample_spec->rate;
|
||||||
|
config->channel_mask = hal_channel_mask;
|
||||||
|
config->format = hal_audio_format;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
pa_droid_stream *pa_droid_open_output_stream(pa_droid_hw_module *module,
|
pa_droid_stream *pa_droid_open_output_stream(pa_droid_hw_module *module,
|
||||||
const pa_sample_spec *spec,
|
const pa_sample_spec *spec,
|
||||||
const pa_channel_map *map,
|
const pa_channel_map *map,
|
||||||
audio_output_flags_t flags,
|
audio_output_flags_t flags,
|
||||||
audio_devices_t devices) {
|
audio_devices_t devices) {
|
||||||
pa_droid_stream *s = NULL;
|
pa_droid_stream *s = NULL;
|
||||||
|
pa_droid_stream *primary_stream = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
struct audio_stream_out *stream;
|
struct audio_stream_out *stream;
|
||||||
audio_format_t hal_audio_format = 0;
|
pa_channel_map channel_map;
|
||||||
audio_channel_mask_t hal_channel_mask = 0;
|
pa_sample_spec sample_spec;
|
||||||
struct audio_config config_out;
|
struct audio_config config_out;
|
||||||
size_t buffer_size;
|
|
||||||
|
|
||||||
pa_assert(module);
|
pa_assert(module);
|
||||||
pa_assert(spec);
|
pa_assert(spec);
|
||||||
pa_assert(map);
|
pa_assert(map);
|
||||||
|
|
||||||
if (!pa_convert_format(spec->format, CONV_FROM_PA, &hal_audio_format)) {
|
sample_spec = *spec;
|
||||||
pa_log("Sample spec format %u not supported.", spec->format);
|
channel_map = *map;
|
||||||
|
|
||||||
|
if (!stream_config_fill(devices, &sample_spec, &channel_map, &config_out))
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < map->channels; i++) {
|
if (pa_idxset_size(module->outputs) == 0)
|
||||||
audio_channel_mask_t c;
|
|
||||||
if (!pa_convert_output_channel(map->map[i], CONV_FROM_PA, &c)) {
|
|
||||||
pa_log("Failed to convert channel map.");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
hal_channel_mask |= c;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&config_out, 0, sizeof(struct audio_config));
|
|
||||||
config_out.sample_rate = spec->rate;
|
|
||||||
config_out.channel_mask = hal_channel_mask;
|
|
||||||
config_out.format = hal_audio_format;
|
|
||||||
|
|
||||||
if (pa_idxset_size(module->outputs) == 0) {
|
|
||||||
pa_log_debug("Set initial output device to %#010x", devices);
|
pa_log_debug("Set initial output device to %#010x", devices);
|
||||||
module->output_device = devices;
|
else if ((primary_stream = get_primary_output(module))) {
|
||||||
} else {
|
pa_log_debug("Primary output with device %#010x already open, using as initial device.", primary_stream->device);
|
||||||
pa_log_debug("Output with device %#010x already open, using as initial device.", module->output_device);
|
devices = primary_stream->device;
|
||||||
devices = module->output_device;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_droid_hw_module_lock(module);
|
pa_droid_hw_module_lock(module);
|
||||||
|
|
@ -1974,24 +2051,27 @@ pa_droid_stream *pa_droid_open_output_stream(pa_droid_hw_module *module,
|
||||||
s->out = stream;
|
s->out = stream;
|
||||||
s->sample_spec = *spec;
|
s->sample_spec = *spec;
|
||||||
s->channel_map = *map;
|
s->channel_map = *map;
|
||||||
|
s->input_sample_spec = sample_spec;
|
||||||
|
s->input_channel_map = channel_map;
|
||||||
s->flags = flags;
|
s->flags = flags;
|
||||||
|
s->device = devices;
|
||||||
|
|
||||||
if ((s->sample_spec.rate = s->out->common.get_sample_rate(&s->out->common)) != spec->rate)
|
if ((s->sample_spec.rate = s->out->common.get_sample_rate(&s->out->common)) != spec->rate)
|
||||||
pa_log_warn("Requested sample rate %u but got %u instead.", spec->rate, s->sample_spec.rate);
|
pa_log_warn("Requested sample rate %u but got %u instead.", spec->rate, s->sample_spec.rate);
|
||||||
|
|
||||||
pa_idxset_put(module->outputs, s, NULL);
|
pa_idxset_put(module->outputs, s, NULL);
|
||||||
|
|
||||||
buffer_size = s->out->common.get_buffer_size(&s->out->common);
|
s->buffer_size = s->out->common.get_buffer_size(&s->out->common);
|
||||||
|
|
||||||
pa_log_info("Opened droid output stream %p with device: %u flags: %u sample rate: %u channels: %u (%u) format: %u (%u) buffer size: %u (%llu usec)",
|
pa_log_info("Opened droid output stream %p with device: %u flags: %u sample rate: %u channels: %u (%u) format: %u (%u) buffer size: %u (%llu usec)",
|
||||||
(void *) s,
|
(void *) s,
|
||||||
devices,
|
devices,
|
||||||
s->flags,
|
s->flags,
|
||||||
s->sample_spec.rate,
|
s->sample_spec.rate,
|
||||||
s->sample_spec.channels, hal_channel_mask,
|
s->sample_spec.channels, config_out.channel_mask,
|
||||||
s->sample_spec.format, hal_audio_format,
|
s->sample_spec.format, config_out.format,
|
||||||
buffer_size,
|
s->buffer_size,
|
||||||
pa_bytes_to_usec(buffer_size, &s->sample_spec));
|
pa_bytes_to_usec(s->buffer_size, &s->sample_spec));
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
|
|
||||||
|
|
@ -2001,119 +2081,140 @@ fail:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_droid_stream *pa_droid_open_input_stream(pa_droid_hw_module *module,
|
static int input_stream_open(pa_droid_stream *s) {
|
||||||
const pa_sample_spec *spec,
|
|
||||||
const pa_channel_map *map,
|
|
||||||
audio_devices_t devices) {
|
|
||||||
|
|
||||||
pa_droid_stream *s = NULL;
|
|
||||||
int ret;
|
|
||||||
audio_stream_in_t *stream;
|
audio_stream_in_t *stream;
|
||||||
audio_format_t hal_audio_format = 0;
|
audio_source_t audio_source = AUDIO_SOURCE_DEFAULT;
|
||||||
audio_channel_mask_t hal_channel_mask = 0;
|
|
||||||
pa_channel_map channel_map;
|
pa_channel_map channel_map;
|
||||||
pa_sample_spec sample_spec;
|
pa_sample_spec sample_spec;
|
||||||
bool voicecall_record = false;
|
|
||||||
struct audio_config config_in;
|
struct audio_config config_in;
|
||||||
size_t buffer_size;
|
size_t buffer_size;
|
||||||
|
bool buffer_size_changed = false;
|
||||||
|
bool channel_map_changed = false;
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
#if AUDIO_API_VERSION_MAJ >= 2
|
pa_assert(s);
|
||||||
if ((devices & ~AUDIO_DEVICE_BIT_IN) & AUDIO_DEVICE_IN_VOICE_CALL)
|
pa_assert(!s->in);
|
||||||
#else
|
|
||||||
if (devices & AUDIO_DEVICE_IN_VOICE_CALL)
|
|
||||||
#endif
|
|
||||||
voicecall_record = true;
|
|
||||||
|
|
||||||
channel_map = *map;
|
channel_map = s->channel_map;
|
||||||
sample_spec = *spec;
|
sample_spec = s->sample_spec;
|
||||||
|
|
||||||
if (!pa_convert_format(spec->format, CONV_FROM_PA, &hal_audio_format)) {
|
if (!stream_config_fill(s->device, &sample_spec, &channel_map, &config_in))
|
||||||
pa_log("Sample spec format %u not supported.", spec->format);
|
goto done;
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < map->channels; i++) {
|
pa_input_device_default_audio_source(s->device, &audio_source);
|
||||||
audio_channel_mask_t c;
|
|
||||||
if (!pa_convert_input_channel(map->map[i], CONV_FROM_PA, &c)) {
|
|
||||||
pa_log("Failed to convert channel map.");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
hal_channel_mask |= c;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (voicecall_record) {
|
if (channel_map.channels != s->input_channel_map.channels)
|
||||||
pa_channel_map_init_mono(&channel_map);
|
channel_map_changed = true;
|
||||||
sample_spec.channels = 1;
|
|
||||||
/* Only allow recording both downlink and uplink. */
|
|
||||||
#if defined(QCOM_HARDWARE)
|
|
||||||
#if (ANDROID_VERSION_MAJOR <= 4) && defined(HAVE_ENUM_AUDIO_CHANNEL_IN_VOICE_CALL_MONO)
|
|
||||||
hal_channel_mask = AUDIO_CHANNEL_IN_VOICE_CALL_MONO;
|
|
||||||
#else
|
|
||||||
hal_channel_mask = AUDIO_CHANNEL_IN_MONO;
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
hal_channel_mask = AUDIO_CHANNEL_IN_VOICE_UPLINK | AUDIO_CHANNEL_IN_VOICE_DNLINK;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&config_in, 0, sizeof(struct audio_config));
|
pa_droid_hw_module_lock(s->module);
|
||||||
config_in.sample_rate = sample_spec.rate;
|
ret = s->module->device->open_input_stream(s->module->device,
|
||||||
config_in.channel_mask = hal_channel_mask;
|
s->module->stream_in_id++,
|
||||||
config_in.format = hal_audio_format;
|
s->device,
|
||||||
|
&config_in,
|
||||||
pa_droid_hw_module_lock(module);
|
&stream
|
||||||
ret = module->device->open_input_stream(module->device,
|
|
||||||
module->stream_in_id++,
|
|
||||||
devices,
|
|
||||||
&config_in,
|
|
||||||
&stream
|
|
||||||
#if AUDIO_API_VERSION_MAJ >= 3
|
#if AUDIO_API_VERSION_MAJ >= 3
|
||||||
, AUDIO_INPUT_FLAG_NONE /* Default to no input flags */
|
, s->flags
|
||||||
, NULL /* Don't define address */
|
, NULL /* Don't define address */
|
||||||
, AUDIO_SOURCE_DEFAULT /* Default audio source */
|
, audio_source
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
pa_droid_hw_module_unlock(module);
|
pa_droid_hw_module_unlock(s->module);
|
||||||
|
|
||||||
if (ret < 0 || !stream) {
|
if (ret < 0 || !stream) {
|
||||||
pa_log("Failed to open input stream: %d with device: %u flags: %u sample rate: %u channels: %u (%u) format: %u (%u)",
|
pa_log("Failed to open input stream: %d with device: %u flags: %u sample rate: %u channels: %u (%u) format: %u (%u)",
|
||||||
ret,
|
ret,
|
||||||
devices,
|
s->device,
|
||||||
0, /* AUDIO_INPUT_FLAG_NONE on v3. v1 and v2 don't have input flags. */
|
0, /* AUDIO_INPUT_FLAG_NONE on v3. v1 and v2 don't have input flags. */
|
||||||
config_in.sample_rate,
|
config_in.sample_rate,
|
||||||
sample_spec.channels,
|
sample_spec.channels,
|
||||||
config_in.channel_mask,
|
config_in.channel_mask,
|
||||||
sample_spec.format,
|
sample_spec.format,
|
||||||
config_in.format);
|
config_in.format);
|
||||||
goto fail;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
s = droid_stream_new(module);
|
|
||||||
s->in = stream;
|
s->in = stream;
|
||||||
s->sample_spec = sample_spec;
|
s->input_sample_spec = sample_spec;
|
||||||
s->channel_map = channel_map;
|
s->input_channel_map = channel_map;
|
||||||
s->flags = 0;
|
buffer_size = s->in->common.get_buffer_size(&s->in->common);
|
||||||
|
if (s->buffer_size != 0 && s->buffer_size != buffer_size)
|
||||||
|
buffer_size_changed = true;
|
||||||
|
s->buffer_size = buffer_size;
|
||||||
|
|
||||||
|
/* we need to call standby before reading with some devices. */
|
||||||
|
s->in->common.standby(&s->in->common);
|
||||||
|
|
||||||
|
pa_log_debug("Opened input stream %p", (void *) s);
|
||||||
|
|
||||||
|
input_stream_set_route(s, s->device);
|
||||||
|
|
||||||
|
if (buffer_size_changed) {
|
||||||
|
pa_log_debug("Input stream %p buffer size changed to %u.", (void *) s, s->buffer_size);
|
||||||
|
pa_hook_fire(&s->module->hooks[PA_DROID_HOOK_INPUT_BUFFER_SIZE_CHANGED], (void *) s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (channel_map_changed) {
|
||||||
|
pa_log_debug("Input stream %p channel count changed to %d.", (void *) s, s->input_channel_map.channels);
|
||||||
|
pa_hook_fire(&s->module->hooks[PA_DROID_HOOK_INPUT_CHANNEL_MAP_CHANGED], (void *) s);
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void input_stream_close(pa_droid_stream *s) {
|
||||||
|
pa_assert(s);
|
||||||
|
pa_assert(s->in);
|
||||||
|
|
||||||
|
pa_mutex_lock(s->module->input_mutex);
|
||||||
|
s->module->device->close_input_stream(s->module->device, s->in);
|
||||||
|
s->in = NULL;
|
||||||
|
pa_log_debug("Closed input stream %p", (void *) s);
|
||||||
|
pa_mutex_unlock(s->module->input_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
pa_droid_stream *pa_droid_open_input_stream(pa_droid_hw_module *module,
|
||||||
|
const pa_sample_spec *spec,
|
||||||
|
const pa_channel_map *map,
|
||||||
|
audio_devices_t devices) {
|
||||||
|
|
||||||
|
pa_droid_stream *s = NULL;
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
s = droid_stream_new(module);
|
||||||
|
s->sample_spec = *spec;
|
||||||
|
s->channel_map = *map;
|
||||||
|
s->flags = 0; /* AUDIO_INPUT_FLAG_NONE */
|
||||||
|
s->device = devices;
|
||||||
|
|
||||||
|
/* We need to open the stream for a while so that we can know
|
||||||
|
* what sample rate we get. We need the rate for droid source. */
|
||||||
|
|
||||||
|
if ((ret = input_stream_open(s)) < 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
if ((s->sample_spec.rate = s->in->common.get_sample_rate(&s->in->common)) != spec->rate)
|
if ((s->sample_spec.rate = s->in->common.get_sample_rate(&s->in->common)) != spec->rate)
|
||||||
pa_log_warn("Requested sample rate %u but got %u instead.", spec->rate, s->sample_spec.rate);
|
pa_log_warn("Requested sample rate %u but got %u instead.", spec->rate, s->sample_spec.rate);
|
||||||
|
|
||||||
pa_idxset_put(module->inputs, s, NULL);
|
pa_idxset_put(module->inputs, s, NULL);
|
||||||
|
|
||||||
buffer_size = s->in->common.get_buffer_size(&s->in->common);
|
pa_log_info("Opened droid input stream %p with device: %u flags: %u sample rate: %u channels: %u format: %u buffer size: %u (%llu usec)",
|
||||||
|
|
||||||
/* As audio_source_t may not have any effect when opening the input stream
|
|
||||||
* set input parameters immediately after opening the stream. */
|
|
||||||
pa_droid_stream_set_input_route(s, devices, NULL);
|
|
||||||
|
|
||||||
pa_log_info("Opened droid input stream %p with device: %u flags: %u sample rate: %u channels: %u (%u) format: %u (%u) buffer size: %u (%llu usec)",
|
|
||||||
(void *) s,
|
(void *) s,
|
||||||
devices,
|
devices,
|
||||||
s->flags,
|
s->flags,
|
||||||
s->sample_spec.rate,
|
s->sample_spec.rate,
|
||||||
s->sample_spec.channels, hal_channel_mask,
|
s->sample_spec.channels,
|
||||||
s->sample_spec.format, hal_audio_format,
|
s->sample_spec.format,
|
||||||
buffer_size,
|
s->buffer_size,
|
||||||
pa_bytes_to_usec(buffer_size, &s->sample_spec));
|
pa_bytes_to_usec(s->buffer_size, &s->sample_spec));
|
||||||
|
|
||||||
|
/* As audio_source_t may not have any effect when opening the input stream
|
||||||
|
* set input parameters immediately after opening the stream. */
|
||||||
|
if (!pa_droid_quirk(module, QUIRK_CLOSE_INPUT))
|
||||||
|
input_stream_set_route(s, devices);
|
||||||
|
|
||||||
|
/* We start the stream in suspended state. */
|
||||||
|
pa_droid_stream_suspend(s, true);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
|
|
||||||
|
|
@ -2125,7 +2226,6 @@ fail:
|
||||||
|
|
||||||
pa_droid_stream *pa_droid_stream_ref(pa_droid_stream *s) {
|
pa_droid_stream *pa_droid_stream_ref(pa_droid_stream *s) {
|
||||||
pa_assert(s);
|
pa_assert(s);
|
||||||
pa_assert(s->out || s->in);
|
|
||||||
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
||||||
|
|
||||||
PA_REFCNT_INC(s);
|
PA_REFCNT_INC(s);
|
||||||
|
|
@ -2134,7 +2234,6 @@ pa_droid_stream *pa_droid_stream_ref(pa_droid_stream *s) {
|
||||||
|
|
||||||
void pa_droid_stream_unref(pa_droid_stream *s) {
|
void pa_droid_stream_unref(pa_droid_stream *s) {
|
||||||
pa_assert(s);
|
pa_assert(s);
|
||||||
pa_assert(s->out || s->in);
|
|
||||||
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
||||||
|
|
||||||
if (PA_REFCNT_DEC(s) > 0)
|
if (PA_REFCNT_DEC(s) > 0)
|
||||||
|
|
@ -2148,7 +2247,8 @@ void pa_droid_stream_unref(pa_droid_stream *s) {
|
||||||
} else {
|
} else {
|
||||||
pa_mutex_lock(s->module->input_mutex);
|
pa_mutex_lock(s->module->input_mutex);
|
||||||
pa_idxset_remove_by_data(s->module->inputs, s, NULL);
|
pa_idxset_remove_by_data(s->module->inputs, s, NULL);
|
||||||
s->module->device->close_input_stream(s->module->device, s->in);
|
if (s->in)
|
||||||
|
s->module->device->close_input_stream(s->module->device, s->in);
|
||||||
pa_mutex_unlock(s->module->input_mutex);
|
pa_mutex_unlock(s->module->input_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2172,10 +2272,10 @@ static pa_droid_stream *get_primary_output(pa_droid_hw_module *hw) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pa_droid_stream_set_output_route(pa_droid_stream *s, audio_devices_t device) {
|
static int droid_output_stream_set_route(pa_droid_stream *s, audio_devices_t device) {
|
||||||
pa_droid_stream *slave;
|
pa_droid_stream *slave;
|
||||||
uint32_t idx;
|
uint32_t idx;
|
||||||
char *parameters;
|
char *parameters = NULL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
pa_assert(s);
|
pa_assert(s);
|
||||||
|
|
@ -2185,9 +2285,9 @@ int pa_droid_stream_set_output_route(pa_droid_stream *s, audio_devices_t device)
|
||||||
|
|
||||||
pa_mutex_lock(s->module->output_mutex);
|
pa_mutex_lock(s->module->output_mutex);
|
||||||
|
|
||||||
parameters = pa_sprintf_malloc("%s=%u;", AUDIO_PARAMETER_STREAM_ROUTING, device);
|
|
||||||
|
|
||||||
if (s->flags & AUDIO_OUTPUT_FLAG_PRIMARY || get_primary_output(s->module) == NULL) {
|
if (s->flags & AUDIO_OUTPUT_FLAG_PRIMARY || get_primary_output(s->module) == NULL) {
|
||||||
|
parameters = pa_sprintf_malloc("%s=%u;", AUDIO_PARAMETER_STREAM_ROUTING, device);
|
||||||
|
|
||||||
pa_log_debug("output stream %p set_parameters(%s) %#010x", (void *) s, parameters, device);
|
pa_log_debug("output stream %p set_parameters(%s) %#010x", (void *) s, parameters, device);
|
||||||
ret = s->out->common.set_parameters(&s->out->common, parameters);
|
ret = s->out->common.set_parameters(&s->out->common, parameters);
|
||||||
|
|
||||||
|
|
@ -2198,11 +2298,12 @@ int pa_droid_stream_set_output_route(pa_droid_stream *s, audio_devices_t device)
|
||||||
pa_log_warn("output set_parameters(%s) failed", parameters);
|
pa_log_warn("output set_parameters(%s) failed", parameters);
|
||||||
} else {
|
} else {
|
||||||
/* Store last set output device. */
|
/* Store last set output device. */
|
||||||
s->module->output_device = device;
|
s->device = device;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->flags & AUDIO_OUTPUT_FLAG_PRIMARY && pa_idxset_size(s->module->outputs) > 1) {
|
if (s->flags & AUDIO_OUTPUT_FLAG_PRIMARY && pa_idxset_size(s->module->outputs) > 1) {
|
||||||
|
pa_assert(parameters);
|
||||||
|
|
||||||
PA_IDXSET_FOREACH(slave, s->module->outputs, idx) {
|
PA_IDXSET_FOREACH(slave, s->module->outputs, idx) {
|
||||||
if (slave == s)
|
if (slave == s)
|
||||||
|
|
@ -2216,7 +2317,8 @@ int pa_droid_stream_set_output_route(pa_droid_stream *s, audio_devices_t device)
|
||||||
pa_log_warn("output set_parameters(%s) not allowed while stream is active", parameters);
|
pa_log_warn("output set_parameters(%s) not allowed while stream is active", parameters);
|
||||||
else
|
else
|
||||||
pa_log_warn("output set_parameters(%s) failed", parameters);
|
pa_log_warn("output set_parameters(%s) failed", parameters);
|
||||||
}
|
} else
|
||||||
|
slave->device = s->device;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2227,10 +2329,10 @@ int pa_droid_stream_set_output_route(pa_droid_stream *s, audio_devices_t device)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pa_droid_stream_set_input_route(pa_droid_stream *s, audio_devices_t device, audio_source_t *new_source) {
|
static int input_stream_set_route(pa_droid_stream *s, audio_devices_t device) {
|
||||||
audio_source_t source = (uint32_t) -1;
|
audio_source_t source = (uint32_t) -1;
|
||||||
char *parameters;
|
char *parameters;
|
||||||
int ret;
|
int ret = 0;
|
||||||
|
|
||||||
pa_assert(s);
|
pa_assert(s);
|
||||||
pa_assert(s->in);
|
pa_assert(s->in);
|
||||||
|
|
@ -2268,16 +2370,38 @@ int pa_droid_stream_set_input_route(pa_droid_stream *s, audio_devices_t device,
|
||||||
pa_log_warn("input set_parameters(%s) not allowed while stream is active", parameters);
|
pa_log_warn("input set_parameters(%s) not allowed while stream is active", parameters);
|
||||||
else
|
else
|
||||||
pa_log_warn("input set_parameters(%s) failed", parameters);
|
pa_log_warn("input set_parameters(%s) failed", parameters);
|
||||||
}
|
} else
|
||||||
|
s->device = device;
|
||||||
if (new_source)
|
|
||||||
*new_source = source;
|
|
||||||
|
|
||||||
pa_xfree(parameters);
|
pa_xfree(parameters);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int droid_input_stream_set_route(pa_droid_stream *s, audio_devices_t device) {
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
pa_assert(s);
|
||||||
|
|
||||||
|
if (s->in) {
|
||||||
|
input_stream_set_route(s, device);
|
||||||
|
} else {
|
||||||
|
s->device = device;
|
||||||
|
pa_log_debug("input stream (inactive) %p store route %#010x", (void *) s, device);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pa_droid_stream_set_route(pa_droid_stream *s, audio_devices_t device) {
|
||||||
|
pa_assert(s);
|
||||||
|
|
||||||
|
if (s->out)
|
||||||
|
return droid_output_stream_set_route(s, device);
|
||||||
|
else
|
||||||
|
return droid_input_stream_set_route(s, device);
|
||||||
|
}
|
||||||
|
|
||||||
int pa_droid_stream_set_parameters(pa_droid_stream *s, const char *parameters) {
|
int pa_droid_stream_set_parameters(pa_droid_stream *s, const char *parameters) {
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
|
@ -2337,7 +2461,6 @@ bool pa_droid_stream_is_primary(pa_droid_stream *s) {
|
||||||
|
|
||||||
int pa_droid_stream_suspend(pa_droid_stream *s, bool suspend) {
|
int pa_droid_stream_suspend(pa_droid_stream *s, bool suspend) {
|
||||||
pa_assert(s);
|
pa_assert(s);
|
||||||
pa_assert(s->out || s->in);
|
|
||||||
|
|
||||||
if (s->out) {
|
if (s->out) {
|
||||||
if (suspend) {
|
if (suspend) {
|
||||||
|
|
@ -2345,14 +2468,26 @@ int pa_droid_stream_suspend(pa_droid_stream *s, bool suspend) {
|
||||||
return s->out->common.standby(&s->out->common);
|
return s->out->common.standby(&s->out->common);
|
||||||
} else {
|
} else {
|
||||||
pa_atomic_inc(&s->module->active_outputs);
|
pa_atomic_inc(&s->module->active_outputs);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (suspend)
|
if (suspend) {
|
||||||
return s->in->common.standby(&s->in->common);
|
if (s->in) {
|
||||||
else
|
if (pa_droid_quirk(s->module, QUIRK_CLOSE_INPUT))
|
||||||
return 0;
|
input_stream_close(s);
|
||||||
|
else
|
||||||
|
return s->in->common.standby(&s->in->common);
|
||||||
|
}
|
||||||
|
} else if (pa_droid_quirk(s->module, QUIRK_CLOSE_INPUT))
|
||||||
|
return input_stream_open(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pa_droid_stream_buffer_size(pa_droid_stream *s) {
|
||||||
|
pa_assert(s);
|
||||||
|
|
||||||
|
return s->buffer_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pa_sink_is_droid_sink(pa_sink *s) {
|
bool pa_sink_is_droid_sink(pa_sink *s) {
|
||||||
|
|
@ -2365,3 +2500,24 @@ bool pa_sink_is_droid_sink(pa_sink *s) {
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t pa_droid_buffer_size_round_up(size_t buffer_size, size_t block_size) {
|
||||||
|
size_t r;
|
||||||
|
|
||||||
|
pa_assert(buffer_size);
|
||||||
|
pa_assert(block_size);
|
||||||
|
|
||||||
|
r = buffer_size % block_size;
|
||||||
|
|
||||||
|
if (r)
|
||||||
|
return buffer_size + block_size - r;
|
||||||
|
|
||||||
|
return buffer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
pa_hook *pa_droid_hooks(pa_droid_hw_module *hw) {
|
||||||
|
pa_assert(hw);
|
||||||
|
pa_assert(PA_REFCNT_VALUE(hw) >= 1);
|
||||||
|
|
||||||
|
return hw->hooks;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,13 @@ typedef struct pa_droid_config_hw_module pa_droid_config_hw_module;
|
||||||
|
|
||||||
typedef struct pa_droid_quirks pa_droid_quirks;
|
typedef struct pa_droid_quirks pa_droid_quirks;
|
||||||
|
|
||||||
|
typedef enum pa_droid_hook {
|
||||||
|
PA_DROID_HOOK_INPUT_CHANNEL_MAP_CHANGED, /* Call data: pa_droid_stream */
|
||||||
|
PA_DROID_HOOK_INPUT_BUFFER_SIZE_CHANGED, /* Call data: pa_droid_stream */
|
||||||
|
PA_DROID_HOOK_MAX
|
||||||
|
} pa_droid_hook_t;
|
||||||
|
|
||||||
|
|
||||||
struct pa_droid_hw_module {
|
struct pa_droid_hw_module {
|
||||||
PA_REFCNT_DECLARE;
|
PA_REFCNT_DECLARE;
|
||||||
|
|
||||||
|
|
@ -106,9 +113,9 @@ struct pa_droid_hw_module {
|
||||||
pa_idxset *inputs;
|
pa_idxset *inputs;
|
||||||
|
|
||||||
pa_atomic_t active_outputs;
|
pa_atomic_t active_outputs;
|
||||||
uint32_t output_device;
|
|
||||||
|
|
||||||
pa_droid_quirks *quirks;
|
pa_droid_quirks *quirks;
|
||||||
|
pa_hook hooks[PA_DROID_HOOK_MAX];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pa_droid_stream {
|
struct pa_droid_stream {
|
||||||
|
|
@ -118,7 +125,11 @@ struct pa_droid_stream {
|
||||||
|
|
||||||
pa_sample_spec sample_spec;
|
pa_sample_spec sample_spec;
|
||||||
pa_channel_map channel_map;
|
pa_channel_map channel_map;
|
||||||
|
pa_sample_spec input_sample_spec;
|
||||||
|
pa_channel_map input_channel_map;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
|
uint32_t device;
|
||||||
|
size_t buffer_size;
|
||||||
|
|
||||||
struct audio_stream_out *out;
|
struct audio_stream_out *out;
|
||||||
struct audio_stream_in *in;
|
struct audio_stream_in *in;
|
||||||
|
|
@ -255,6 +266,7 @@ struct pa_droid_profile_set {
|
||||||
enum pa_droid_quirk_type {
|
enum pa_droid_quirk_type {
|
||||||
QUIRK_INPUT_ATOI,
|
QUIRK_INPUT_ATOI,
|
||||||
QUIRK_SET_PARAMETERS,
|
QUIRK_SET_PARAMETERS,
|
||||||
|
QUIRK_CLOSE_INPUT,
|
||||||
QUIRK_COUNT
|
QUIRK_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -270,8 +282,9 @@ void pa_droid_hw_module_lock(pa_droid_hw_module *hw);
|
||||||
bool pa_droid_hw_module_try_lock(pa_droid_hw_module *hw);
|
bool pa_droid_hw_module_try_lock(pa_droid_hw_module *hw);
|
||||||
void pa_droid_hw_module_unlock(pa_droid_hw_module *hw);
|
void pa_droid_hw_module_unlock(pa_droid_hw_module *hw);
|
||||||
|
|
||||||
bool pa_droid_parse_quirks(pa_droid_hw_module *hw, const char *quirks);
|
bool pa_droid_quirk_parse(pa_droid_hw_module *hw, const char *quirks);
|
||||||
bool pa_droid_quirk(pa_droid_hw_module *hw, enum pa_droid_quirk_type quirk);
|
bool pa_droid_quirk(pa_droid_hw_module *hw, enum pa_droid_quirk_type quirk);
|
||||||
|
void pa_droid_quirk_log(pa_droid_hw_module *hw);
|
||||||
|
|
||||||
/* Conversion helpers */
|
/* Conversion helpers */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
@ -340,6 +353,8 @@ bool pa_droid_input_port_name(audio_devices_t value, const char **to_str);
|
||||||
/* Pretty audio source names */
|
/* Pretty audio source names */
|
||||||
bool pa_droid_audio_source_name(audio_source_t value, const char **to_str);
|
bool pa_droid_audio_source_name(audio_source_t value, const char **to_str);
|
||||||
|
|
||||||
|
pa_hook *pa_droid_hooks(pa_droid_hw_module *hw);
|
||||||
|
|
||||||
/* Module operations */
|
/* Module operations */
|
||||||
int pa_droid_set_parameters(pa_droid_hw_module *hw, const char *parameters);
|
int pa_droid_set_parameters(pa_droid_hw_module *hw, const char *parameters);
|
||||||
|
|
||||||
|
|
@ -356,29 +371,44 @@ pa_droid_stream *pa_droid_open_output_stream(pa_droid_hw_module *module,
|
||||||
audio_output_flags_t flags,
|
audio_output_flags_t flags,
|
||||||
audio_devices_t devices);
|
audio_devices_t devices);
|
||||||
|
|
||||||
/* Set routing to the output stream, with following side-effects:
|
/* Set routing to the input or output stream, with following side-effects:
|
||||||
|
* Output:
|
||||||
* - if routing is set to primary output stream, set routing to all other
|
* - if routing is set to primary output stream, set routing to all other
|
||||||
* open streams as well
|
* open streams as well
|
||||||
* - if routing is set to non-primary stream and primary stream exists, do nothing
|
* - if routing is set to non-primary stream and primary stream exists, do nothing
|
||||||
* - if routing is set to non-primary stream and primary stream doesn't exist, set routing
|
* - if routing is set to non-primary stream and primary stream doesn't exist, set routing
|
||||||
|
* Input:
|
||||||
|
* - buffer size or channel count may change
|
||||||
*/
|
*/
|
||||||
int pa_droid_stream_set_output_route(pa_droid_stream *s, audio_devices_t device);
|
int pa_droid_stream_set_route(pa_droid_stream *s, audio_devices_t device);
|
||||||
|
|
||||||
/* Input stream operations */
|
/* Input stream operations */
|
||||||
pa_droid_stream *pa_droid_open_input_stream(pa_droid_hw_module *module,
|
pa_droid_stream *pa_droid_open_input_stream(pa_droid_hw_module *module,
|
||||||
const pa_sample_spec *spec,
|
const pa_sample_spec *spec,
|
||||||
const pa_channel_map *map,
|
const pa_channel_map *map,
|
||||||
audio_devices_t devices);
|
audio_devices_t devices);
|
||||||
int pa_droid_stream_set_input_route(pa_droid_stream *s, audio_devices_t device, audio_source_t *new_source);
|
|
||||||
|
|
||||||
bool pa_droid_stream_is_primary(pa_droid_stream *s);
|
bool pa_droid_stream_is_primary(pa_droid_stream *s);
|
||||||
|
|
||||||
int pa_droid_stream_suspend(pa_droid_stream *s, bool suspend);
|
int pa_droid_stream_suspend(pa_droid_stream *s, bool suspend);
|
||||||
|
|
||||||
|
size_t pa_droid_stream_buffer_size(pa_droid_stream *s);
|
||||||
|
|
||||||
static inline int pa_droid_output_stream_any_active(pa_droid_stream *s) {
|
static inline int pa_droid_output_stream_any_active(pa_droid_stream *s) {
|
||||||
return pa_atomic_load(&s->module->active_outputs);
|
return pa_atomic_load(&s->module->active_outputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline ssize_t pa_droid_stream_write(pa_droid_stream *stream, const void *buffer, size_t bytes) {
|
||||||
|
return stream->out->write(stream->out, buffer, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ssize_t pa_droid_stream_read(pa_droid_stream *stream, void *buffer, size_t bytes) {
|
||||||
|
return stream->in->read(stream->in, buffer, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
bool pa_sink_is_droid_sink(pa_sink *s);
|
bool pa_sink_is_droid_sink(pa_sink *s);
|
||||||
|
|
||||||
|
/* Misc */
|
||||||
|
size_t pa_droid_buffer_size_round_up(size_t buffer_size, size_t block_size);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -213,7 +213,7 @@ static void do_routing(struct userdata *u) {
|
||||||
|
|
||||||
routing = u->primary_devices | u->extra_devices;
|
routing = u->primary_devices | u->extra_devices;
|
||||||
|
|
||||||
pa_droid_stream_set_output_route(u->stream, routing);
|
pa_droid_stream_set_route(u->stream, routing);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool parse_device_list(const char *str, audio_devices_t *dst) {
|
static bool parse_device_list(const char *str, audio_devices_t *dst) {
|
||||||
|
|
@ -255,7 +255,7 @@ static int thread_write_silence(struct userdata *u) {
|
||||||
* here it's okay, as long as mute time isn't configured too strictly. */
|
* here it's okay, as long as mute time isn't configured too strictly. */
|
||||||
|
|
||||||
p = pa_memblock_acquire(u->silence.memblock);
|
p = pa_memblock_acquire(u->silence.memblock);
|
||||||
wrote = u->stream->out->write(u->stream->out, (const uint8_t*) p + u->silence.index, u->silence.length);
|
wrote = pa_droid_stream_write(u->stream, (const uint8_t *) p + u->silence.index, u->silence.length);
|
||||||
pa_memblock_release(u->silence.memblock);
|
pa_memblock_release(u->silence.memblock);
|
||||||
|
|
||||||
u->write_time = pa_rtclock_now() - u->write_time;
|
u->write_time = pa_rtclock_now() - u->write_time;
|
||||||
|
|
@ -280,7 +280,7 @@ static int thread_write(struct userdata *u) {
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
p = pa_memblock_acquire(c.memblock);
|
p = pa_memblock_acquire(c.memblock);
|
||||||
wrote = u->stream->out->write(u->stream->out, (const uint8_t*) p + c.index, c.length);
|
wrote = pa_droid_stream_write(u->stream, (const uint8_t *) p + c.index, c.length);
|
||||||
pa_memblock_release(c.memblock);
|
pa_memblock_release(c.memblock);
|
||||||
|
|
||||||
if (wrote < 0) {
|
if (wrote < 0) {
|
||||||
|
|
@ -1199,19 +1199,12 @@ pa_sink *pa_droid_sink_new(pa_module *m,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
u->buffer_size = u->stream->out->common.get_buffer_size(&u->stream->out->common);
|
u->buffer_size = pa_droid_stream_buffer_size(u->stream);
|
||||||
if (sink_buffer) {
|
if (sink_buffer) {
|
||||||
if (sink_buffer < u->buffer_size)
|
u->buffer_size = pa_droid_buffer_size_round_up(sink_buffer, u->buffer_size);
|
||||||
pa_log_warn("Requested buffer size %u less than HAL reported buffer size (%u).", sink_buffer, u->buffer_size);
|
pa_log_info("Using buffer size %u (requested %u).", u->buffer_size, sink_buffer);
|
||||||
else if (sink_buffer % u->buffer_size) {
|
} else
|
||||||
uint32_t trunc = (sink_buffer / u->buffer_size) * u->buffer_size;
|
pa_log_info("Using buffer size %u.", u->buffer_size);
|
||||||
pa_log_warn("Requested buffer size %u not multiple of HAL buffer size (%u). Using buffer size %u", sink_buffer, u->buffer_size, trunc);
|
|
||||||
u->buffer_size = trunc;
|
|
||||||
} else {
|
|
||||||
pa_log_info("Using requested buffer size %u.", sink_buffer);
|
|
||||||
u->buffer_size = sink_buffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((prewrite_resume = pa_modargs_get_value(ma, "prewrite_on_resume", NULL))) {
|
if ((prewrite_resume = pa_modargs_get_value(ma, "prewrite_on_resume", NULL))) {
|
||||||
if (!parse_prewrite_on_resume(u, prewrite_resume, am ? am->output->name : module_id)) {
|
if (!parse_prewrite_on_resume(u, prewrite_resume, am ? am->output->name : module_id)) {
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@
|
||||||
#include <pulsecore/thread-mq.h>
|
#include <pulsecore/thread-mq.h>
|
||||||
#include <pulsecore/rtpoll.h>
|
#include <pulsecore/rtpoll.h>
|
||||||
#include <pulsecore/time-smoother.h>
|
#include <pulsecore/time-smoother.h>
|
||||||
|
#include <pulsecore/resampler.h>
|
||||||
|
|
||||||
#include "droid-source.h"
|
#include "droid-source.h"
|
||||||
#include "droid-util.h"
|
#include "droid-util.h"
|
||||||
|
|
@ -66,16 +67,24 @@ struct userdata {
|
||||||
|
|
||||||
pa_memchunk memchunk;
|
pa_memchunk memchunk;
|
||||||
audio_devices_t primary_devices;
|
audio_devices_t primary_devices;
|
||||||
bool routing_changes_enabled;
|
|
||||||
|
|
||||||
|
size_t source_buffer_size;
|
||||||
size_t buffer_size;
|
size_t buffer_size;
|
||||||
pa_usec_t timestamp;
|
pa_usec_t timestamp;
|
||||||
|
|
||||||
|
pa_hook_slot *input_buffer_size_changed_slot;
|
||||||
|
pa_hook_slot *input_channel_map_changed_slot;
|
||||||
|
pa_resampler *resampler;
|
||||||
|
|
||||||
pa_droid_card_data *card_data;
|
pa_droid_card_data *card_data;
|
||||||
pa_droid_hw_module *hw_module;
|
pa_droid_hw_module *hw_module;
|
||||||
pa_droid_stream *stream;
|
pa_droid_stream *stream;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SOURCE_MESSAGE_DO_ROUTING = PA_SOURCE_MESSAGE_MAX
|
||||||
|
};
|
||||||
|
|
||||||
#define DEFAULT_MODULE_ID "primary"
|
#define DEFAULT_MODULE_ID "primary"
|
||||||
|
|
||||||
#define DROID_AUDIO_SOURCE "droid.audio_source"
|
#define DROID_AUDIO_SOURCE "droid.audio_source"
|
||||||
|
|
@ -83,46 +92,23 @@ struct userdata {
|
||||||
|
|
||||||
static void userdata_free(struct userdata *u);
|
static void userdata_free(struct userdata *u);
|
||||||
|
|
||||||
static int do_routing(struct userdata *u, audio_devices_t devices, bool force) {
|
static int do_routing(struct userdata *u, audio_devices_t devices) {
|
||||||
int ret;
|
int ret;
|
||||||
pa_proplist *p;
|
|
||||||
const char *source_str;
|
|
||||||
audio_devices_t old_device;
|
audio_devices_t old_device;
|
||||||
audio_source_t source;
|
|
||||||
|
|
||||||
pa_assert(u);
|
pa_assert(u);
|
||||||
pa_assert(u->stream);
|
pa_assert(u->stream);
|
||||||
|
|
||||||
if (!force && !u->routing_changes_enabled) {
|
if (u->primary_devices == devices)
|
||||||
pa_log_debug("Skipping routing change.");
|
pa_log_debug("Refresh active device routing.");
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (u->primary_devices == devices) {
|
|
||||||
if (force)
|
|
||||||
pa_log_debug("Refresh active device routing.");
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
old_device = u->primary_devices;
|
old_device = u->primary_devices;
|
||||||
u->primary_devices = devices;
|
u->primary_devices = devices;
|
||||||
|
|
||||||
ret = pa_droid_stream_set_input_route(u->stream, devices, &source);
|
ret = pa_droid_stream_set_route(u->stream, devices);
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
u->primary_devices = old_device;
|
u->primary_devices = old_device;
|
||||||
else {
|
|
||||||
if (source != (uint32_t) -1)
|
|
||||||
pa_assert_se(pa_droid_audio_source_name(source, &source_str));
|
|
||||||
else
|
|
||||||
source_str = DROID_AUDIO_SOURCE_UNDEFINED;
|
|
||||||
|
|
||||||
p = pa_proplist_new();
|
|
||||||
pa_proplist_sets(p, DROID_AUDIO_SOURCE, source_str);
|
|
||||||
pa_source_update_proplist(u->source, PA_UPDATE_REPLACE, p);
|
|
||||||
pa_proplist_free(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
@ -161,7 +147,7 @@ static int thread_read(struct userdata *u) {
|
||||||
chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) u->buffer_size);
|
chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) u->buffer_size);
|
||||||
|
|
||||||
p = pa_memblock_acquire(chunk.memblock);
|
p = pa_memblock_acquire(chunk.memblock);
|
||||||
readd = u->stream->in->read(u->stream->in, (uint8_t*) p, pa_memblock_get_length(chunk.memblock));
|
readd = pa_droid_stream_read(u->stream, p, pa_memblock_get_length(chunk.memblock));
|
||||||
pa_memblock_release(chunk.memblock);
|
pa_memblock_release(chunk.memblock);
|
||||||
|
|
||||||
if (readd < 0) {
|
if (readd < 0) {
|
||||||
|
|
@ -174,6 +160,19 @@ static int thread_read(struct userdata *u) {
|
||||||
chunk.index = 0;
|
chunk.index = 0;
|
||||||
chunk.length = readd;
|
chunk.length = readd;
|
||||||
|
|
||||||
|
if (u->resampler) {
|
||||||
|
pa_memchunk rchunk;
|
||||||
|
|
||||||
|
pa_resampler_run(u->resampler, &chunk, &rchunk);
|
||||||
|
|
||||||
|
if (rchunk.length > 0)
|
||||||
|
pa_source_post(u->source, &rchunk);
|
||||||
|
if (rchunk.memblock)
|
||||||
|
pa_memblock_unref(rchunk.memblock);
|
||||||
|
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
if (chunk.length > 0)
|
if (chunk.length > 0)
|
||||||
pa_source_post(u->source, &chunk);
|
pa_source_post(u->source, &chunk);
|
||||||
|
|
||||||
|
|
@ -198,8 +197,6 @@ static void thread_func(void *userdata) {
|
||||||
|
|
||||||
u->timestamp = pa_rtclock_now();
|
u->timestamp = pa_rtclock_now();
|
||||||
|
|
||||||
u->stream->in->common.standby(&u->stream->in->common);
|
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
|
@ -262,24 +259,37 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
|
||||||
struct userdata *u = PA_SOURCE(o)->userdata;
|
struct userdata *u = PA_SOURCE(o)->userdata;
|
||||||
|
|
||||||
switch (code) {
|
switch (code) {
|
||||||
|
case SOURCE_MESSAGE_DO_ROUTING: {
|
||||||
|
audio_devices_t device = PA_PTR_TO_UINT(data);
|
||||||
|
|
||||||
|
pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
|
||||||
|
|
||||||
|
pa_droid_stream_suspend(u->stream, true);
|
||||||
|
do_routing(u, device);
|
||||||
|
pa_droid_stream_suspend(u->stream, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case PA_SOURCE_MESSAGE_SET_STATE: {
|
case PA_SOURCE_MESSAGE_SET_STATE: {
|
||||||
switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
|
switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
|
||||||
case PA_SOURCE_SUSPENDED: {
|
case PA_SOURCE_SUSPENDED: {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
|
if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
|
||||||
|
if ((r = suspend(u)) < 0)
|
||||||
if ((r = suspend(u)) < 0)
|
return r;
|
||||||
return r;
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case PA_SOURCE_IDLE:
|
case PA_SOURCE_IDLE:
|
||||||
break;
|
/* Fall through */
|
||||||
case PA_SOURCE_RUNNING: {
|
case PA_SOURCE_RUNNING: {
|
||||||
unsuspend(u);
|
if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) {
|
||||||
u->timestamp = pa_rtclock_now();
|
unsuspend(u);
|
||||||
|
u->timestamp = pa_rtclock_now();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -301,7 +311,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
|
||||||
return pa_source_process_msg(o, code, data, offset, chunk);
|
return pa_source_process_msg(o, code, data, offset, chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int droid_source_set_port(pa_source *s, pa_device_port *p, bool force) {
|
static int source_set_port_cb(pa_source *s, pa_device_port *p) {
|
||||||
struct userdata *u = s->userdata;
|
struct userdata *u = s->userdata;
|
||||||
pa_droid_port_data *data;
|
pa_droid_port_data *data;
|
||||||
|
|
||||||
|
|
@ -320,33 +330,13 @@ static int droid_source_set_port(pa_source *s, pa_device_port *p, bool force) {
|
||||||
|
|
||||||
pa_log_debug("Source set port %u", data->device);
|
pa_log_debug("Source set port %u", data->device);
|
||||||
|
|
||||||
return do_routing(u, data->device, force);
|
if (!PA_SOURCE_IS_OPENED(pa_source_get_state(u->source)))
|
||||||
}
|
do_routing(u, data->device);
|
||||||
|
else {
|
||||||
int pa_droid_source_set_port(pa_source *s, pa_device_port *p) {
|
pa_asyncmsgq_post(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_DO_ROUTING, PA_UINT_TO_PTR(data->device), 0, NULL, NULL);
|
||||||
return droid_source_set_port(s, p, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int source_set_port_cb(pa_source *s, pa_device_port *p) {
|
|
||||||
return droid_source_set_port(s, p, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void source_set_voicecall_source_port(struct userdata *u) {
|
|
||||||
pa_device_port *port;
|
|
||||||
pa_droid_port_data *data;
|
|
||||||
void *state;
|
|
||||||
|
|
||||||
pa_assert(u);
|
|
||||||
pa_assert(u->source);
|
|
||||||
|
|
||||||
PA_HASHMAP_FOREACH(port, u->source->ports, state) {
|
|
||||||
data = PA_DEVICE_PORT_DATA(port);
|
|
||||||
|
|
||||||
if (data->device & AUDIO_DEVICE_IN_VOICE_CALL) {
|
|
||||||
pa_source_set_port(u->source, port->name, false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void source_set_name(pa_modargs *ma, pa_source_new_data *data, const char *module_id) {
|
static void source_set_name(pa_modargs *ma, pa_source_new_data *data, const char *module_id) {
|
||||||
|
|
@ -427,15 +417,71 @@ static void source_set_mute_control(struct userdata *u) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pa_droid_source_set_routing(pa_source *s, bool enabled) {
|
/* Called from main and IO context */
|
||||||
struct userdata *u = s->userdata;
|
static void update_latency(struct userdata *u) {
|
||||||
|
pa_assert(u);
|
||||||
|
pa_assert(u->source);
|
||||||
|
pa_assert(u->stream);
|
||||||
|
|
||||||
pa_assert(s);
|
u->buffer_size = pa_droid_stream_buffer_size(u->stream);
|
||||||
pa_assert(s->userdata);
|
|
||||||
|
|
||||||
if (u->routing_changes_enabled != enabled)
|
if (u->source_buffer_size) {
|
||||||
pa_log_debug("%s source routing changes.", enabled ? "Enabling" : "Disabling");
|
u->buffer_size = pa_droid_buffer_size_round_up(u->source_buffer_size, u->buffer_size);
|
||||||
u->routing_changes_enabled = enabled;
|
pa_log_info("Using buffer size %u (requested %u).", u->buffer_size, u->source_buffer_size);
|
||||||
|
} else
|
||||||
|
pa_log_info("Using buffer size %u.", u->buffer_size);
|
||||||
|
|
||||||
|
if (pa_thread_mq_get())
|
||||||
|
pa_source_set_fixed_latency_within_thread(u->source, pa_bytes_to_usec(u->buffer_size, &u->stream->sample_spec));
|
||||||
|
else
|
||||||
|
pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->buffer_size, &u->stream->sample_spec));
|
||||||
|
|
||||||
|
pa_log_debug("Set fixed latency %" PRIu64 " usec", pa_bytes_to_usec(u->buffer_size, &u->stream->sample_spec));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called from IO context. */
|
||||||
|
static pa_hook_result_t input_buffer_size_changed_cb(pa_droid_hw_module *module,
|
||||||
|
pa_droid_stream *stream,
|
||||||
|
struct userdata *u) {
|
||||||
|
pa_assert(module);
|
||||||
|
pa_assert(stream);
|
||||||
|
pa_assert(u);
|
||||||
|
|
||||||
|
if (stream != u->stream)
|
||||||
|
return PA_HOOK_OK;
|
||||||
|
|
||||||
|
update_latency(u);
|
||||||
|
|
||||||
|
return PA_HOOK_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called from IO context. */
|
||||||
|
static pa_hook_result_t input_channel_map_changed_cb(pa_droid_hw_module *module,
|
||||||
|
pa_droid_stream *stream,
|
||||||
|
struct userdata *u) {
|
||||||
|
pa_assert(module);
|
||||||
|
pa_assert(stream);
|
||||||
|
pa_assert(u);
|
||||||
|
|
||||||
|
if (stream != u->stream)
|
||||||
|
return PA_HOOK_OK;
|
||||||
|
|
||||||
|
if (u->stream->input_channel_map.channels != u->source->channel_map.channels) {
|
||||||
|
if (u->resampler)
|
||||||
|
pa_resampler_free(u->resampler);
|
||||||
|
|
||||||
|
u->resampler = pa_resampler_new(u->core->mempool,
|
||||||
|
&u->stream->input_sample_spec, &u->stream->input_channel_map,
|
||||||
|
&u->source->sample_spec, &u->source->channel_map,
|
||||||
|
u->core->lfe_crossover_freq,
|
||||||
|
PA_RESAMPLER_COPY,
|
||||||
|
0);
|
||||||
|
} else if (u->resampler) {
|
||||||
|
pa_resampler_free(u->resampler);
|
||||||
|
u->resampler = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PA_HOOK_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_source *pa_droid_source_new(pa_module *m,
|
pa_source *pa_droid_source_new(pa_module *m,
|
||||||
|
|
@ -459,7 +505,6 @@ pa_source *pa_droid_source_new(pa_module *m,
|
||||||
bool namereg_fail = false;
|
bool namereg_fail = false;
|
||||||
pa_droid_config_audio *config = NULL; /* Only used when source is created without card */
|
pa_droid_config_audio *config = NULL; /* Only used when source is created without card */
|
||||||
uint32_t source_buffer = 0;
|
uint32_t source_buffer = 0;
|
||||||
bool voicecall_source = false;
|
|
||||||
|
|
||||||
pa_assert(m);
|
pa_assert(m);
|
||||||
pa_assert(ma);
|
pa_assert(ma);
|
||||||
|
|
@ -474,11 +519,6 @@ pa_source *pa_droid_source_new(pa_module *m,
|
||||||
sample_spec = m->core->default_sample_spec;
|
sample_spec = m->core->default_sample_spec;
|
||||||
channel_map = m->core->default_channel_map;
|
channel_map = m->core->default_channel_map;
|
||||||
|
|
||||||
if (device & AUDIO_DEVICE_IN_VOICE_CALL) {
|
|
||||||
pa_log_info("Enabling voice call record source. Most module arguments are overridden.");
|
|
||||||
voicecall_source = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* First parse both sample spec and channel map, then see if source_* override some
|
/* First parse both sample spec and channel map, then see if source_* override some
|
||||||
* of the values. */
|
* of the values. */
|
||||||
if (pa_modargs_get_sample_spec_and_channel_map(ma, &sample_spec, &channel_map, PA_CHANNEL_MAP_AIFF) < 0) {
|
if (pa_modargs_get_sample_spec_and_channel_map(ma, &sample_spec, &channel_map, PA_CHANNEL_MAP_AIFF) < 0) {
|
||||||
|
|
@ -530,9 +570,6 @@ pa_source *pa_droid_source_new(pa_module *m,
|
||||||
u->rtpoll = pa_rtpoll_new();
|
u->rtpoll = pa_rtpoll_new();
|
||||||
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
|
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
|
||||||
|
|
||||||
/* Enabled routing changes by default, except for voicecall source. */
|
|
||||||
u->routing_changes_enabled = voicecall_source ? false : true;
|
|
||||||
|
|
||||||
if (card_data) {
|
if (card_data) {
|
||||||
pa_assert(card);
|
pa_assert(card);
|
||||||
u->card_data = card_data;
|
u->card_data = card_data;
|
||||||
|
|
@ -577,24 +614,11 @@ pa_source *pa_droid_source_new(pa_module *m,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
u->buffer_size = u->stream->in->common.get_buffer_size(&u->stream->in->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_source_new_data_init(&data);
|
pa_source_new_data_init(&data);
|
||||||
data.driver = driver;
|
data.driver = driver;
|
||||||
data.module = m;
|
data.module = m;
|
||||||
data.card = card;
|
data.card = card;
|
||||||
|
data.suspend_cause = PA_SUSPEND_IDLE;
|
||||||
|
|
||||||
source_set_name(ma, &data, module_id);
|
source_set_name(ma, &data, module_id);
|
||||||
|
|
||||||
|
|
@ -647,14 +671,18 @@ pa_source *pa_droid_source_new(pa_module *m,
|
||||||
pa_xfree(thread_name);
|
pa_xfree(thread_name);
|
||||||
thread_name = NULL;
|
thread_name = NULL;
|
||||||
|
|
||||||
pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->buffer_size, &u->stream->sample_spec));
|
update_latency(u);
|
||||||
pa_log_debug("Set fixed latency %" PRIu64 " usec", pa_bytes_to_usec(u->buffer_size, &u->stream->sample_spec));
|
|
||||||
|
|
||||||
if (!voicecall_source && u->source->active_port)
|
if (u->source->active_port)
|
||||||
source_set_port_cb(u->source, u->source->active_port);
|
source_set_port_cb(u->source, u->source->active_port);
|
||||||
|
|
||||||
if (voicecall_source)
|
u->input_buffer_size_changed_slot = pa_hook_connect(&pa_droid_hooks(u->hw_module)[PA_DROID_HOOK_INPUT_BUFFER_SIZE_CHANGED],
|
||||||
source_set_voicecall_source_port(u);
|
PA_HOOK_NORMAL,
|
||||||
|
(pa_hook_cb_t) input_buffer_size_changed_cb, u);
|
||||||
|
|
||||||
|
u->input_channel_map_changed_slot = pa_hook_connect(&pa_droid_hooks(u->hw_module)[PA_DROID_HOOK_INPUT_CHANNEL_MAP_CHANGED],
|
||||||
|
PA_HOOK_NORMAL,
|
||||||
|
(pa_hook_cb_t) input_channel_map_changed_cb, u);
|
||||||
|
|
||||||
pa_source_put(u->source);
|
pa_source_put(u->source);
|
||||||
|
|
||||||
|
|
@ -682,6 +710,13 @@ void pa_droid_source_free(pa_source *s) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void userdata_free(struct userdata *u) {
|
static void userdata_free(struct userdata *u) {
|
||||||
|
pa_assert(u);
|
||||||
|
|
||||||
|
if (u->input_channel_map_changed_slot)
|
||||||
|
pa_hook_slot_free(u->input_channel_map_changed_slot);
|
||||||
|
|
||||||
|
if (u->input_buffer_size_changed_slot)
|
||||||
|
pa_hook_slot_free(u->input_buffer_size_changed_slot);
|
||||||
|
|
||||||
if (u->source)
|
if (u->source)
|
||||||
pa_source_unlink(u->source);
|
pa_source_unlink(u->source);
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,4 @@ pa_source *pa_droid_source_new(pa_module *m,
|
||||||
pa_card *card);
|
pa_card *card);
|
||||||
void pa_droid_source_free(pa_source *s);
|
void pa_droid_source_free(pa_source *s);
|
||||||
|
|
||||||
void pa_droid_source_set_routing(pa_source *s, bool enabled);
|
|
||||||
int pa_droid_source_set_port(pa_source *s, pa_device_port *p);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ PA_MODULE_USAGE(
|
||||||
"rate=<sample rate> "
|
"rate=<sample rate> "
|
||||||
"output_flags=<flags for sink> "
|
"output_flags=<flags for sink> "
|
||||||
"module_id=<which droid hw module to load, default primary> "
|
"module_id=<which droid hw module to load, default primary> "
|
||||||
"voice_source_routing=<route source ports during voice call, default false> "
|
"voice_source_routing=<always true, parameter left for compatibility> "
|
||||||
"deferred_volume=<synchronize software and hardware volume changes to avoid momentary jumps?> "
|
"deferred_volume=<synchronize software and hardware volume changes to avoid momentary jumps?> "
|
||||||
"config=<location for droid audio configuration> "
|
"config=<location for droid audio configuration> "
|
||||||
"voice_property_key=<proplist key searched for sink-input that should control voice call volume> "
|
"voice_property_key=<proplist key searched for sink-input that should control voice call volume> "
|
||||||
|
|
@ -151,9 +151,6 @@ struct userdata {
|
||||||
pa_droid_card_data card_data;
|
pa_droid_card_data card_data;
|
||||||
|
|
||||||
pa_droid_profile *old_profile;
|
pa_droid_profile *old_profile;
|
||||||
pa_source *voicecall_source;
|
|
||||||
|
|
||||||
bool voice_source_routing;
|
|
||||||
|
|
||||||
pa_modargs *modargs;
|
pa_modargs *modargs;
|
||||||
pa_card *card;
|
pa_card *card;
|
||||||
|
|
@ -445,8 +442,6 @@ static bool voicecall_profile_event_cb(struct userdata *u, pa_droid_profile *p,
|
||||||
/* call mode specialities */
|
/* call mode specialities */
|
||||||
if (enabling) {
|
if (enabling) {
|
||||||
pa_droid_sink_set_voice_control(am_output->sink, true);
|
pa_droid_sink_set_voice_control(am_output->sink, true);
|
||||||
if (am_input && !u->voice_source_routing)
|
|
||||||
pa_droid_source_set_routing(am_input->source, false);
|
|
||||||
if (am_input && am_input->input->devices & AUDIO_DEVICE_IN_VOICE_CALL &&
|
if (am_input && am_input->input->devices & AUDIO_DEVICE_IN_VOICE_CALL &&
|
||||||
(cp = pa_hashmap_get(u->card->profiles, VOICE_RECORD_PROFILE_NAME))) {
|
(cp = pa_hashmap_get(u->card->profiles, VOICE_RECORD_PROFILE_NAME))) {
|
||||||
if (cp->available == PA_AVAILABLE_NO) {
|
if (cp->available == PA_AVAILABLE_NO) {
|
||||||
|
|
@ -456,8 +451,6 @@ static bool voicecall_profile_event_cb(struct userdata *u, pa_droid_profile *p,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pa_droid_sink_set_voice_control(am_output->sink, false);
|
pa_droid_sink_set_voice_control(am_output->sink, false);
|
||||||
if (am_input && !u->voice_source_routing)
|
|
||||||
pa_droid_source_set_routing(am_input->source, true);
|
|
||||||
if (am_input && am_input->input->devices & AUDIO_DEVICE_IN_VOICE_CALL &&
|
if (am_input && am_input->input->devices & AUDIO_DEVICE_IN_VOICE_CALL &&
|
||||||
(cp = pa_hashmap_get(u->card->profiles, VOICE_RECORD_PROFILE_NAME))) {
|
(cp = pa_hashmap_get(u->card->profiles, VOICE_RECORD_PROFILE_NAME))) {
|
||||||
if (cp->available == PA_AVAILABLE_YES) {
|
if (cp->available == PA_AVAILABLE_YES) {
|
||||||
|
|
@ -470,105 +463,6 @@ static bool voicecall_profile_event_cb(struct userdata *u, pa_droid_profile *p,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (AUDIO_API_VERSION_MAJ == 1) || \
|
|
||||||
(defined(QCOM_HARDWARE) && ANDROID_VERSION_MAJOR == 5 && ANDROID_VERSION_MINOR == 1)
|
|
||||||
static bool voicecall_record_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) {
|
|
||||||
pa_queue *source_outputs = NULL;
|
|
||||||
pa_droid_mapping *am;
|
|
||||||
|
|
||||||
pa_assert_ctl_context();
|
|
||||||
pa_assert(u);
|
|
||||||
pa_assert(p);
|
|
||||||
pa_assert(u->old_profile);
|
|
||||||
|
|
||||||
if (enabling) {
|
|
||||||
/* don't do anything if voicecall source has already been created. */
|
|
||||||
if (u->voicecall_source)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
pa_log_info("Enabling voice call record.");
|
|
||||||
|
|
||||||
am = pa_droid_idxset_get_primary(u->old_profile->input_mappings);
|
|
||||||
|
|
||||||
if (am && am->source) {
|
|
||||||
source_outputs = pa_source_move_all_start(am->source, source_outputs);
|
|
||||||
pa_droid_source_free(am->source);
|
|
||||||
am->source = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
u->voicecall_source = pa_droid_source_new(u->module, u->modargs, __FILE__, AUDIO_DEVICE_IN_VOICE_CALL, &u->card_data, am, u->card);
|
|
||||||
if (!u->voicecall_source)
|
|
||||||
pa_log("Failed to enable voice call recording.");
|
|
||||||
|
|
||||||
if (u->voicecall_source && source_outputs) {
|
|
||||||
pa_source_move_all_finish(u->voicecall_source, source_outputs, false);
|
|
||||||
source_outputs = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
pa_log_info("Disabling voice call record.");
|
|
||||||
|
|
||||||
if (u->voicecall_source) {
|
|
||||||
source_outputs = pa_source_move_all_start(u->voicecall_source, source_outputs);
|
|
||||||
pa_droid_source_free(u->voicecall_source);
|
|
||||||
u->voicecall_source = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
am = pa_droid_idxset_get_primary(u->old_profile->input_mappings);
|
|
||||||
|
|
||||||
if (am && !am->source) {
|
|
||||||
am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, (audio_devices_t) 0, &u->card_data, am, u->card);
|
|
||||||
|
|
||||||
if (source_outputs && am->source) {
|
|
||||||
pa_source_move_all_finish(am->source, source_outputs, false);
|
|
||||||
source_outputs = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (source_outputs)
|
|
||||||
pa_source_move_all_fail(source_outputs);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
static bool voicecall_record_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) {
|
|
||||||
pa_droid_mapping *am;
|
|
||||||
pa_device_port *port;
|
|
||||||
const char *port_name;
|
|
||||||
|
|
||||||
pa_assert_ctl_context();
|
|
||||||
pa_assert(u);
|
|
||||||
pa_assert(p);
|
|
||||||
pa_assert(u->old_profile);
|
|
||||||
|
|
||||||
if (!(am = pa_droid_idxset_get_primary(u->old_profile->input_mappings))) {
|
|
||||||
pa_log("Active profile doesn't have primary input device. Cannot enable record profile.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!am->source) {
|
|
||||||
pa_log("No active source, refusing to switch source port.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pa_source_assert_ref(am->source);
|
|
||||||
|
|
||||||
pa_assert_se(pa_droid_input_port_name(enabling ? AUDIO_DEVICE_IN_VOICE_CALL : AUDIO_DEVICE_IN_DEFAULT,
|
|
||||||
&port_name));
|
|
||||||
pa_assert_se((port = pa_hashmap_get(am->source->ports, port_name)));
|
|
||||||
|
|
||||||
if (pa_droid_source_set_port(am->source, port) != 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
pa_hook_fire(&u->core->hooks[PA_CORE_HOOK_SOURCE_PORT_CHANGED], am->source);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef DROID_AUDIO_HAL_USE_VSID
|
#ifdef DROID_AUDIO_HAL_USE_VSID
|
||||||
static bool voicecall_vsid(struct userdata *u, pa_droid_profile *p, uint32_t vsid, bool enabling)
|
static bool voicecall_vsid(struct userdata *u, pa_droid_profile *p, uint32_t vsid, bool enabling)
|
||||||
{
|
{
|
||||||
|
|
@ -781,7 +675,6 @@ int pa__init(pa_module *m) {
|
||||||
pa_droid_config_audio *config = NULL;
|
pa_droid_config_audio *config = NULL;
|
||||||
const char *module_id;
|
const char *module_id;
|
||||||
bool namereg_fail = false;
|
bool namereg_fail = false;
|
||||||
bool voice_source_routing = false;
|
|
||||||
pa_card_profile *virtual;
|
pa_card_profile *virtual;
|
||||||
const char *combine;
|
const char *combine;
|
||||||
const char *quirks;
|
const char *quirks;
|
||||||
|
|
@ -802,12 +695,6 @@ int pa__init(pa_module *m) {
|
||||||
if (!(config = pa_droid_config_load(ma)))
|
if (!(config = pa_droid_config_load(ma)))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
if (pa_modargs_get_value_boolean(ma, "voice_source_routing", &voice_source_routing) < 0) {
|
|
||||||
pa_log("Failed to parse voice_source_routing argument.");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
u->voice_source_routing = voice_source_routing;
|
|
||||||
|
|
||||||
module_id = pa_modargs_get_value(ma, "module_id", DEFAULT_MODULE_ID);
|
module_id = pa_modargs_get_value(ma, "module_id", DEFAULT_MODULE_ID);
|
||||||
|
|
||||||
/* Ownership of config transfers to hw_module if opening of hw module succeeds. */
|
/* Ownership of config transfers to hw_module if opening of hw module succeeds. */
|
||||||
|
|
@ -815,12 +702,14 @@ int pa__init(pa_module *m) {
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
if ((quirks = pa_modargs_get_value(ma, "quirks", NULL))) {
|
if ((quirks = pa_modargs_get_value(ma, "quirks", NULL))) {
|
||||||
if (!pa_droid_parse_quirks(u->hw_module, quirks)) {
|
if (!pa_droid_quirk_parse(u->hw_module, quirks)) {
|
||||||
pa_log("Failed to parse quirks.");
|
pa_log("Failed to parse quirks.");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pa_droid_quirk_log(u->hw_module);
|
||||||
|
|
||||||
u->card_data.set_parameters = set_parameters_cb;
|
u->card_data.set_parameters = set_parameters_cb;
|
||||||
u->card_data.module_id = pa_xstrdup(module_id);
|
u->card_data.module_id = pa_xstrdup(module_id);
|
||||||
u->card_data.userdata = u;
|
u->card_data.userdata = u;
|
||||||
|
|
@ -892,7 +781,7 @@ int pa__init(pa_module *m) {
|
||||||
AUDIO_MODE_IN_CALL, voicecall_profile_event_cb,
|
AUDIO_MODE_IN_CALL, voicecall_profile_event_cb,
|
||||||
PA_AVAILABLE_YES, NULL, data.profiles);
|
PA_AVAILABLE_YES, NULL, data.profiles);
|
||||||
add_virtual_profile(u, VOICE_RECORD_PROFILE_NAME, VOICE_RECORD_PROFILE_DESC,
|
add_virtual_profile(u, VOICE_RECORD_PROFILE_NAME, VOICE_RECORD_PROFILE_DESC,
|
||||||
AUDIO_MODE_IN_CALL, voicecall_record_profile_event_cb,
|
AUDIO_MODE_IN_CALL, NULL,
|
||||||
PA_AVAILABLE_NO, virtual, data.profiles);
|
PA_AVAILABLE_NO, virtual, data.profiles);
|
||||||
add_virtual_profile(u, COMMUNICATION_PROFILE_NAME, COMMUNICATION_PROFILE_DESC,
|
add_virtual_profile(u, COMMUNICATION_PROFILE_NAME, COMMUNICATION_PROFILE_DESC,
|
||||||
AUDIO_MODE_IN_COMMUNICATION, NULL,
|
AUDIO_MODE_IN_COMMUNICATION, NULL,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue