Merge pull request #95 from jusa/input-fixes

Input fixes
This commit is contained in:
Juho Hämäläinen 2019-11-19 15:46:09 +02:00 committed by GitHub
commit 25abbb4388
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 519 additions and 221 deletions

9
README
View file

@ -15,6 +15,7 @@ Supported Android versions:
* 6.0.x
* 7.x
* 8.x
* 9.x
Headers for defining devices and strings for different droid versions are in
src/common/droid-util-audio.h (legacy headers for Jolla 1 in droid-util-41qc.h).
@ -371,6 +372,14 @@ Currently there are following quirks:
* Some HAL module implementations get stuck in mutex or segfault when
trying to unload the module. To avoid confusing segfaults call
exit(0) instead of calling unload for the module.
* output_fast
* Enabled by default.
* Create separate sink if AUDIO_OUTPUT_FLAG_FAST is found. If this sink
is misbehaving try disabling this quirk.
* output_deep_buffer
* Enabled by default.
* Create separate sink if AUDIO_OUTPUT_FLAG_DEEP_BUFFER is found. If
this sink is misbehaving try disabling this quirk.
For example, to disable input_atoi and enable close_input quirks, use module
argument

View file

@ -343,86 +343,70 @@ static char *xml_string_dup(const XML_Char *xml_str, int len)
return str;
}
static void device_free(struct device *d) {
pa_assert(d);
pa_xfree(d->name);
pa_xfree(d);
}
static void profile_free(struct profile *p) {
pa_assert(p);
pa_xfree(p->name);
pa_xfree(p);
}
static void mix_port_free(struct mix_port *p) {
struct profile *profile;
pa_assert(p);
while (p->profiles) {
SLLIST_STEAL_FIRST(profile, p->profiles);
profile_free(profile);
};
pa_xfree(p->name);
pa_xfree(p->role);
pa_xfree(p);
}
static void device_port_free(struct device_port *p) {
pa_assert(p);
pa_xfree(p->tag_name);
pa_xfree(p->role);
pa_xfree(p);
}
static void route_free(struct route *r) {
static void device_list_free(struct device *list) {
struct device *d;
pa_assert(r);
while (r->sources) {
SLLIST_STEAL_FIRST(d, r->sources);
device_free(d);
while (list) {
SLLIST_STEAL_FIRST(d, list);
pa_xfree(d->name);
pa_xfree(d);
}
}
static void profile_list_free(struct profile *list) {
struct profile *p;
while (list) {
SLLIST_STEAL_FIRST(p, list);
pa_xfree(p->name);
pa_xfree(p);
};
}
static void mix_port_list_free(struct mix_port *list) {
struct mix_port *p;
while (list) {
SLLIST_STEAL_FIRST(p, list);
profile_list_free(p->profiles);
pa_xfree(p->name);
pa_xfree(p->role);
pa_xfree(p);
}
}
static void device_port_list_free(struct device_port *list) {
struct device_port *p;
while (list) {
SLLIST_STEAL_FIRST(p, list);
profile_list_free(p->profiles);
pa_xfree(p->tag_name);
pa_xfree(p->role);
pa_xfree(p);
}
}
static void route_list_free(struct route *list) {
struct route *r;
while (list) {
SLLIST_STEAL_FIRST(r, list);
device_list_free(r->sources);
pa_xfree(r->type);
pa_xfree(r->sink);
pa_xfree(r);
}
pa_xfree(r->type);
pa_xfree(r->sink);
pa_xfree(r);
}
static void module_free(struct module *m) {
struct device *dev;
struct mix_port *mix_port;
struct device_port *device_port;
struct route *route;
pa_assert(m);
while (m->attached_devices) {
SLLIST_STEAL_FIRST(dev, m->attached_devices);
device_free(dev);
};
while (m->default_output) {
SLLIST_STEAL_FIRST(dev, m->default_output);
device_free(dev);
};
while (m->mix_ports) {
SLLIST_STEAL_FIRST(mix_port, m->mix_ports);
mix_port_free(mix_port);
};
while (m->device_ports) {
SLLIST_STEAL_FIRST(device_port, m->device_ports);
device_port_free(device_port);
};
while (m->routes) {
SLLIST_STEAL_FIRST(route, m->routes);
route_free(route);
};
device_list_free(m->attached_devices);
device_list_free(m->default_output);
mix_port_list_free(m->mix_ports);
device_port_list_free(m->device_ports);
route_list_free(m->routes);
pa_xfree(m->name);
pa_xfree(m);
@ -679,7 +663,7 @@ done:
data->current_mix_port = p;
} else {
pa_log("[%s:%u] Failed to parse element <" ELEMENT_mixPort ">", data->fn, data->lineno);
mix_port_free(p);
mix_port_list_free(p);
}
return parsed;
@ -759,15 +743,15 @@ done:
if (!parsed) {
pa_log_error("[%s:%u] Failed to parse element <" ELEMENT_profile ">", data->fn, data->lineno);
profile_free(p);
profile_list_free(p);
} else if (unknown_format) {
pa_log_info("[%s:%u] Ignore profile with unknown format.", data->fn, data->lineno);
profile_free(p);
profile_list_free(p);
} else {
if (data->current_mix_port)
SLLIST_APPEND(struct profile, data->current_module->mix_ports->profiles, p);
SLLIST_APPEND(struct profile, data->current_mix_port->profiles, p);
else if (data->current_device_port)
SLLIST_APPEND(struct profile, data->current_module->device_ports->profiles, p);
SLLIST_APPEND(struct profile, data->current_device_port->profiles, p);
else
pa_assert_not_reached();
}
@ -802,10 +786,10 @@ done:
if (!parsed) {
pa_log("[%s:%u] Failed to parse element <" ELEMENT_devicePort ">", data->fn, data->lineno);
device_port_free(d);
device_port_list_free(d);
} else if (unknown_device) {
pa_log_info("[%s:%u] Ignore <" ELEMENT_devicePort "> with unknown device.", data->fn, data->lineno);
device_port_free(d);
device_port_list_free(d);
} else {
SLLIST_APPEND(struct device_port, data->current_module->device_ports, d);
data->current_device_port = d;
@ -844,7 +828,7 @@ done:
SLLIST_APPEND(struct route, data->current_module->routes, r);
} else {
pa_log("[%s:%u] Failed to parse element <" ELEMENT_route ">", data->fn, data->lineno);
route_free(r);
route_list_free(r);
}
return parsed;

View file

@ -117,6 +117,34 @@ static char *list_string(struct string_conversion *list, uint32_t flags) {
return str;
}
/* Generic conversion */
bool pa_string_convert_num_to_str(pa_conversion_string_t type, uint32_t value, const char **to_str) {
switch (type) {
case CONV_STRING_FORMAT:
return string_convert_num_to_str(string_conversion_table_format, value, to_str);
case CONV_STRING_OUTPUT_CHANNELS:
return string_convert_num_to_str(string_conversion_table_output_channels, value, to_str);
case CONV_STRING_INPUT_CHANNELS:
return string_convert_num_to_str(string_conversion_table_input_channels, value, to_str);
case CONV_STRING_OUTPUT_DEVICE:
return string_convert_num_to_str(string_conversion_table_output_device, value, to_str);
case CONV_STRING_INPUT_DEVICE:
return string_convert_num_to_str(string_conversion_table_input_device, value, to_str);
case CONV_STRING_OUTPUT_FLAG:
return string_convert_num_to_str(string_conversion_table_output_flag, value, to_str);
case CONV_STRING_INPUT_FLAG:
return string_convert_num_to_str(string_conversion_table_input_flag, value, to_str);
}
pa_assert_not_reached();
return false;
}
/* Output device */
bool pa_string_convert_output_device_num_to_str(audio_devices_t value, const char **to_str) {

View file

@ -199,6 +199,28 @@ const pa_droid_config_hw_module *pa_droid_config_find_module(const pa_droid_conf
return NULL;
}
static const pa_droid_config_device *find_device(const pa_droid_config_hw_module *module, bool output, const char* device_name) {
pa_droid_config_device *device;
pa_assert(module);
pa_assert(device_name);
SLLIST_FOREACH(device, output ? module->outputs : module->inputs) {
if (pa_streq(device_name, device->name))
return device;
}
return NULL;
}
const pa_droid_config_device *pa_droid_config_find_output(const pa_droid_config_hw_module *module, const char* output_name) {
return find_device(module, true, output_name);
}
const pa_droid_config_device *pa_droid_config_find_input(const pa_droid_config_hw_module *module, const char* input_name) {
return find_device(module, false, input_name);
}
pa_droid_config_hw_module *pa_droid_config_hw_module_new(const pa_droid_config_audio *config, const char *name) {
pa_droid_config_hw_module *hw_module;

View file

@ -80,10 +80,13 @@ struct droid_quirk valid_quirks[] = {
{ "output_make_writable", QUIRK_OUTPUT_MAKE_WRITABLE },
{ "realcall", QUIRK_REALCALL },
{ "unload_call_exit", QUIRK_UNLOAD_CALL_EXIT },
{ "output_fast", QUIRK_OUTPUT_FAST },
{ "output_deep_buffer", QUIRK_OUTPUT_DEEP_BUFFER },
};
#define DEFAULT_PRIORITY (100)
#define DEFAULT_PRIORITY (100)
#define DEFAULT_AUDIO_FORMAT (AUDIO_FORMAT_PCM_16_BIT)
static const char * const droid_combined_auto_outputs[3] = { "primary", "low_latency", NULL };
@ -91,8 +94,7 @@ static const char * const droid_combined_auto_inputs[2] = { "primary", NULL
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_hw_module *hw_module);
static int input_stream_set_route(pa_droid_hw_module *hw_module, pa_droid_stream *s);
static pa_droid_profile *profile_new(pa_droid_profile_set *ps,
const pa_droid_config_hw_module *module,
@ -259,8 +261,8 @@ static void add_default_profile(pa_droid_profile_set *ps,
pa_assert(ps);
pa_assert(module);
pa_assert(!primary_output || primary_output->direction == PA_DIRECTION_OUTPUT);
pa_assert(!low_latency_output || primary_output->direction == PA_DIRECTION_OUTPUT);
pa_assert(!media_latency_output || primary_output->direction == PA_DIRECTION_OUTPUT);
pa_assert(!low_latency_output || low_latency_output->direction == PA_DIRECTION_OUTPUT);
pa_assert(!media_latency_output || media_latency_output->direction == PA_DIRECTION_OUTPUT);
pa_log_debug("New default profile");
@ -709,8 +711,12 @@ static pa_droid_quirks *get_quirks(pa_droid_quirks *q) {
static pa_droid_quirks *set_default_quirks(pa_droid_quirks *q) {
q = NULL;
#if (ANDROID_VERSION_MAJOR >= 5) || defined(DROID_AUDIO_HAL_ATOI_FIX)
q = get_quirks(q);
q->enabled[QUIRK_CLOSE_INPUT] = true;
q->enabled[QUIRK_OUTPUT_FAST] = true;
q->enabled[QUIRK_OUTPUT_DEEP_BUFFER] = true;
#if (ANDROID_VERSION_MAJOR >= 5) || defined(DROID_AUDIO_HAL_ATOI_FIX)
q->enabled[QUIRK_INPUT_ATOI] = true;
#endif
@ -720,13 +726,9 @@ static pa_droid_quirks *set_default_quirks(pa_droid_quirks *q) {
defined(DROID_DEVICE_MANGO) || defined(DROID_DEVICE_SATSUMA) ||\
defined(DROID_DEVICE_SMULTRON) || defined(DROID_DEVICE_URUSHI)
#warning Using set_parameters hack, originating from previous cm10 mako.
q = get_quirks(q);
q->enabled[QUIRK_SET_PARAMETERS] = true;
#endif
q = get_quirks(q);
q->enabled[QUIRK_CLOSE_INPUT] = true;
return q;
}
@ -797,10 +799,10 @@ static void update_sink_types(pa_droid_hw_module *hw, pa_sink *ignore_sink) {
if (s->output->flags & AUDIO_OUTPUT_FLAG_PRIMARY)
primary_sink = sink;
if (s->output->flags & AUDIO_OUTPUT_FLAG_FAST)
if (pa_droid_quirk(hw, QUIRK_OUTPUT_FAST) && s->output->flags & AUDIO_OUTPUT_FLAG_FAST)
low_latency_sink = sink;
if (s->output->flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER)
if (pa_droid_quirk(hw, QUIRK_OUTPUT_DEEP_BUFFER) && s->output->flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER)
media_latency_sink = sink;
#if defined(HAVE_ENUM_AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)
@ -1044,13 +1046,15 @@ void pa_droid_hw_module_unlock(pa_droid_hw_module *hw) {
pa_mutex_unlock(hw->hw_mutex);
}
static pa_droid_stream *droid_stream_new(pa_droid_hw_module *module) {
static pa_droid_stream *droid_stream_new(pa_droid_hw_module *module,
const pa_droid_config_device *device_def) {
pa_droid_stream *s;
s = pa_xnew0(pa_droid_stream, 1);
PA_REFCNT_INIT(s);
s->module = pa_droid_hw_module_ref(module);
s->device_def = device_def;
return s;
}
@ -1060,10 +1064,37 @@ static pa_droid_output_stream *droid_output_stream_new(void) {
}
static pa_droid_input_stream *droid_input_stream_new(void) {
return pa_xnew0(pa_droid_input_stream, 1);
pa_droid_input_stream *input = pa_xnew0(pa_droid_input_stream, 1);
input->first = true;
return input;
}
static bool stream_config_fill(audio_devices_t devices,
static int stream_standby(pa_droid_stream *s) {
int ret = 0;
pa_assert(s);
pa_assert(s->output || s->input);
if ((s->output && !s->output->stream) ||
(s->input && !s->input->stream))
return ret;
if (s->output) {
pa_mutex_lock(s->module->output_mutex);
ret = s->output->stream->common.standby(&s->output->stream->common);
pa_mutex_unlock(s->module->output_mutex);
} else {
pa_mutex_lock(s->module->input_mutex);
ret = s->input->stream->common.standby(&s->input->stream->common);
pa_mutex_unlock(s->module->input_mutex);
}
return ret;
}
static bool stream_config_fill(const pa_droid_config_device *device_def,
audio_devices_t devices,
pa_sample_spec *sample_spec,
pa_channel_map *channel_map,
struct audio_config *config) {
@ -1071,6 +1102,7 @@ static bool stream_config_fill(audio_devices_t devices,
audio_channel_mask_t hal_channel_mask = 0;
bool voicecall_record = false;
bool output = true;
int i;
pa_assert(sample_spec);
pa_assert(channel_map);
@ -1085,15 +1117,12 @@ static bool stream_config_fill(audio_devices_t devices,
output = !(devices & AUDIO_DEVICE_IN_ALL);
#endif
if (devices & AUDIO_DEVICE_IN_VOICE_CALL)
if (devices & AUDIO_DEVICE_IN_VOICE_CALL) {
pa_log_debug("Fill config: Use 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;
}
for (int i = 0; i < channel_map->channels; i++) {
for (i = 0; i < channel_map->channels; i++) {
bool found;
audio_channel_mask_t c;
@ -1127,8 +1156,81 @@ static bool stream_config_fill(audio_devices_t devices,
sample_spec->channels = 1;
hal_channel_mask = AUDIO_CHANNEL_IN_MONO;
#endif
pa_log_debug("Fill config: Use %d channel(s) for voice call.", sample_spec->channels);
}
if (!pa_convert_format(sample_spec->format, CONV_FROM_PA, &hal_audio_format)) {
pa_log_warn("Sample spec format %u not supported.", sample_spec->format);
goto fail;
}
/* As input sample metrics are based on user request we need to make sure that the sample
* format requested is actually usable with the input route. */
if (!output) {
const pa_droid_config_device *ddef;
uint32_t tmp;
const char *fmt;
uint32_t format_found = 0;
pa_log_debug("Fill config: Try to use format %s, sample rate %u",
pa_string_convert_num_to_str(CONV_STRING_FORMAT, hal_audio_format, &fmt) ? fmt : "<unknown>",
sample_spec->rate);
SLLIST_FOREACH(ddef, device_def) {
format_found |= ddef->formats & hal_audio_format;
if (!(ddef->formats & hal_audio_format) ||
!(ddef->devices & devices))
continue;
for (i = 0; i < AUDIO_MAX_SAMPLING_RATES && ddef->sampling_rates[i]; i++) {
if (ddef->sampling_rates[i] == (uint32_t) -1 ||
ddef->sampling_rates[i] == sample_spec->rate) {
goto format_found;
}
}
}
if (!format_found && hal_audio_format != DEFAULT_AUDIO_FORMAT) {
hal_audio_format = DEFAULT_AUDIO_FORMAT;
pa_assert_se(pa_convert_format(hal_audio_format, CONV_FROM_HAL, &tmp));
sample_spec->format = tmp;
pa_log_debug("Fill config: Override sample format, use default %s.",
pa_string_convert_num_to_str(CONV_STRING_FORMAT, hal_audio_format, &fmt) ? fmt : "<unknown>");
}
SLLIST_FOREACH(ddef, device_def) {
if (!(ddef->devices & devices) ||
!ddef->sampling_rates[0])
continue;
if (ddef->sampling_rates[0] == (uint32_t) -1)
goto format_found;
for (i = 0; i < AUDIO_MAX_SAMPLING_RATES && ddef->sampling_rates[i]; i++) {
if (ddef->sampling_rates[i] == sample_spec->rate)
goto format_found;
}
/* Start from highest sample rate value */
for (i = 0; i < AUDIO_MAX_SAMPLING_RATES && ddef->sampling_rates[i]; i++)
;
for (i -= 1; i >= 0; i--) {
if ((ddef->sampling_rates[i] % 11025 == 0 && sample_spec->rate % 11025 == 0) ||
(ddef->sampling_rates[i] % 4000 == 0 && sample_spec->rate % 4000 == 0)) {
sample_spec->rate = ddef->sampling_rates[i];
pa_log_debug("Fill config: Override sample rate, use %u.", sample_spec->rate);
goto format_found;
}
}
}
/* Format not found, fail */
goto fail;
}
format_found:
memset(config, 0, sizeof(*config));
config->sample_rate = sample_spec->rate;
config->channel_mask = hal_channel_mask;
@ -1143,8 +1245,9 @@ fail:
pa_droid_stream *pa_droid_open_output_stream(pa_droid_hw_module *module,
const pa_sample_spec *spec,
const pa_channel_map *map,
audio_output_flags_t flags,
const char *module_output_name,
audio_devices_t devices) {
const pa_droid_config_device *module_output = NULL;
pa_droid_stream *s = NULL;
pa_droid_output_stream *output = NULL;
pa_droid_stream *primary_stream = NULL;
@ -1157,16 +1260,22 @@ pa_droid_stream *pa_droid_open_output_stream(pa_droid_hw_module *module,
pa_assert(module);
pa_assert(spec);
pa_assert(map);
pa_assert(module_output_name);
sample_spec = *spec;
channel_map = *map;
if (!stream_config_fill(devices, &sample_spec, &channel_map, &config_out))
if (!(module_output = pa_droid_config_find_output(module->enabled_module, module_output_name))) {
pa_log("Could not find output %s from module %s.", module_output_name, module->enabled_module->name);
goto fail;
}
if (!stream_config_fill(module_output, devices, &sample_spec, &channel_map, &config_out))
goto fail;
if (pa_idxset_size(module->outputs) == 0)
pa_log_debug("Set initial output device to %#010x", devices);
else if ((primary_stream = get_primary_output(module))) {
else if ((primary_stream = pa_droid_hw_primary_output_stream(module))) {
pa_log_debug("Primary output with device %#010x already open, using as initial device.",
primary_stream->output->device);
devices = primary_stream->output->device;
@ -1176,7 +1285,7 @@ pa_droid_stream *pa_droid_open_output_stream(pa_droid_hw_module *module,
ret = module->device->open_output_stream(module->device,
++module->stream_out_id,
devices,
flags,
module_output->flags,
&config_out,
&stream
#if AUDIO_API_VERSION_MAJ >= 3
@ -1192,12 +1301,12 @@ pa_droid_stream *pa_droid_open_output_stream(pa_droid_hw_module *module,
goto fail;
}
s = droid_stream_new(module);
s = droid_stream_new(module, module_output);
s->output = output = droid_output_stream_new();
output->stream = stream;
output->sample_spec = *spec;
output->channel_map = *map;
output->flags = flags;
output->flags = module_output->flags;
output->device = devices;
if ((output->sample_spec.rate = output->stream->common.get_sample_rate(&output->stream->common)) != spec->rate)
@ -1356,6 +1465,7 @@ static int input_stream_open(pa_droid_stream *s, bool resume_from_suspend) {
pa_channel_map channel_map;
pa_sample_spec sample_spec;
size_t buffer_size;
bool try_defaults = true;
int ret = -1;
struct audio_config config_try;
@ -1381,7 +1491,7 @@ static int input_stream_open(pa_droid_stream *s, bool resume_from_suspend) {
sample_spec = input->req_sample_spec;
channel_map = input->req_channel_map;
if (!stream_config_fill(hw_module->state.input_device, &sample_spec, &channel_map, &config_try))
if (!stream_config_fill(s->device_def, hw_module->state.input_device, &sample_spec, &channel_map, &config_try))
goto done;
pa_droid_hw_module_lock(s->module);
@ -1422,14 +1532,24 @@ static int input_stream_open(pa_droid_stream *s, bool resume_from_suspend) {
if (!stream_config_convert(PA_DIRECTION_INPUT, &config_in, &sample_spec, &channel_map)) {
pa_log_warn("Failed to update PulseAudio structures from received config values.");
break;
goto open_done;
}
config_try = config_in;
continue;
} else if (try_defaults) {
pa_log_info("Could not open input stream, trying with defaults.");
sample_spec = input->default_sample_spec;
channel_map = input->default_channel_map;
if (!stream_config_fill(s->device_def, hw_module->state.input_device, &sample_spec, &channel_map, &config_try))
goto done;
try_defaults = false;
continue;
} else {
pa_log_warn("Could not open input stream and no suggested changes received, bailing out.");
break;
goto open_done;
}
} else if (config_diff(&config_in, &config_try, &diff_sample_rate, &diff_channel_mask, &diff_format)) {
pa_log_info("Opened input stream, but differences in%s%s%s",
@ -1445,8 +1565,9 @@ static int input_stream_open(pa_droid_stream *s, bool resume_from_suspend) {
}
}
break;
goto open_done;
}
open_done:
pa_droid_hw_module_unlock(s->module);
if (ret < 0 || !input->stream) {
@ -1469,13 +1590,17 @@ static int input_stream_open(pa_droid_stream *s, bool resume_from_suspend) {
&config_in,
ret);
input->sample_spec = sample_spec;
input->channel_map = channel_map;
input->req_sample_spec = input->sample_spec = sample_spec;
input->req_channel_map = input->channel_map = channel_map;
buffer_size = input->stream->common.get_buffer_size(&input->stream->common);
s->buffer_size = buffer_size;
/* Set input stream to standby */
s->input->stream->common.standby(&s->input->stream->common);
stream_standby(s);
/* As audio_source_t may not have any effect when opening the input stream
* set input parameters immediately after opening the stream. */
input_stream_set_route(s->module, s);
pa_log_debug("Opened input stream %p", (void *) s);
set_active_input(s->module, s);
@ -1487,47 +1612,73 @@ done:
static void input_stream_close(pa_droid_stream *s) {
pa_assert(s);
pa_assert(s->input);
pa_assert(s->input->stream);
if (!s->input->stream)
return;
pa_mutex_lock(s->module->input_mutex);
set_active_input(s->module, NULL);
s->input->stream->common.standby(&s->input->stream->common);
s->module->device->close_input_stream(s->module->device, s->input->stream);
s->input->stream = NULL;
pa_log_debug("Closed input stream %p", (void *) s);
pa_mutex_unlock(s->module->input_mutex);
}
bool pa_droid_stream_reconfigure_input(pa_droid_stream *s,
const pa_sample_spec *requested_sample_spec,
const pa_channel_map *requested_channel_map) {
pa_assert(s);
pa_assert(s->input);
pa_assert(requested_sample_spec);
pa_assert(requested_channel_map);
/* Copy our requested specs, so we know them when resuming from suspend
* as well. */
s->input->req_sample_spec = *requested_sample_spec;
s->input->req_channel_map = *requested_channel_map;
input_stream_close(s);
if (input_stream_open(s, false) < 0) {
if (!s->input->first) {
pa_log_debug("Input stream reconfigure failed, restore default values.");
s->input->req_sample_spec = s->input->default_sample_spec;
s->input->req_channel_map = s->input->default_channel_map;
input_stream_open(s, false);
}
return false;
}
return true;
}
pa_droid_stream *pa_droid_open_input_stream(pa_droid_hw_module *hw_module,
const pa_sample_spec *requested_sample_spec,
const pa_channel_map *requested_channel_map) {
const pa_sample_spec *default_sample_spec,
const pa_channel_map *default_channel_map) {
pa_droid_stream *s = NULL;
pa_droid_input_stream *input = NULL;
pa_assert(hw_module);
pa_assert(requested_sample_spec);
pa_assert(requested_channel_map);
pa_assert(default_sample_spec);
pa_assert(default_channel_map);
if (hw_module->state.active_input) {
pa_log_warn("Opening input stream while there is already active input stream.");
return pa_droid_stream_ref(hw_module->state.active_input);
pa_log("Opening input stream while there is already active input stream.");
return NULL;
}
s = droid_stream_new(hw_module);
s = droid_stream_new(hw_module, hw_module->enabled_module->inputs);
s->input = input = droid_input_stream_new();
s->input->default_sample_spec = *default_sample_spec;
s->input->default_channel_map = *default_channel_map;
/* Copy our requested specs, so we know them when resuming from suspend
* as well. */
input->req_sample_spec = *requested_sample_spec;
input->req_channel_map = *requested_channel_map;
if (input_stream_open(s, false) < 0) {
if (!pa_droid_stream_reconfigure_input(s, default_sample_spec, default_channel_map)) {
pa_droid_stream_unref(s);
s = NULL;
}
} else
s->input->first = false;
return s;
}
@ -1557,14 +1708,8 @@ void pa_droid_stream_unref(pa_droid_stream *s) {
pa_xfree(s->output);
} else {
pa_log_debug("Destroy input stream %p", (void *) s);
set_active_input(s->module, NULL);
pa_mutex_lock(s->module->input_mutex);
pa_idxset_remove_by_data(s->module->inputs, s, NULL);
if (s->input->stream) {
s->input->stream->common.standby(&s->input->stream->common);
s->module->device->close_input_stream(s->module->device, s->input->stream);
}
pa_mutex_unlock(s->module->input_mutex);
input_stream_close(s);
pa_xfree(s->input);
}
@ -1573,7 +1718,7 @@ void pa_droid_stream_unref(pa_droid_stream *s) {
pa_xfree(s);
}
static pa_droid_stream *get_primary_output(pa_droid_hw_module *hw) {
pa_droid_stream *pa_droid_hw_primary_output_stream(pa_droid_hw_module *hw) {
pa_droid_stream *s;
uint32_t idx;
@ -1604,7 +1749,7 @@ static int droid_output_stream_set_route(pa_droid_stream *s, audio_devices_t dev
pa_mutex_lock(s->module->output_mutex);
if (output->flags & AUDIO_OUTPUT_FLAG_PRIMARY || get_primary_output(s->module) == NULL) {
if (output->flags & AUDIO_OUTPUT_FLAG_PRIMARY || pa_droid_hw_primary_output_stream(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);
@ -1648,8 +1793,7 @@ static int droid_output_stream_set_route(pa_droid_stream *s, audio_devices_t dev
return ret;
}
static int input_stream_set_route(pa_droid_hw_module *hw_module) {
pa_droid_stream *s;
static int input_stream_set_route(pa_droid_hw_module *hw_module, pa_droid_stream *s) {
pa_droid_input_stream *input;
audio_devices_t device;
audio_source_t source;
@ -1658,8 +1802,11 @@ static int input_stream_set_route(pa_droid_hw_module *hw_module) {
pa_assert(hw_module);
if (!s)
s = hw_module->state.active_input;
/* No active input, no need for set parameters */
if (!(s = hw_module->state.active_input))
if (!s)
goto done;
input = s->input;
@ -1682,7 +1829,7 @@ static int input_stream_set_route(pa_droid_hw_module *hw_module) {
AUDIO_PARAMETER_STREAM_INPUT_SOURCE, source);
pa_log_debug("input stream %p set_parameters(%s) %#010x ; %#010x",
(void *) input, parameters, device, source);
(void *) s, parameters, device, source);
if (pa_droid_quirk(hw_module, QUIRK_SET_PARAMETERS)) {
pa_mutex_lock(hw_module->hw_mutex);
@ -1714,7 +1861,7 @@ int pa_droid_stream_set_route(pa_droid_stream *s, audio_devices_t device) {
return droid_output_stream_set_route(s, device);
else {
pa_droid_hw_set_input_device(s->module, device);
return input_stream_set_route(s->module);
return input_stream_set_route(s->module, NULL);
}
}
@ -1782,18 +1929,17 @@ int pa_droid_stream_suspend(pa_droid_stream *s, bool suspend) {
if (s->output) {
if (suspend) {
pa_atomic_dec(&s->module->active_outputs);
return s->output->stream->common.standby(&s->output->stream->common);
return stream_standby(s);
} else {
pa_atomic_inc(&s->module->active_outputs);
}
} else {
if (suspend) {
if (s->input->stream) {
if (pa_droid_quirk(s->module, QUIRK_CLOSE_INPUT)) {
s->input->stream->common.standby(&s->input->stream->common);
if (pa_droid_quirk(s->module, QUIRK_CLOSE_INPUT))
input_stream_close(s);
} else
return s->input->stream->common.standby(&s->input->stream->common);
else
return stream_standby(s);
}
} else if (pa_droid_quirk(s->module, QUIRK_CLOSE_INPUT))
return input_stream_open(s, true);
@ -1934,12 +2080,15 @@ bool pa_droid_hw_set_input_device(pa_droid_hw_module *hw_module,
audio_source_t audio_source_override = AUDIO_SOURCE_DEFAULT;
bool device_changed = false;
bool source_changed = false;
const char *audio_source_name;
pa_assert(hw_module);
if (hw_module->state.input_device != device) {
pa_log_debug("Set global input to %#010x", device);
const char *name = NULL;
pa_log_debug("Set global input to %s (%#010x)",
pa_string_convert_input_device_num_to_str(device, &name)
? name : "<unknown>",
device);
hw_module->state.input_device = device;
device_changed = true;
}
@ -1971,16 +2120,17 @@ bool pa_droid_hw_set_input_device(pa_droid_hw_module *hw_module,
}
if (audio_source != hw_module->state.audio_source) {
const char *name = NULL;
pa_log_debug("set global audio source to %s (%#010x)",
pa_droid_audio_source_name(audio_source, &audio_source_name)
? audio_source_name : "<unknown>",
pa_droid_audio_source_name(audio_source, &name)
? name : "<unknown>",
audio_source);
hw_module->state.audio_source = audio_source;
source_changed = true;
}
if (hw_module->state.active_input && (device_changed || source_changed))
input_stream_set_route(hw_module);
input_stream_set_route(hw_module, NULL);
return true;
}

View file

@ -55,6 +55,8 @@ typedef enum {
CONV_STRING_INPUT_FLAG
} pa_conversion_string_t;
bool pa_string_convert_num_to_str(pa_conversion_string_t type, uint32_t value, const char **to_str);
bool pa_convert_output_channel(uint32_t value, pa_conversion_field_t from, uint32_t *to_value);
bool pa_convert_input_channel(uint32_t value, pa_conversion_field_t from, uint32_t *to_value);
bool pa_convert_format(uint32_t value, pa_conversion_field_t from, uint32_t *to_value);

View file

@ -91,6 +91,8 @@ pa_droid_config_audio *pa_parse_droid_audio_config_xml(const char *filename);
pa_droid_config_audio *pa_parse_droid_audio_config(const char *filename);
const pa_droid_config_hw_module *pa_droid_config_find_module(const pa_droid_config_audio *config, const char* module_id);
const pa_droid_config_device *pa_droid_config_find_output(const pa_droid_config_hw_module *module, const char* output_name);
const pa_droid_config_device *pa_droid_config_find_input(const pa_droid_config_hw_module *module, const char* input_name);
pa_droid_config_hw_module *pa_droid_config_hw_module_new(const pa_droid_config_audio *config, const char *name);
void pa_droid_config_hw_module_free(pa_droid_config_hw_module *hw_module);

View file

@ -116,18 +116,22 @@ struct pa_droid_output_stream {
struct pa_droid_input_stream {
struct audio_stream_in *stream;
pa_sample_spec default_sample_spec;
pa_channel_map default_channel_map;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_sample_spec req_sample_spec;
pa_channel_map req_channel_map;
uint32_t flags;
uint32_t device;
bool first;
};
struct pa_droid_stream {
PA_REFCNT_DECLARE;
pa_droid_hw_module *module;
const pa_droid_config_device *device_def;
size_t buffer_size;
void *data;
@ -222,6 +226,8 @@ enum pa_droid_quirk_type {
QUIRK_OUTPUT_MAKE_WRITABLE,
QUIRK_REALCALL,
QUIRK_UNLOAD_CALL_EXIT,
QUIRK_OUTPUT_FAST,
QUIRK_OUTPUT_DEEP_BUFFER,
QUIRK_COUNT
};
@ -275,6 +281,7 @@ void pa_droid_add_card_ports(pa_card_profile *cp, pa_hashmap *ports, pa_droid_ma
/* Module operations */
int pa_droid_set_parameters(pa_droid_hw_module *hw, const char *parameters);
pa_droid_stream *pa_droid_hw_primary_output_stream(pa_droid_hw_module *hw);
/* Stream operations */
pa_droid_stream *pa_droid_stream_ref(pa_droid_stream *s);
@ -286,7 +293,7 @@ int pa_droid_stream_set_parameters(pa_droid_stream *s, const char *parameters);
pa_droid_stream *pa_droid_open_output_stream(pa_droid_hw_module *module,
const pa_sample_spec *spec,
const pa_channel_map *map,
audio_output_flags_t flags,
const char *module_output_name,
audio_devices_t devices);
/* Set routing to the input or output stream, with following side-effects:
@ -303,8 +310,11 @@ int pa_droid_stream_set_route(pa_droid_stream *s, audio_devices_t device);
/* Open input stream with currently active routing, sample_spec and channel_map
* are requests and may change when opening the stream. */
pa_droid_stream *pa_droid_open_input_stream(pa_droid_hw_module *hw_module,
const pa_sample_spec *requested_sample_spec,
const pa_channel_map *requested_channel_map);
const pa_sample_spec *default_sample_spec,
const pa_channel_map *default_channel_map);
bool pa_droid_stream_reconfigure_input(pa_droid_stream *s,
const pa_sample_spec *requested_sample_spec,
const pa_channel_map *requested_channel_map);
bool pa_droid_hw_set_input_device(pa_droid_hw_module *hw_module,
audio_devices_t device);

View file

@ -1046,6 +1046,7 @@ pa_sink *pa_droid_sink_new(pa_module *m,
pa_card *card) {
struct userdata *u = NULL;
const pa_droid_config_device *output = NULL;
bool deferred_volume = false;
char *thread_name = NULL;
pa_sink_new_data data;
@ -1068,15 +1069,18 @@ pa_sink *pa_droid_sink_new(pa_module *m,
pa_assert(ma);
pa_assert(driver);
pa_log_info("Create new droid-sink");
deferred_volume = m->core->deferred_volume;
if (pa_modargs_get_value_boolean(ma, "deferred_volume", &deferred_volume) < 0) {
pa_log("Failed to parse deferred_volume argument.");
goto fail;
}
if (card && am)
module_id = am->output->module->name;
else
if (card && am) {
output = am->output;
module_id = output->module->name;
} else
module_id = pa_modargs_get_value(ma, "module_id", DEFAULT_MODULE_ID);
sample_spec = m->core->default_sample_spec;
@ -1150,6 +1154,18 @@ pa_sink *pa_droid_sink_new(pa_module *m,
pa_assert(card);
pa_assert_se((u->hw_module = pa_droid_hw_module_get(u->core, NULL, card_data->module_id)));
} else {
const char *output_name;
if (!(output_name = pa_modargs_get_value(ma, "output", NULL))) {
pa_log("No output name defined.");
goto fail;
}
if (!(output = pa_droid_config_find_output(u->hw_module->enabled_module, output_name))) {
pa_log("Could not find output %s from module %s.", output_name, u->hw_module->enabled_module->name);
goto fail;
}
/* Sink wasn't created from inside card module, so we'll need to open
* hw module ourself.
*
@ -1169,8 +1185,8 @@ pa_sink *pa_droid_sink_new(pa_module *m,
}
/* Default routing */
dev_out = (am && am->output->module->global_config) ? am->output->module->global_config->default_output_device
: u->hw_module->config->global_config->default_output_device;
dev_out = output->module->global_config ? output->module->global_config->default_output_device
: u->hw_module->config->global_config->default_output_device;
if ((tmp = pa_modargs_get_value(ma, "output_devices", NULL))) {
audio_devices_t tmp_dev;
@ -1181,10 +1197,9 @@ pa_sink *pa_droid_sink_new(pa_module *m,
pa_log_debug("Set initial devices %s", tmp);
}
if (am)
flags = am->output->flags;
flags = output->flags;
u->stream = pa_droid_open_output_stream(u->hw_module, &sample_spec, &channel_map, flags, dev_out);
u->stream = pa_droid_open_output_stream(u->hw_module, &sample_spec, &channel_map, output->name, dev_out);
if (!u->stream) {
pa_log("Failed to open output stream.");
@ -1199,7 +1214,7 @@ pa_sink *pa_droid_sink_new(pa_module *m,
pa_log_info("Using buffer size %u.", u->buffer_size);
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, output->name)) {
pa_log("Failed to parse prewrite_on_resume (%s)", prewrite_resume);
goto fail;
}
@ -1216,10 +1231,7 @@ pa_sink *pa_droid_sink_new(pa_module *m,
data.module = m;
data.card = card;
if (am)
set_sink_name(ma, &data, am->output->name);
else
set_sink_name(ma, &data, module_id);
set_sink_name(ma, &data, output->name);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "sound");
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, PROP_DROID_API_STRING);
@ -1285,10 +1297,7 @@ pa_sink *pa_droid_sink_new(pa_module *m,
/* Rewind internal memblockq */
pa_sink_set_max_rewind(u->sink, 0);
if (am)
thread_name = pa_sprintf_malloc("droid-sink-%s", am->output->name);
else
thread_name = pa_sprintf_malloc("droid-sink-%s", module_id);
thread_name = pa_sprintf_malloc("droid-sink-%s", output->name);
if (!(u->thread = pa_thread_new(thread_name, thread_func, u))) {
pa_log("Failed to create thread.");
goto fail;

View file

@ -82,10 +82,6 @@ struct userdata {
bool stream_valid;
};
enum {
SOURCE_MESSAGE_DO_ROUTING = PA_SOURCE_MESSAGE_MAX
};
#define DEFAULT_MODULE_ID "primary"
#define DROID_AUDIO_SOURCE "droid.audio_source"
@ -94,6 +90,10 @@ enum {
static void userdata_free(struct userdata *u);
static int suspend(struct userdata *u);
static void unsuspend(struct userdata *u);
static void source_reconfigure(struct userdata *u,
const pa_sample_spec *reconfigure_sample_spec,
const pa_channel_map *reconfigure_channel_map,
audio_devices_t update_device);
/* Our droid source may be left in a state of not having an input stream
* if reconfiguration fails and fallback to previously active values fails
@ -326,28 +326,17 @@ static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_
/* Called from IO context */
static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
#if PULSEAUDIO_VERSION < 12
struct userdata *u = PA_SOURCE(o)->userdata;
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));
suspend(u);
do_routing(u, device);
unsuspend(u);
break;
}
#if PULSEAUDIO_VERSION < 12
case PA_SOURCE_MESSAGE_SET_STATE: {
int r;
if ((r = source_set_state_in_io_thread_cb(u->source, PA_PTR_TO_UINT(data), 0)) < 0)
return r;
}
#endif
}
#endif
return pa_source_process_msg(o, code, data, offset, chunk);
}
@ -373,9 +362,8 @@ static int source_set_port_cb(pa_source *s, pa_device_port *p) {
if (!PA_SOURCE_IS_OPENED(u->source->state))
do_routing(u, data->device);
else {
pa_asyncmsgq_post(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_DO_ROUTING, PA_UINT_TO_PTR(data->device), 0, NULL, NULL);
}
else
source_reconfigure(u, NULL, NULL, data->device);
return 0;
}
@ -454,23 +442,16 @@ static void update_latency(struct userdata *u) {
pa_log_debug("Set fixed latency %" PRIu64 " usec", pa_bytes_to_usec(u->buffer_size, pa_droid_stream_sample_spec(u->stream)));
}
static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
static void source_reconfigure(struct userdata *u,
const pa_sample_spec *reconfigure_sample_spec,
const pa_channel_map *reconfigure_channel_map,
audio_devices_t update_device) {
pa_channel_map old_channel_map;
pa_sample_spec old_sample_spec;
pa_channel_map new_channel_map;
pa_sample_spec new_sample_spec;
pa_queue *source_outputs = NULL;
/* Not meant for us */
if (new_data->source != u->source)
return PA_HOOK_OK;
if (pa_sample_spec_equal(&new_data->sample_spec, pa_droid_stream_sample_spec(u->stream)) &&
pa_channel_map_equal(&new_data->channel_map, pa_droid_stream_channel_map(u->stream)))
return PA_HOOK_OK;
pa_log_info("New source-output connecting and our source needs to be reconfigured.");
if (pa_source_used_by(u->source)) {
/* If we already have connected source outputs detach those
* so that when re-attaching them to our source resampling etc.
@ -482,25 +463,26 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
old_channel_map = *pa_droid_stream_channel_map(u->stream);
old_sample_spec = *pa_droid_stream_sample_spec(u->stream);
new_channel_map = new_data->channel_map;
new_sample_spec = new_data->sample_spec;
new_channel_map = reconfigure_channel_map ? *reconfigure_channel_map : old_channel_map;
new_sample_spec = reconfigure_sample_spec ? *reconfigure_sample_spec : old_sample_spec;
pa_droid_stream_unref(u->stream);
if (!(u->stream = pa_droid_open_input_stream(u->hw_module, &new_sample_spec, &new_channel_map)))
u->stream = pa_droid_open_input_stream(u->hw_module, &old_sample_spec, &old_channel_map);
if (update_device)
do_routing(u, update_device);
if (u->stream) {
/* We need to be really careful here as we are modifying
* quite profound internal structures. */
new_sample_spec = *pa_droid_stream_sample_spec(u->stream);
new_channel_map = *pa_droid_stream_channel_map(u->stream);
u->source->channel_map = new_channel_map;
u->source->sample_spec = new_sample_spec;
pa_assert_se(pa_cvolume_remap(&u->source->reference_volume, &old_channel_map, &new_channel_map));
pa_assert_se(pa_cvolume_remap(&u->source->real_volume, &old_channel_map, &new_channel_map));
pa_assert_se(pa_cvolume_remap(&u->source->soft_volume, &old_channel_map, &new_channel_map));
if (pa_droid_stream_reconfigure_input(u->stream, &new_sample_spec, &new_channel_map))
pa_log_info("Source reconfigured.");
}
else
pa_log_info("Failed to reconfigure input stream, no worries, using defaults.");
/* We need to be really careful here as we are modifying
* quite profound internal structures. */
new_sample_spec = *pa_droid_stream_sample_spec(u->stream);
new_channel_map = *pa_droid_stream_channel_map(u->stream);
u->source->channel_map = new_channel_map;
u->source->sample_spec = new_sample_spec;
pa_assert_se(pa_cvolume_remap(&u->source->reference_volume, &old_channel_map, &new_channel_map));
pa_assert_se(pa_cvolume_remap(&u->source->real_volume, &old_channel_map, &new_channel_map));
pa_assert_se(pa_cvolume_remap(&u->source->soft_volume, &old_channel_map, &new_channel_map));
update_latency(u);
pa_source_suspend(u->source, false, PA_SUSPEND_UNAVAILABLE);
@ -508,7 +490,68 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
if (source_outputs && u->source) {
pa_source_move_all_finish(u->source, source_outputs, false);
}
}
static pa_hook_result_t source_output_new_hook_callback(void *hook_data,
void *call_data,
void *slot_data) {
pa_source_output_new_data *new_data = call_data;
struct userdata *u = slot_data;
pa_droid_stream *primary_output;
/* Not meant for us */
if (new_data->source != u->source)
return PA_HOOK_OK;
if (pa_sample_spec_equal(&new_data->sample_spec, pa_droid_stream_sample_spec(u->stream)) &&
pa_channel_map_equal(&new_data->channel_map, pa_droid_stream_channel_map(u->stream)))
return PA_HOOK_OK;
pa_log_info("New source-output connecting and our source needs to be reconfigured.");
/* Workaround for fm-radio loopback */
if (pa_safe_streq(pa_proplist_gets(new_data->proplist, "media.name"), "fmradio-loopback-source") &&
(primary_output = pa_droid_hw_primary_output_stream(u->hw_module))) {
pa_log_debug("Workaround for fm-radio loopback.");
source_reconfigure(u,
pa_droid_stream_sample_spec(primary_output),
pa_droid_stream_channel_map(primary_output),
0);
} else
source_reconfigure(u, &new_data->sample_spec, &new_data->channel_map, 0);
return PA_HOOK_OK;
}
static void source_reconfigure_after_changes(struct userdata *u) {
pa_source_output *so = NULL;
pa_source_output *so_i;
void *state = NULL;
if (!pa_source_used_by(u->source))
return;
/* Find last inserted source-output */
so = pa_idxset_iterate(u->source->outputs, &state, NULL);
if (so) {
while ((so_i = pa_idxset_iterate(u->source->outputs, &state, NULL)))
so = so_i;
}
if (so) {
if (!pa_sample_spec_equal(&so->sample_spec, pa_droid_stream_sample_spec(u->stream)) ||
!pa_channel_map_equal(&so->channel_map, pa_droid_stream_channel_map(u->stream))) {
pa_log_info("Source-output disconnected and our source needs to be reconfigured.");
source_reconfigure(u, &so->sample_spec, &so->channel_map, 0);
}
}
}
static pa_hook_result_t source_output_unlink_post_hook_callback(void *hook_data,
void *call_data,
void *slot_data) {
source_reconfigure_after_changes(slot_data);
return PA_HOOK_OK;
}
@ -538,6 +581,8 @@ pa_source *pa_droid_source_new(pa_module *m,
pa_assert(ma);
pa_assert(driver);
pa_log_info("Create new droid-source");
/* When running under card use hw module name for source by default. */
if (am)
module_id = am->inputs->module->name;
@ -653,6 +698,7 @@ pa_source *pa_droid_source_new(pa_module *m,
data.driver = driver;
data.module = m;
data.card = card;
/* Start suspended */
data.suspend_cause = PA_SUSPEND_IDLE;
if (am)
@ -722,11 +768,22 @@ pa_source *pa_droid_source_new(pa_module *m,
if (u->source->active_port)
source_set_port_cb(u->source, u->source->active_port);
/* Since we started in suspended mode suspend our stream immediately as well. */
pa_droid_stream_suspend(u->stream, true);
pa_droid_stream_set_data(u->stream, u->source);
pa_source_put(u->source);
/* As late as possible */
pa_module_hook_connect(u->module, &u->module->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_LATE * 2, (pa_hook_cb_t) source_output_new_hook_callback, u);
pa_module_hook_connect(u->module,
&u->module->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW],
PA_HOOK_LATE * 2,
source_output_new_hook_callback, u);
pa_module_hook_connect(u->module,
&u->module->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST],
PA_HOOK_LATE * 2,
source_output_unlink_post_hook_callback, u);
return u->source;

View file

@ -300,6 +300,19 @@ static void set_card_name(pa_modargs *ma, pa_card_new_data *data, const char *mo
data->namereg_fail = false;
}
static bool output_enabled(struct userdata *u, pa_droid_mapping *am) {
pa_assert(u);
pa_assert(am);
if (!pa_droid_quirk(u->hw_module, QUIRK_OUTPUT_FAST) && am->output->flags & AUDIO_OUTPUT_FLAG_FAST)
return false;
if (!pa_droid_quirk(u->hw_module, QUIRK_OUTPUT_DEEP_BUFFER) && am->output->flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER)
return false;
return true;
}
static void add_profile(struct userdata *u, pa_hashmap *h, pa_hashmap *ports, pa_droid_profile *ap) {
pa_card_profile *cp;
struct profile_data *d;
@ -320,6 +333,9 @@ static void add_profile(struct userdata *u, pa_hashmap *h, pa_hashmap *ports, pa
max_channels = 0;
PA_IDXSET_FOREACH(am, ap->output_mappings, idx) {
if (!output_enabled(u, am))
continue;
cp->n_sinks++;
pa_droid_add_card_ports(cp, ports, am, u->core);
max_channels = popcount(am->output->channel_masks) > max_channels
@ -373,6 +389,9 @@ static void init_profile(struct userdata *u) {
if (d->droid_profile && pa_idxset_size(d->droid_profile->output_mappings) > 0) {
PA_IDXSET_FOREACH(am, d->droid_profile->output_mappings, idx) {
if (!output_enabled(u, am))
continue;
am->sink = pa_droid_sink_new(u->module, u->modargs, __FILE__, &u->card_data, 0, am, u->card);
}
}
@ -661,6 +680,9 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
if (next->droid_profile && pa_idxset_size(next->droid_profile->output_mappings) > 0) {
PA_IDXSET_FOREACH(am, next->droid_profile->output_mappings, idx) {
if (!output_enabled(u, am))
continue;
if (!am->sink)
am->sink = pa_droid_sink_new(u->module, u->modargs, __FILE__, &u->card_data, 0, am, u->card);
@ -711,6 +733,8 @@ int pa__init(pa_module *m) {
pa_assert(m);
pa_log_info("Create new droid-card");
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments.");
goto fail;

View file

@ -60,6 +60,7 @@ static const char* const valid_modargs[] = {
"sink_channel_map",
"sink_mix_route",
"flags",
"output",
"output_devices",
"sink_name",
"module_id",