commit
25abbb4388
12 changed files with 519 additions and 221 deletions
9
README
9
README
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ static const char* const valid_modargs[] = {
|
|||
"sink_channel_map",
|
||||
"sink_mix_route",
|
||||
"flags",
|
||||
"output",
|
||||
"output_devices",
|
||||
"sink_name",
|
||||
"module_id",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue