commit
772b9ca3f1
8 changed files with 695 additions and 641 deletions
104
README
104
README
|
|
@ -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
|
||||
------
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue