Merge pull request #92 from jusa/input

Rewrite input stream handling.
This commit is contained in:
Juho Hämäläinen 2019-10-08 14:22:21 +03:00 committed by GitHub
commit 772b9ca3f1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 695 additions and 641 deletions

104
README
View file

@ -98,8 +98,7 @@ default profile
When module-droid-card is loaded with default arguments, droid-card will try
to create a default profile (called surprisingly "default"). The default
profile will try to merge useful output and input streams to one profile,
to allow use of possible low latency outputs or multiple inputs if the
input streams are split to multiple devices.
to allow use of possible low latency or deep buffer outputs.
For example configuration with
@ -110,8 +109,8 @@ For example configuration with
deep_buffer {}
}
inputs {
builtin {}
external {}
primary {}
voice_rx {}
}
}
other {
@ -120,39 +119,11 @@ For example configuration with
}
The default profile would contain two sinks, sink.primary and sink.deep_buffer
and one source, source.builtin_external. Then this combined source would use
either "builtin" or "external" input stream, depending on which one has the
input route currently in use (for example, input-wired_headset from "external"
and input-back_mic from "builtin" input stream).
and one source, source.droid.
Usually this default profile is everything that is needed in normal use, and
additional profiles created should be needed only for testing things out etc.
additional profiles
-------------------
In addition to the default profile all input and output definitions are
translated to PulseAudio card profiles. For example configuration with
audio_hw_modules {
primary {
outputs {
primary {}
lpa {}
}
inputs {
primary {}
}
}
other {
...
}
}
Would map to card profiles (input-output) primary-primary and lpa-primary.
When module-droid-card is run without module_id argument, as default "primary"
is used.
virtual profiles
----------------
@ -202,6 +173,33 @@ should be used when ringtone is playing, to again enable possible loudness
related optimizations etc. Voicecall-record profile can be enabled when
voicecall profile is active.
debugging profiles
------------------
If needed in favour of default profile one can opt to creating combinations
of all output and input definitions in a module definition. This can be
done by passing "default=false" to module-droid-card. Module argument
module_id will then defines which module to load (by default "primary").
Without default profile all input and output definitions are translated
to PulseAudio card profiles. For example configuration with
audio_hw_modules {
primary {
outputs {
primary {}
lpa {}
}
inputs {
primary {}
}
}
other {
...
}
}
Would map to card profiles (<output>-<input>) primary-primary and lpa-primary.
module-droid-sink and module-droid-source
-----------------------------------------
@ -252,6 +250,41 @@ control sinks and sources. (For example in SailfishOS this entity is OHM with
accessory-plugin and pulseaudio-policy-enforcement module for actually making
the port switching)
Droid source automatic reconfiguration
--------------------------------------
As droid HAL makes assumptions on (input) routing based on what the parameters
for the stream are (device, sample rate, channels, format, etc.) normal
PulseAudio sources are a bit inflexible as only sample rate can change after
source creation and even then there are restrictions based on alternative
sample rate value.
To overcome this and to allow some more variables affecting the stream being
passed to the input stream droid source is modified to reconfigure itself
with the source-output that connects to it. This means, that just looking at
inactive source from "pactl list" listing doesn't tell the whole story.
Droid source is always reconfigured with the *last* source-output that
connects to it, possibly already connected source-outputs will continue
to read from the source but through resampler.
For example,
1) source-output 44100Hz, stereo connects (so1)
a) source is configured with 44100Hz, stereo
b) so1 connects to the source without resampler
2) source-output 16000Hz, mono connects (so2)
a) so1 is detached from the source
b) source is configured with 16000Hz, mono
c) so2 connects to the source without resampler
d) resampler is created for so1, 16000Hz, mono -> 44100Hz stereo
f) so1 is re-attached to the source through resampler
3) source-output 16000Hz, mono connects (so3)
a) so1 and so2 are detached from the source
b) so3 connects to the source without resampler
c) so1 is re-attached to the source through resampler
d) so2 is attached to the source
Classifying sinks and sources
-----------------------------
@ -285,9 +318,8 @@ properties:
There also may be just one sink, with all the properties defined as "true"
and so on.
Similarly if there is only one input device the sole source would have both
input type properties set as "true", but it also might be that the different
input type properties are split to two different sources.
Right now there exists only one source (input device) which will always have
both properties as true.
Quirks
------

View file

@ -260,9 +260,10 @@ CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_INPUT_FLAG_NONE])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_INPUT_FLAG_FAST])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_INPUT_FLAG_HW_HOTWORD])
# Added in 6.0
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_INPUT_FLAG_RAW])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_INPUT_FLAG_SYNC])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_INPUT_FLAG_MMAP_NOIRQ])
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_INPUT_FLAG_VOIP_TX])
# Channels
CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_CHANNEL_OUT_SURROUND])

View file

@ -339,6 +339,8 @@ struct string_conversion string_conversion_table_input_flag[] = {
STRING_ENTRY_IF_AUDIO_INPUT_FLAG_HW_HOTWORD
STRING_ENTRY_IF_AUDIO_INPUT_FLAG_RAW
STRING_ENTRY_IF_AUDIO_INPUT_FLAG_SYNC
STRING_ENTRY_IF_AUDIO_INPUT_FLAG_MMAP_NOIRQ
STRING_ENTRY_IF_AUDIO_INPUT_FLAG_VOIP_TX
{ 0, NULL }
};

File diff suppressed because it is too large Load diff

View file

@ -92,13 +92,18 @@ struct pa_droid_hw_module {
pa_idxset *inputs;
pa_hook_slot *sink_put_hook_slot;
pa_hook_slot *sink_unlink_hook_slot;
pa_hook_slot *source_put_hook_slot;
pa_hook_slot *source_unlink_hook_slot;
pa_atomic_t active_outputs;
pa_droid_quirks *quirks;
pa_hook hooks[PA_DROID_HOOK_MAX];
/* Mode and input control */
struct _state {
audio_mode_t mode;
audio_devices_t input_device;
audio_source_t audio_source;
pa_droid_stream *active_input;
} state;
};
struct pa_droid_output_stream {
@ -113,12 +118,10 @@ struct pa_droid_input_stream {
struct audio_stream_in *stream;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_sample_spec input_sample_spec;
pa_channel_map input_channel_map;
pa_sample_spec req_sample_spec;
pa_channel_map req_channel_map;
uint32_t flags;
uint32_t device;
audio_devices_t all_devices;
bool merged;
};
struct pa_droid_stream {
@ -162,8 +165,8 @@ struct pa_droid_mapping {
pa_droid_profile_set *profile_set;
const pa_droid_config_device *output;
const pa_droid_config_device *input;
const pa_droid_config_device *input2;
/* Use all devices in one input */
const pa_droid_config_device *inputs;
char *name;
char *description;
@ -189,9 +192,12 @@ typedef struct pa_droid_profile {
unsigned priority;
/* Idxsets contain pa_droid_mapping objects.
* Profile doesn't own the mappings. */
* Profile doesn't own the mappings, these
* are references to structs in profile set
* hashmaps. */
pa_idxset *output_mappings;
pa_idxset *input_mappings;
/* Only one input */
pa_droid_mapping *input_mapping;
} pa_droid_profile;
@ -240,24 +246,24 @@ static inline bool pa_droid_quirk(pa_droid_hw_module *hw, enum pa_droid_quirk_ty
return hw->quirks && hw->quirks->enabled[quirk];
}
bool pa_droid_hw_set_mode(pa_droid_hw_module *hw_module, audio_mode_t mode);
bool pa_droid_hw_has_mic_control(pa_droid_hw_module *hw);
int pa_droid_hw_mic_get_mute(pa_droid_hw_module *hw_module, bool *muted);
void pa_droid_hw_mic_set_mute(pa_droid_hw_module *hw_module, bool muted);
/* Profiles */
pa_droid_profile_set *pa_droid_profile_set_new(const pa_droid_config_hw_module *module);
pa_droid_profile_set *pa_droid_profile_set_default_new(const pa_droid_config_hw_module *module,
bool merge_inputs);
pa_droid_profile_set *pa_droid_profile_set_default_new(const pa_droid_config_hw_module *module);
void pa_droid_profile_set_free(pa_droid_profile_set *ps);
void pa_droid_profile_add_mapping(pa_droid_profile *p, pa_droid_mapping *am);
void pa_droid_profile_free(pa_droid_profile *p);
pa_droid_mapping *pa_droid_mapping_get(pa_droid_profile_set *ps, const pa_droid_config_device *device);
pa_droid_mapping *pa_droid_mapping_merged_get(pa_droid_profile_set *ps,
const pa_droid_config_device *input1,
const pa_droid_config_device *input2);
bool pa_droid_mapping_is_primary(pa_droid_mapping *am);
/* Go through idxset containing pa_droid_mapping objects and if primary output or input
* mapping is found, return pointer to that mapping. */
pa_droid_mapping *pa_droid_idxset_get_primary(pa_idxset *i);
pa_droid_mapping *pa_droid_idxset_mapping_with_device(pa_idxset *i, uint32_t flag);
void pa_droid_mapping_free(pa_droid_mapping *am);
/* Add ports from sinks/sources.
@ -267,8 +273,6 @@ void pa_droid_add_ports(pa_hashmap *ports, pa_droid_mapping *am, pa_card *card);
* May be called multiple times for one card profile. */
void pa_droid_add_card_ports(pa_card_profile *cp, pa_hashmap *ports, pa_droid_mapping *am, pa_core *core);
pa_hook *pa_droid_hooks(pa_droid_hw_module *hw);
/* Module operations */
int pa_droid_set_parameters(pa_droid_hw_module *hw, const char *parameters);
@ -296,12 +300,16 @@ pa_droid_stream *pa_droid_open_output_stream(pa_droid_hw_module *module,
*/
int pa_droid_stream_set_route(pa_droid_stream *s, audio_devices_t device);
/* Input stream operations */
pa_droid_stream *pa_droid_open_input_stream(pa_droid_hw_module *module,
const pa_sample_spec *spec,
const pa_channel_map *map,
audio_devices_t devices,
pa_droid_mapping *am);
/* 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);
bool pa_droid_hw_set_input_device(pa_droid_hw_module *hw_module,
audio_devices_t device);
const pa_sample_spec *pa_droid_stream_sample_spec(pa_droid_stream *stream);
const pa_channel_map *pa_droid_stream_channel_map(pa_droid_stream *stream);
bool pa_droid_stream_is_primary(pa_droid_stream *s);

View file

@ -398,11 +398,7 @@ static void thread_func(void *userdata) {
pa_rtpoll_set_timer_disabled(u->rtpoll);
/* Sleep */
#if (PULSEAUDIO_VERSION == 5)
if ((ret = pa_rtpoll_run(u->rtpoll, true)) < 0)
#elif (PULSEAUDIO_VERSION >= 6)
if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
#endif
goto fail;
if (ret == 0)

View file

@ -73,8 +73,6 @@ struct userdata {
size_t buffer_size;
pa_usec_t timestamp;
pa_hook_slot *input_buffer_size_changed_slot;
pa_hook_slot *input_channel_map_changed_slot;
pa_resampler *resampler;
pa_droid_card_data *card_data;
@ -96,12 +94,17 @@ static void userdata_free(struct userdata *u);
static int suspend(struct userdata *u);
static void unsuspend(struct userdata *u);
/* 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
* as well. In this case just avoid using the stream but don't die. */
#define assert_stream(x, action) if (!x) do { pa_log_warn("Assert " #x " failed."); action; } while(0)
static int do_routing(struct userdata *u, audio_devices_t devices) {
int ret;
audio_devices_t old_device;
pa_assert(u);
pa_assert(u->stream);
assert_stream(u->stream, return 0);
if (u->primary_devices == devices)
pa_log_debug("Refresh active device routing.");
@ -225,11 +228,7 @@ static void thread_func(void *userdata) {
pa_rtpoll_set_timer_disabled(u->rtpoll);
/* Sleep */
#if (PULSEAUDIO_VERSION == 5)
if ((ret = pa_rtpoll_run(u->rtpoll, true)) < 0)
#elif (PULSEAUDIO_VERSION >= 6)
if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
#endif
goto fail;
if (ret == 0)
@ -252,7 +251,7 @@ static int suspend(struct userdata *u) {
int ret;
pa_assert(u);
pa_assert(u->stream);
assert_stream(u->stream, return 0);
ret = pa_droid_stream_suspend(u->stream, true);
@ -265,9 +264,10 @@ static int suspend(struct userdata *u) {
/* Called from IO context */
static void unsuspend(struct userdata *u) {
pa_assert(u);
pa_assert(u->stream);
if (pa_droid_stream_suspend(u->stream, false) >= 0) {
if (!u->stream) {
assert_stream(u->stream, u->stream_valid = false);
} else if (pa_droid_stream_suspend(u->stream, false) >= 0) {
u->stream_valid = true;
pa_log_info("Resuming...");
} else
@ -364,7 +364,7 @@ static int source_set_port_cb(pa_source *s, pa_device_port *p) {
return 0;
}
pa_log_debug("Source set port %u", data->device);
pa_log_debug("Source set port %#010x", data->device);
if (!PA_SOURCE_IS_OPENED(pa_source_get_state(u->source)))
do_routing(u, data->device);
@ -396,60 +396,30 @@ static void source_set_name(pa_modargs *ma, pa_source_new_data *data, const char
}
}
#if (PULSEAUDIO_VERSION == 5)
static void source_get_mute_cb(pa_source *s) {
#elif (PULSEAUDIO_VERSION >= 6)
static int source_get_mute_cb(pa_source *s, bool *muted) {
#endif
struct userdata *u = s->userdata;
int ret = 0;
bool b;
pa_assert(u);
pa_assert(u->hw_module && u->hw_module->device);
pa_assert(u->hw_module);
pa_droid_hw_module_lock(u->hw_module);
if (u->hw_module->device->get_mic_mute(u->hw_module->device, &b) < 0) {
pa_log("Failed to get mute state.");
ret = -1;
}
pa_droid_hw_module_unlock(u->hw_module);
#if (PULSEAUDIO_VERSION == 5)
if (ret == 0)
s->muted = b;
#elif (PULSEAUDIO_VERSION >= 6)
if (ret == 0)
*muted = b;
return ret;
#endif
return pa_droid_hw_mic_get_mute(u->hw_module, muted);
}
static void source_set_mute_cb(pa_source *s) {
struct userdata *u = s->userdata;
pa_assert(u);
pa_assert(u->hw_module && u->hw_module->device);
pa_droid_hw_module_lock(u->hw_module);
if (u->hw_module->device->set_mic_mute(u->hw_module->device, s->muted) < 0)
pa_log("Failed to set mute state to %smuted.", s->muted ? "" : "un");
pa_droid_hw_module_unlock(u->hw_module);
pa_droid_hw_mic_set_mute(u->hw_module, s->muted);
}
static void source_set_mute_control(struct userdata *u) {
pa_assert(u);
pa_assert(u->hw_module && u->hw_module->device);
if (u->hw_module->device->set_mic_mute) {
pa_log_info("Using hardware mute control for %s", u->source->name);
if (pa_droid_hw_has_mic_control(u->hw_module)) {
pa_source_set_get_mute_callback(u->source, source_get_mute_cb);
pa_source_set_set_mute_callback(u->source, source_set_mute_cb);
} else {
pa_log_info("Using software mute control for %s", u->source->name);
pa_source_set_get_mute_callback(u->source, NULL);
pa_source_set_set_mute_callback(u->source, NULL);
}
}
@ -457,9 +427,13 @@ static void source_set_mute_control(struct userdata *u) {
static void update_latency(struct userdata *u) {
pa_assert(u);
pa_assert(u->source);
pa_assert(u->stream);
u->buffer_size = pa_droid_stream_buffer_size(u->stream);
if (u->stream)
u->buffer_size = pa_droid_stream_buffer_size(u->stream);
else
u->buffer_size = 1024; /* Random valid value */
assert_stream(u->stream, return);
if (u->source_buffer_size) {
u->buffer_size = pa_droid_buffer_size_round_up(u->source_buffer_size, u->buffer_size);
@ -468,53 +442,66 @@ static void update_latency(struct userdata *u) {
pa_log_info("Using buffer size %u.", u->buffer_size);
if (pa_thread_mq_get())
pa_source_set_fixed_latency_within_thread(u->source, pa_bytes_to_usec(u->buffer_size, &u->stream->input->sample_spec));
pa_source_set_fixed_latency_within_thread(u->source, pa_bytes_to_usec(u->buffer_size, pa_droid_stream_sample_spec(u->stream)));
else
pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->buffer_size, &u->stream->input->sample_spec));
pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->buffer_size, pa_droid_stream_sample_spec(u->stream)));
pa_log_debug("Set fixed latency %" PRIu64 " usec", pa_bytes_to_usec(u->buffer_size, &u->stream->input->sample_spec));
pa_log_debug("Set fixed latency %" PRIu64 " usec", pa_bytes_to_usec(u->buffer_size, pa_droid_stream_sample_spec(u->stream)));
}
/* Called from IO context. */
static pa_hook_result_t input_buffer_size_changed_cb(pa_droid_hw_module *module,
pa_droid_stream *stream,
struct userdata *u) {
pa_assert(module);
pa_assert(stream);
pa_assert(u);
static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
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;
if (stream != u->stream)
/* 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.
* is renegotiated correctly. */
source_outputs = pa_source_move_all_start(u->source, NULL);
}
pa_source_suspend(u->source, true, PA_SUSPEND_UNAVAILABLE);
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;
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 (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));
pa_log_info("Source reconfigured.");
}
update_latency(u);
pa_source_suspend(u->source, false, PA_SUSPEND_UNAVAILABLE);
return PA_HOOK_OK;
}
/* Called from IO context. */
static pa_hook_result_t input_channel_map_changed_cb(pa_droid_hw_module *module,
pa_droid_stream *stream,
struct userdata *u) {
pa_assert(module);
pa_assert(stream);
pa_assert(u);
if (stream != u->stream)
return PA_HOOK_OK;
if (u->stream->input->input_channel_map.channels != u->source->channel_map.channels) {
if (u->resampler)
pa_resampler_free(u->resampler);
u->resampler = pa_resampler_new(u->core->mempool,
&u->stream->input->input_sample_spec, &u->stream->input->input_channel_map,
&u->source->sample_spec, &u->source->channel_map,
u->core->lfe_crossover_freq,
PA_RESAMPLER_COPY,
0);
} else if (u->resampler) {
pa_resampler_free(u->resampler);
u->resampler = NULL;
if (source_outputs && u->source) {
pa_source_move_all_finish(u->source, source_outputs, false);
}
return PA_HOOK_OK;
@ -548,7 +535,7 @@ pa_source *pa_droid_source_new(pa_module *m,
/* When running under card use hw module name for source by default. */
if (am)
module_id = am->input->module->name;
module_id = am->inputs->module->name;
else
module_id = pa_modargs_get_value(ma, "module_id", DEFAULT_MODULE_ID);
@ -649,10 +636,8 @@ pa_source *pa_droid_source_new(pa_module *m,
}
}
if (am)
u->stream = pa_droid_open_input_stream(u->hw_module, &sample_spec, &channel_map, dev_in, am);
else
u->stream = pa_droid_open_input_stream(u->hw_module, &sample_spec, &channel_map, dev_in, NULL);
pa_droid_hw_set_input_device(u->hw_module, dev_in);
u->stream = pa_droid_open_input_stream(u->hw_module, &sample_spec, &channel_map);
if (!u->stream) {
pa_log("Failed to open input stream.");
@ -672,6 +657,8 @@ pa_source *pa_droid_source_new(pa_module *m,
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "sound");
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, PROP_DROID_API_STRING);
pa_proplist_sets(data.proplist, PROP_DROID_INPUT_EXTERNAL, "true");
pa_proplist_sets(data.proplist, PROP_DROID_INPUT_BUILTIN, "true");
/* We need to give pa_modargs_get_value_boolean() a pointer to a local
* variable instead of using &data.namereg_fail directly, because
@ -685,8 +672,8 @@ pa_source *pa_droid_source_new(pa_module *m,
}
data.namereg_fail = namereg_fail;
pa_source_new_data_set_sample_spec(&data, &u->stream->input->sample_spec);
pa_source_new_data_set_channel_map(&data, &u->stream->input->channel_map);
pa_source_new_data_set_sample_spec(&data, pa_droid_stream_sample_spec(u->stream));
pa_source_new_data_set_channel_map(&data, pa_droid_stream_channel_map(u->stream));
pa_source_new_data_set_alternate_sample_rate(&data, alternate_sample_rate);
if (am && card)
@ -730,17 +717,12 @@ pa_source *pa_droid_source_new(pa_module *m,
if (u->source->active_port)
source_set_port_cb(u->source, u->source->active_port);
u->input_buffer_size_changed_slot = pa_hook_connect(&pa_droid_hooks(u->hw_module)[PA_DROID_HOOK_INPUT_BUFFER_SIZE_CHANGED],
PA_HOOK_NORMAL,
(pa_hook_cb_t) input_buffer_size_changed_cb, u);
u->input_channel_map_changed_slot = pa_hook_connect(&pa_droid_hooks(u->hw_module)[PA_DROID_HOOK_INPUT_CHANNEL_MAP_CHANGED],
PA_HOOK_NORMAL,
(pa_hook_cb_t) input_channel_map_changed_cb, u);
pa_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);
return u->source;
fail:
@ -765,12 +747,6 @@ void pa_droid_source_free(pa_source *s) {
static void userdata_free(struct userdata *u) {
pa_assert(u);
if (u->input_channel_map_changed_slot)
pa_hook_slot_free(u->input_channel_map_changed_slot);
if (u->input_buffer_size_changed_slot)
pa_hook_slot_free(u->input_buffer_size_changed_slot);
if (u->source)
pa_source_unlink(u->source);
@ -790,7 +766,6 @@ static void userdata_free(struct userdata *u) {
if (u->stream)
pa_droid_stream_unref(u->stream);
// Stand alone source
if (u->hw_module)
pa_droid_hw_module_unref(u->hw_module);

View file

@ -60,6 +60,7 @@
//#include <droid/system/audio_policy.h>
#include <droid/droid-util.h>
#include <droid/sllist.h>
#include "droid-sink.h"
#include "droid-source.h"
@ -82,7 +83,7 @@ PA_MODULE_USAGE(
"voice_property_key=<proplist key searched for sink-input that should control voice call volume> "
"voice_property_value=<proplist value for the key for voice control sink-input> "
"default_profile=<boolean. create default profile for primary module or not. defaults to true> "
"merge_inputs=<boolean. merge input streams to single source with default profile. defaults to true> "
"merge_inputs=<unused, always true> "
"quirks=<comma separated list of quirks to enable/disable>"
);
@ -116,6 +117,7 @@ static const char* const valid_modargs[] = {
"voice_property_value",
"default_profile",
"combine",
"merge_inputs",
"quirks",
NULL,
};
@ -238,7 +240,7 @@ static pa_card_profile* add_virtual_profile(struct userdata *u, const char *name
pa_hashmap *profiles) {
pa_droid_profile *ap;
pa_card_profile *cp;
struct profile_data *d, *ext;
struct profile_data *d;
pa_assert(u);
pa_assert(u->profile_set);
@ -326,11 +328,13 @@ static void add_profile(struct userdata *u, pa_hashmap *h, pa_hashmap *ports, pa
cp->max_sink_channels = max_channels;
max_channels = 0;
PA_IDXSET_FOREACH(am, ap->input_mappings, idx) {
if ((am = ap->input_mapping)) {
const pa_droid_config_device *input;
cp->n_sources++;
pa_droid_add_card_ports(cp, ports, am, u->core);
max_channels = popcount(am->input->channel_masks) > max_channels
? popcount(am->input->channel_masks) : max_channels;
SLLIST_FOREACH(input, am->inputs)
max_channels = popcount(input->channel_masks) > max_channels
? popcount(input->channel_masks) : max_channels;
}
cp->max_source_channels = max_channels;
@ -373,46 +377,11 @@ static void init_profile(struct userdata *u) {
}
}
if (d->droid_profile && pa_idxset_size(d->droid_profile->input_mappings) > 0) {
PA_IDXSET_FOREACH(am, d->droid_profile->input_mappings, idx) {
am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, (audio_devices_t) 0, &u->card_data, am, u->card);
}
if (d->droid_profile && (am = d->droid_profile->input_mapping)) {
am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, (audio_devices_t) 0, &u->card_data, am, u->card);
}
}
static int set_mode(struct userdata *u, audio_mode_t mode) {
int ret;
const char *mode_str;
pa_assert(u);
pa_assert(u->hw_module);
pa_assert(u->hw_module->device);
switch (mode) {
case AUDIO_MODE_RINGTONE:
mode_str = "AUDIO_MODE_RINGTONE";
break;
case AUDIO_MODE_IN_CALL:
mode_str = "AUDIO_MODE_IN_CALL";
break;
case AUDIO_MODE_IN_COMMUNICATION:
mode_str = "AUDIO_MODE_IN_COMMUNICATION";
break;
default:
mode_str = "AUDIO_MODE_NORMAL";
break;
}
pa_log_debug("Set mode to %s.", mode_str);
pa_droid_hw_module_lock(u->hw_module);
if ((ret = u->hw_module->device->set_mode(u->hw_module->device, mode)) < 0)
pa_log("Failed to set mode.");
pa_droid_hw_module_unlock(u->hw_module);
return ret;
}
static void park_profile(pa_droid_profile *dp) {
pa_droid_mapping *am;
uint32_t idx;
@ -428,11 +397,9 @@ static void park_profile(pa_droid_profile *dp) {
};
/* Virtual profiles don't have input mappings. */
if (dp->input_mappings) {
PA_IDXSET_FOREACH(am, dp->input_mappings, idx) {
if (pa_droid_mapping_is_primary(am))
pa_source_set_port(am->source, PA_DROID_INPUT_PARKING, false);
}
if ((am = dp->input_mapping)) {
if (pa_droid_mapping_is_primary(am))
pa_source_set_port(am->source, PA_DROID_INPUT_PARKING, false);
};
}
@ -447,7 +414,6 @@ static pa_droid_profile *card_get_droid_profile(pa_card_profile *cp) {
static bool voicecall_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) {
pa_droid_profile *dp = NULL;
pa_card_profile *cp = NULL;
pa_droid_mapping *am_output;
pa_assert(u);
@ -559,7 +525,7 @@ static pa_card_profile *leave_virtual_profile(struct userdata *u, pa_card *c,
if (next->mode != current->mode) {
park_profile(current->droid_profile);
set_mode(u, next->mode);
pa_droid_hw_set_mode(u->hw_module, next->mode);
}
virtual_event(u, current, false);
@ -612,7 +578,7 @@ static void enter_virtual_profile(struct userdata *u, pa_card *c,
if (next->mode != current->mode) {
park_profile(current->droid_profile);
set_mode(u, next->mode);
pa_droid_hw_set_mode(u->hw_module, next->mode);
}
if (next->virtual.parent) {
@ -685,15 +651,8 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
}
}
if (curr->droid_profile && pa_idxset_size(curr->droid_profile->input_mappings) > 0) {
PA_IDXSET_FOREACH(am, curr->droid_profile->input_mappings, idx) {
if (!am->source)
continue;
if (next->droid_profile &&
pa_idxset_get_by_data(next->droid_profile->input_mappings, am, NULL))
continue;
if (curr->droid_profile && (am = curr->droid_profile->input_mapping)) {
if (am->source && next->droid_profile && next->droid_profile->input_mapping) {
source_outputs = pa_source_move_all_start(am->source, source_outputs);
pa_droid_source_free(am->source);
am->source = NULL;
@ -712,15 +671,13 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
}
}
if (next->droid_profile && pa_idxset_size(next->droid_profile->input_mappings) > 0) {
PA_IDXSET_FOREACH(am, next->droid_profile->input_mappings, idx) {
if (!am->source)
am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, (audio_devices_t) 0, &u->card_data, am, u->card);
if (next->droid_profile && (am = next->droid_profile->input_mapping)) {
if (!am->source)
am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, (audio_devices_t) 0, &u->card_data, am, u->card);
if (source_outputs && am->source) {
pa_source_move_all_finish(am->source, source_outputs, false);
source_outputs = NULL;
}
if (source_outputs && am->source) {
pa_source_move_all_finish(am->source, source_outputs, false);
source_outputs = NULL;
}
}
@ -749,7 +706,6 @@ int pa__init(pa_module *m) {
const char *module_id;
bool namereg_fail = false;
bool default_profile = true;
bool merge_inputs = true;
const char *quirks;
pa_card_profile *voicecall = NULL;
@ -765,11 +721,6 @@ int pa__init(pa_module *m) {
goto fail;
}
if (pa_modargs_get_value_boolean(ma, "merge_inputs", &merge_inputs) < 0) {
pa_log("Failed to parse merge_inputs argument. Expects boolean value");
goto fail;
}
u = pa_xnew0(struct userdata, 1);
u->core = m->core;
m->userdata = u;
@ -803,10 +754,10 @@ int pa__init(pa_module *m) {
u->card_data.module_id = pa_xstrdup(module_id);
u->card_data.userdata = u;
if (!default_profile || !pa_streq(module_id, DEFAULT_MODULE_ID))
u->profile_set = pa_droid_profile_set_new(u->hw_module->enabled_module);
if (default_profile)
u->profile_set = pa_droid_profile_set_default_new(u->hw_module->enabled_module);
else
u->profile_set = pa_droid_profile_set_default_new(u->hw_module->enabled_module, merge_inputs);
u->profile_set = pa_droid_profile_set_new(u->hw_module->enabled_module);
pa_card_new_data_init(&data);
data.driver = __FILE__;
@ -890,14 +841,9 @@ int pa__init(pa_module *m) {
u->modargs = ma;
u->module = m;
#if (PULSEAUDIO_VERSION >= 10)
pa_card_choose_initial_profile(u->card);
#endif
init_profile(u);
#if (PULSEAUDIO_VERSION >= 10)
pa_card_put(u->card);
#endif
return 0;