diff options
Diffstat (limited to 'cras/src/server')
54 files changed, 810 insertions, 280 deletions
diff --git a/cras/src/server/audio_thread.c b/cras/src/server/audio_thread.c index df713ca5..cd155e82 100644 --- a/cras/src/server/audio_thread.c +++ b/cras/src/server/audio_thread.c @@ -555,8 +555,11 @@ static void append_stream_dump_info(struct audio_debug_info *info, si->runtime_nsec = time_since.tv_nsec; } -/* Handle a message sent to the playback thread */ -static int handle_playback_thread_message(struct audio_thread *thread) +/* Handle a message sent from main thread to the audio thread. + * Returns: + * Error code when reading or sending message fails. + */ +static int handle_audio_thread_message(struct audio_thread *thread) { uint8_t buf[256]; struct audio_thread_msg *msg = (struct audio_thread_msg *)buf; @@ -711,7 +714,7 @@ static int handle_playback_thread_message(struct audio_thread *thread) err = audio_thread_send_response(thread, ret); if (err < 0) return err; - return ret; + return 0; } /* Returns the number of active streams plus the number of active devices. */ @@ -912,7 +915,7 @@ static void *audio_io_thread(void *arg) continue; if (thread->pollfds[0].revents & POLLIN) { - rc = handle_playback_thread_message(thread); + rc = handle_audio_thread_message(thread); if (rc < 0) syslog(LOG_ERR, "handle message %d", rc); } diff --git a/cras/src/server/config/cras_board_config.c b/cras/src/server/config/cras_board_config.c index d04d626b..14d3fa0c 100644 --- a/cras/src/server/config/cras_board_config.c +++ b/cras/src/server/config/cras_board_config.c @@ -13,12 +13,14 @@ static const int32_t DEFAULT_OUTPUT_BUFFER_SIZE = 512; static const int32_t AEC_SUPPORTED_DEFAULT = 0; static const int32_t AEC_GROUP_ID_DEFAULT = -1; static const int32_t BLUETOOTH_WBS_ENABLED_INI_DEFAULT = 1; +static const int32_t BLUETOOTH_DEPRIORITIZE_WBS_MIC_INI_DEFAULT = 0; #define CONFIG_NAME "board.ini" #define DEFAULT_OUTPUT_BUF_SIZE_INI_KEY "output:default_output_buffer_size" #define AEC_SUPPORTED_INI_KEY "processing:aec_supported" #define AEC_GROUP_ID_INI_KEY "processing:group_id" #define BLUETOOTH_WBS_ENABLED_INI_KEY "bluetooth:wbs_enabled" +#define BLUETOOTH_DEPRIORITIZE_WBS_MIC_INI_KEY "bluetooth:deprioritize_wbs_mic" #define UCM_IGNORE_SUFFIX_KEY "ucm:ignore_suffix" void cras_board_config_get(const char *config_path, @@ -34,6 +36,8 @@ void cras_board_config_get(const char *config_path, board_config->aec_group_id = AEC_GROUP_ID_DEFAULT; board_config->ucm_ignore_suffix = NULL; board_config->bt_wbs_enabled = BLUETOOTH_WBS_ENABLED_INI_DEFAULT; + board_config->deprioritize_bt_wbs_mic = + BLUETOOTH_DEPRIORITIZE_WBS_MIC_INI_DEFAULT; if (config_path == NULL) return; @@ -66,6 +70,12 @@ void cras_board_config_get(const char *config_path, board_config->bt_wbs_enabled = iniparser_getint( ini, ini_key, BLUETOOTH_WBS_ENABLED_INI_DEFAULT); + snprintf(ini_key, MAX_INI_KEY_LENGTH, + BLUETOOTH_DEPRIORITIZE_WBS_MIC_INI_KEY); + ini_key[MAX_INI_KEY_LENGTH] = 0; + board_config->deprioritize_bt_wbs_mic = iniparser_getint( + ini, ini_key, BLUETOOTH_DEPRIORITIZE_WBS_MIC_INI_DEFAULT); + snprintf(ini_key, MAX_INI_KEY_LENGTH, UCM_IGNORE_SUFFIX_KEY); ini_key[MAX_INI_KEY_LENGTH] = 0; ptr = iniparser_getstring(ini, ini_key, ""); diff --git a/cras/src/server/config/cras_board_config.h b/cras/src/server/config/cras_board_config.h index ed80bec5..2ecde265 100644 --- a/cras/src/server/config/cras_board_config.h +++ b/cras/src/server/config/cras_board_config.h @@ -13,6 +13,7 @@ struct cras_board_config { int32_t aec_supported; int32_t aec_group_id; int32_t bt_wbs_enabled; + int32_t deprioritize_bt_wbs_mic; char *ucm_ignore_suffix; }; diff --git a/cras/src/server/cras_a2dp_iodev.c b/cras/src/server/cras_a2dp_iodev.c index 683fa31f..6c434758 100644 --- a/cras/src/server/cras_a2dp_iodev.c +++ b/cras/src/server/cras_a2dp_iodev.c @@ -664,7 +664,7 @@ struct cras_iodev *a2dp_iodev_create(struct cras_bt_transport *transport) iodev->start = start; iodev->frames_to_play_in_sleep = frames_to_play_in_sleep; - /* Create a dummy ionode */ + /* Create an empty ionode */ node = (struct cras_ionode *)calloc(1, sizeof(*node)); node->dev = iodev; strcpy(node->name, iodev->info.name); @@ -684,6 +684,8 @@ struct cras_iodev *a2dp_iodev_create(struct cras_bt_transport *transport) iodev->info.max_supported_channels = (a2dp.channel_mode == SBC_CHANNEL_MODE_MONO) ? 1 : 2; + ewma_power_disable(&iodev->ewma); + return iodev; error: if (a2dpio) { diff --git a/cras/src/server/cras_alsa_helpers.c b/cras/src/server/cras_alsa_helpers.c index 4f402498..6cdc165a 100644 --- a/cras/src/server/cras_alsa_helpers.c +++ b/cras/src/server/cras_alsa_helpers.c @@ -556,7 +556,7 @@ int cras_alsa_set_hwparams(snd_pcm_t *handle, struct cras_audio_format *format, return 0; } -int cras_alsa_set_swparams(snd_pcm_t *handle, int *enable_htimestamp) +int cras_alsa_set_swparams(snd_pcm_t *handle) { int err; snd_pcm_sw_params_t *swparams; @@ -593,50 +593,7 @@ int cras_alsa_set_swparams(snd_pcm_t *handle, int *enable_htimestamp) return err; } - if (*enable_htimestamp) { - /* Use MONOTONIC_RAW time-stamps. */ - err = snd_pcm_sw_params_set_tstamp_type( - handle, swparams, SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW); - if (err < 0) { - syslog(LOG_ERR, "set_tstamp_type: %s\n", - snd_strerror(err)); - return err; - } - err = snd_pcm_sw_params_set_tstamp_mode(handle, swparams, - SND_PCM_TSTAMP_ENABLE); - if (err < 0) { - syslog(LOG_ERR, "set_tstamp_mode: %s\n", - snd_strerror(err)); - return err; - } - } - - /* This hack is required because ALSA-LIB does not provide any way to - * detect whether MONOTONIC_RAW timestamps are supported by the kernel. - * In ALSA-LIB, the code checks the hardware protocol version. */ err = snd_pcm_sw_params(handle, swparams); - if (err == -EINVAL && *enable_htimestamp) { - *enable_htimestamp = 0; - syslog(LOG_WARNING, - "MONOTONIC_RAW timestamps are not supported."); - - err = snd_pcm_sw_params_set_tstamp_type( - handle, swparams, SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY); - if (err < 0) { - syslog(LOG_ERR, "set_tstamp_type: %s\n", - snd_strerror(err)); - return err; - } - err = snd_pcm_sw_params_set_tstamp_mode(handle, swparams, - SND_PCM_TSTAMP_NONE); - if (err < 0) { - syslog(LOG_ERR, "set_tstamp_mode: %s\n", - snd_strerror(err)); - return err; - } - - err = snd_pcm_sw_params(handle, swparams); - } if (err < 0) { syslog(LOG_ERR, "sw_params: %s\n", snd_strerror(err)); diff --git a/cras/src/server/cras_alsa_helpers.h b/cras/src/server/cras_alsa_helpers.h index 38976749..01a42aea 100644 --- a/cras/src/server/cras_alsa_helpers.h +++ b/cras/src/server/cras_alsa_helpers.h @@ -135,13 +135,10 @@ int cras_alsa_set_hwparams(snd_pcm_t *handle, struct cras_audio_format *format, /* Sets up the swparams to alsa. * Args: * handle - The open PCM to configure. - * enable_htimestamp - If non-zero, enable and configure hardware timestamps, - * updated to reflect whether MONOTONIC RAW htimestamps - * are supported by the kernel implementation. * Returns: * 0 on success, negative error on failure. */ -int cras_alsa_set_swparams(snd_pcm_t *handle, int *enable_htimestamp); +int cras_alsa_set_swparams(snd_pcm_t *handle); /* Get the number of used frames in the alsa buffer. * diff --git a/cras/src/server/cras_alsa_io.c b/cras/src/server/cras_alsa_io.c index 792305bb..da4ef630 100644 --- a/cras/src/server/cras_alsa_io.c +++ b/cras/src/server/cras_alsa_io.c @@ -113,7 +113,6 @@ struct alsa_input_node { * is_first - true if this is the first iodev on the card. * fully_specified - true if this device and it's nodes were fully specified. * That is, don't automatically create nodes for it. - * enable_htimestamp - True when the device's htimestamp is used. * handle - Handle to the opened ALSA device. * num_severe_underruns - Number of times we have run out of data badly. Unlike num_underruns which records for the duration @@ -148,7 +147,6 @@ struct alsa_io { enum CRAS_ALSA_CARD_TYPE card_type; int is_first; int fully_specified; - int enable_htimestamp; snd_pcm_t *handle; unsigned int num_severe_underruns; snd_pcm_stream_t alsa_stream; @@ -331,8 +329,7 @@ static int frames_queued(const struct cras_iodev *iodev, aio->num_severe_underruns++; return rc; } - if (!aio->enable_htimestamp) - clock_gettime(CLOCK_MONOTONIC_RAW, tstamp); + clock_gettime(CLOCK_MONOTONIC_RAW, tstamp); if (iodev->direction == CRAS_STREAM_INPUT) return (int)frames; @@ -374,7 +371,7 @@ static int close_dev(struct cras_iodev *iodev) return 0; } -static int dummy_hotword_cb(void *arg, int revents) +static int empty_hotword_cb(void *arg, int revents) { /* Only need this once. */ struct alsa_io *aio = (struct alsa_io *)arg; @@ -449,7 +446,7 @@ static int configure_dev(struct cras_iodev *iodev) return rc; /* Configure software params. */ - rc = cras_alsa_set_swparams(aio->handle, &aio->enable_htimestamp); + rc = cras_alsa_set_swparams(aio->handle); if (rc < 0) return rc; @@ -490,7 +487,7 @@ static int configure_dev(struct cras_iodev *iodev) if (aio->poll_fd >= 0) audio_thread_add_events_callback( - aio->poll_fd, dummy_hotword_cb, aio, POLLIN); + aio->poll_fd, empty_hotword_cb, aio, POLLIN); } /* Capture starts right away, playback will wait for samples. */ @@ -1549,7 +1546,7 @@ static void jack_output_plug_event(const struct cras_alsa_jack *jack, * For HDMI plug event cases, update max supported channels according * to the current active node. */ - if (node->base.type == CRAS_NODE_TYPE_HDMI) + if (node->base.type == CRAS_NODE_TYPE_HDMI && plugged) update_max_supported_channels(&aio->base); } @@ -2134,8 +2131,6 @@ alsa_iodev_create(size_t card_index, const char *card_name, size_t device_index, rc = ucm_get_min_buffer_level(ucm, &level); if (!rc && direction == CRAS_STREAM_OUTPUT) iodev->min_buffer_level = level; - - aio->enable_htimestamp = ucm_get_enable_htimestamp_flag(ucm); } set_iodev_name(iodev, card_name, dev_name, card_index, device_index, @@ -2355,8 +2350,11 @@ void alsa_iodev_ucm_complete_init(struct cras_iodev *iodev) set_default_hotword_model(iodev); + node = iodev->active_node; + /* Record max supported channels into cras_iodev_info. */ - update_max_supported_channels(iodev); + if (node && node->plugged) + update_max_supported_channels(iodev); } void alsa_iodev_destroy(struct cras_iodev *iodev) diff --git a/cras/src/server/cras_alsa_jack.c b/cras/src/server/cras_alsa_jack.c index 52a227e7..6d4d7bf5 100644 --- a/cras/src/server/cras_alsa_jack.c +++ b/cras/src/server/cras_alsa_jack.c @@ -734,6 +734,16 @@ static snd_hctl_elem_t *find_eld_control_by_dev_index(snd_hctl_t *hctl, return snd_hctl_find_elem(hctl, elem_id); } +/* For non-gpio jack, check if it's of type hdmi/dp by + * matching jack name. */ +static int is_jack_hdmi_dp(const char *jack_name) +{ + // TODO(hychao): Use the information provided in UCM instead of + // name matching. + static const char *hdmi_dp = "HDMI"; + return !!strstr(jack_name, hdmi_dp); +} + /* Find GPIO jacks for this jack_list. * Args: * jack_list - Jack list to add to. @@ -772,8 +782,9 @@ static int find_gpio_jacks(struct cras_alsa_jack_list *jack_list, if (result_jack) { *result_jack = data.result_jack; - /* Find ELD control for HDMI/DP gpio jack. */ - if (*result_jack) + /* Find ELD control only for HDMI/DP gpio jack. */ + if (*result_jack && + is_jack_hdmi_dp((*result_jack)->gpio.device_name)) (*result_jack)->eld_control = find_eld_control_by_dev_index( jack_list->hctl, @@ -833,14 +844,6 @@ static unsigned int hctl_jack_device_index(const char *name) return (unsigned int)device_index; } -/* For non-gpio jack, check if it's of type hdmi/dp by - * matching jack name. */ -static int is_jack_hdmi_dp(const char *jack_name) -{ - static const char *hdmi_dp = "HDMI/DP"; - return strncmp(jack_name, hdmi_dp, strlen(hdmi_dp)) == 0; -} - /* Checks if the given control name is in the supplied list of possible jack * control base names. */ static int is_jack_control_in_list(const char *const *list, diff --git a/cras/src/server/cras_alsa_plugin_io.c b/cras/src/server/cras_alsa_plugin_io.c index 9c557a40..32c1ae11 100644 --- a/cras/src/server/cras_alsa_plugin_io.c +++ b/cras/src/server/cras_alsa_plugin_io.c @@ -24,9 +24,9 @@ #define PLUGIN_KEY_PCM "pcm" #define PLUGIN_KEY_CARD "card" -#define DUMMY_USB_VID 0x00 -#define DUMMY_USB_PID 0x00 -#define DUMMY_USB_SERIAL_NUMBER "serial-number-not-used" +#define NULL_USB_VID 0x00 +#define NULL_USB_PID 0x00 +#define NULL_USB_SERIAL_NUMBER "serial-number-not-used" struct hctl_poll_fd { int fd; @@ -159,12 +159,11 @@ void alsa_plugin_io_create(enum CRAS_STREAM_DIRECTION direction, "section %s mixer_name %s", section->name, section->mixer_name); } - plugin->iodev = - alsa_iodev_create(0, card_name, 0, pcm_name, "", "", - ALSA_CARD_TYPE_USB, 1, /* is first */ - plugin->mixer, NULL, plugin->ucm, - plugin->hctl, direction, DUMMY_USB_VID, - DUMMY_USB_PID, DUMMY_USB_SERIAL_NUMBER); + plugin->iodev = alsa_iodev_create(0, card_name, 0, pcm_name, "", "", + ALSA_CARD_TYPE_USB, 1, /* is first */ + plugin->mixer, NULL, plugin->ucm, + plugin->hctl, direction, NULL_USB_VID, + NULL_USB_PID, NULL_USB_SERIAL_NUMBER); DL_FOREACH (ucm_sections, section) { if (section->dir != plugin->iodev->direction) diff --git a/cras/src/server/cras_alsa_ucm.c b/cras/src/server/cras_alsa_ucm.c index 3782cb24..9759a50f 100644 --- a/cras/src/server/cras_alsa_ucm.c +++ b/cras/src/server/cras_alsa_ucm.c @@ -57,7 +57,6 @@ static const char default_node_gain[] = "DefaultNodeGain"; static const char hotword_model_prefix[] = "Hotword Model"; static const char fully_specified_ucm_var[] = "FullySpecifiedUCM"; static const char main_volume_names[] = "MainVolumeNames"; -static const char enable_htimestamp_var[] = "EnableHtimestamp"; /* Use case verbs corresponding to CRAS_STREAM_TYPE. */ static const char *use_case_verbs[] = { @@ -1121,15 +1120,3 @@ unsigned int ucm_get_dma_period_for_dev(struct cras_use_case_mgr *mgr, return 0; return value; } - -unsigned int ucm_get_enable_htimestamp_flag(struct cras_use_case_mgr *mgr) -{ - char *flag; - int ret = 0; - flag = ucm_get_flag(mgr, enable_htimestamp_var); - if (!flag) - return 0; - ret = !strcmp(flag, "1"); - free(flag); - return ret; -} diff --git a/cras/src/server/cras_alsa_ucm.h b/cras/src/server/cras_alsa_ucm.h index 48dc6550..99a8b440 100644 --- a/cras/src/server/cras_alsa_ucm.h +++ b/cras/src/server/cras_alsa_ucm.h @@ -472,12 +472,4 @@ unsigned int ucm_get_dma_period_for_dev(struct cras_use_case_mgr *mgr, */ unsigned int ucm_get_optimize_no_stream_flag(struct cras_use_case_mgr *mgr); -/* Retrieve the flag that enables use of htimestamp. - * Args: - * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create. - * Returns: - * 1 if the flag is enabled. 0 otherwise. - */ -unsigned int ucm_get_enable_htimestamp_flag(struct cras_use_case_mgr *mgr); - #endif /* _CRAS_ALSA_UCM_H */ diff --git a/cras/src/server/cras_apm_list.h b/cras/src/server/cras_apm_list.h index b9a7fe2f..7a36ceae 100644 --- a/cras/src/server/cras_apm_list.h +++ b/cras/src/server/cras_apm_list.h @@ -162,7 +162,7 @@ void cras_apm_list_set_aec_dump(struct cras_apm_list *list, void *dev_ptr, /* * If webrtc audio processing library is not available then define all - * cras_apm_list functions as dummy. As long as cras_apm_list_add returns + * cras_apm_list functions as empty. As long as cras_apm_list_add returns * NULL, non of the other functions should be called. */ static inline int cras_apm_list_init(const char *device_config_dir) diff --git a/cras/src/server/cras_bt_device.c b/cras/src/server/cras_bt_device.c index 0607ac8e..70c87479 100644 --- a/cras/src/server/cras_bt_device.c +++ b/cras/src/server/cras_bt_device.c @@ -61,9 +61,7 @@ static const unsigned int CONN_WATCH_MAX_RETRIES = 30; static const unsigned int SCO_SUSPEND_DELAY_MS = 5000; static const unsigned int CRAS_SUPPORTED_PROFILES = - CRAS_BT_DEVICE_PROFILE_A2DP_SINK | - CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE | - CRAS_BT_DEVICE_PROFILE_HSP_AUDIOGATEWAY; + CRAS_BT_DEVICE_PROFILE_A2DP_SINK | CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE; /* Object to represent a general bluetooth device, and used to * associate with some CRAS modules if it supports audio. @@ -79,6 +77,8 @@ static const unsigned int CRAS_SUPPORTED_PROFILES = * connected - If this devices is connected. * connected_profiles - OR'ed all connected audio profiles. * profiles - OR'ed by all audio profiles this device supports. + * hidden_profiles - OR'ed by all audio profiles this device actually + * supports but is not scanned by BlueZ. * bt_iodevs - The pointer to the cras_iodevs of this device. * active_profile - The flag to indicate the active audio profile this * device is currently using. @@ -102,8 +102,9 @@ struct cras_bt_device { int paired; int trusted; int connected; - enum cras_bt_device_profile connected_profiles; - enum cras_bt_device_profile profiles; + unsigned int connected_profiles; + unsigned int profiles; + unsigned int hidden_profiles; struct cras_iodev *bt_iodevs[CRAS_NUM_DIRECTIONS]; unsigned int active_profile; int use_hardware_volume; @@ -512,14 +513,18 @@ int cras_bt_device_audio_gateway_initialized(struct cras_bt_device *device) * behavior on qualification test software. */ if (!cras_bt_device_supports_profile( device, CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE)) { - cras_bt_device_add_supported_profiles(device, HFP_HF_UUID); + unsigned int profiles = + device->profiles | CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE; + cras_bt_device_set_supported_profiles(device, profiles); + device->hidden_profiles |= CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE; bt_device_conn_watch_cb(NULL, (void *)device); } return 0; } -int cras_bt_device_get_active_profile(const struct cras_bt_device *device) +unsigned int +cras_bt_device_get_active_profile(const struct cras_bt_device *device) { return device->active_profile; } @@ -569,6 +574,19 @@ static void cras_bt_device_log_profile(const struct cras_bt_device *device, } } +static void cras_bt_device_log_profiles(const struct cras_bt_device *device, + unsigned int profiles) +{ + unsigned int profile; + + while (profiles) { + /* Get the LSB of profiles */ + profile = profiles & -profiles; + cras_bt_device_log_profile(device, profile); + profiles ^= profile; + } +} + static int cras_bt_device_is_profile_connected(const struct cras_bt_device *device, enum cras_bt_device_profile profile) @@ -708,7 +726,7 @@ void cras_bt_device_set_connected(struct cras_bt_device *device, int value) void cras_bt_device_notify_profile_dropped(struct cras_bt_device *device, enum cras_bt_device_profile profile) { - device->connected_profiles &= !profile; + device->connected_profiles &= ~profile; /* Do nothing if device already disconnected. */ if (!device->connected) @@ -723,37 +741,33 @@ void cras_bt_device_notify_profile_dropped(struct cras_bt_device *device, UNEXPECTED_PROFILE_DROP); } -/* - * Check if the uuid is of a new audio profile that isn't listed - * as supported by device. +/* Refresh the list of known supported profiles. * Args: - * device - The BT device holding supported profiles bitmap. - * uuid - UUID string from the device properties notified by BlueZ. + * device - The BT device holding scanned profiles bitmap. + * profiles - The OR'ed profiles the device claims to support as is notified + * by BlueZ. * Returns: - * True if uuid is a new audio profiles not already supported by device. + * The OR'ed profiles that are both supported by Cras and isn't previously + * supported by the device. */ -int cras_bt_device_add_supported_profiles(struct cras_bt_device *device, - const char *uuid) +int cras_bt_device_set_supported_profiles(struct cras_bt_device *device, + unsigned int profiles) { - enum cras_bt_device_profile profile = - cras_bt_device_profile_from_uuid(uuid); - - if (profile == 0) + /* Do nothing if no new profiles. */ + if ((device->profiles & profiles) == profiles) return 0; - /* Do nothing if this profile is not new. */ - if (device->profiles & profile) - return 0; + unsigned int new_profiles = profiles & ~device->profiles; /* Log this event as we might need to re-intialize the BT audio nodes * if new audio profile is reported for already connected device. */ - if (device->connected && (profile & CRAS_SUPPORTED_PROFILES)) + if (device->connected && (new_profiles & CRAS_SUPPORTED_PROFILES)) BTLOG(btlog, BT_NEW_AUDIO_PROFILE_AFTER_CONNECT, - device->profiles, profile); - device->profiles |= profile; - cras_bt_device_log_profile(device, profile); + device->profiles, new_profiles); + cras_bt_device_log_profiles(device, new_profiles); + device->profiles = profiles | device->hidden_profiles; - return (profile & CRAS_SUPPORTED_PROFILES); + return (new_profiles & CRAS_SUPPORTED_PROFILES); } void cras_bt_device_update_properties(struct cras_bt_device *device, @@ -821,6 +835,7 @@ void cras_bt_device_update_properties(struct cras_bt_device *device, "as") == 0 && strcmp(key, "UUIDs") == 0) { DBusMessageIter uuid_array_iter; + unsigned int profiles = 0; dbus_message_iter_recurse(&variant_iter, &uuid_array_iter); @@ -830,22 +845,21 @@ void cras_bt_device_update_properties(struct cras_bt_device *device, dbus_message_iter_get_basic(&uuid_array_iter, &uuid); - - /* - * If updated properties includes new audio - * profile, and device is connected, we need - * to start connection watcher. This is needed - * because on some bluetooth device, supported - * profiles do not present when device - * interface is added and they are updated - * later. - */ - if (cras_bt_device_add_supported_profiles( - device, uuid)) - watch_needed = device->connected; + profiles |= + cras_bt_device_profile_from_uuid(uuid); dbus_message_iter_next(&uuid_array_iter); } + + /* If updated properties includes new audio profile and + * device is connected, we need to start connection + * watcher. This is needed because on some bluetooth + * devices, supported profiles do not present when + * device interface is added and they are updated later. + */ + if (cras_bt_device_set_supported_profiles(device, + profiles)) + watch_needed = device->connected; } dbus_message_iter_next(properties_array_iter); @@ -876,7 +890,7 @@ void cras_bt_device_update_properties(struct cras_bt_device *device, } else if (strcmp(key, "Connected") == 0) { device->connected = 0; } else if (strcmp(key, "UUIDs") == 0) { - device->profiles = 0; + device->profiles = device->hidden_profiles; } dbus_message_iter_next(invalidated_array_iter); diff --git a/cras/src/server/cras_bt_device.h b/cras/src/server/cras_bt_device.h index 3800927e..4202bc93 100644 --- a/cras/src/server/cras_bt_device.h +++ b/cras/src/server/cras_bt_device.h @@ -63,8 +63,8 @@ void cras_bt_device_update_properties(struct cras_bt_device *device, DBusMessageIter *invalidated_array_iter); /* Updates the supported profiles on dev. Expose for unit test. */ -int cras_bt_device_add_supported_profiles(struct cras_bt_device *device, - const char *uuid); +int cras_bt_device_set_supported_profiles(struct cras_bt_device *device, + unsigned int profiles); /* Checks if profile is claimed supported by the device. */ int cras_bt_device_supports_profile(const struct cras_bt_device *device, @@ -133,7 +133,8 @@ void cras_bt_device_rm_iodev(struct cras_bt_device *device, struct cras_iodev *iodev); /* Gets the active profile of the bt device. */ -int cras_bt_device_get_active_profile(const struct cras_bt_device *device); +unsigned int +cras_bt_device_get_active_profile(const struct cras_bt_device *device); /* Sets the active profile of the bt device. */ void cras_bt_device_set_active_profile(struct cras_bt_device *device, diff --git a/cras/src/server/cras_bt_io.c b/cras/src/server/cras_bt_io.c index 3cffe148..9f5c2f79 100644 --- a/cras/src/server/cras_bt_io.c +++ b/cras/src/server/cras_bt_io.c @@ -518,7 +518,7 @@ struct cras_iodev *cras_bt_io_create(struct cras_bt_device *device, iodev->set_volume = set_bt_volume; } - /* Create the dummy node so it's the only node exposed to UI, and + /* Create the fake node so it's the only node exposed to UI, and * point it to the first profile dev. */ active = (struct bt_node *)calloc(1, sizeof(*active)); if (!active) diff --git a/cras/src/server/cras_control_rclient.c b/cras/src/server/cras_control_rclient.c index 3906a23b..cd0c4d3b 100644 --- a/cras/src/server/cras_control_rclient.c +++ b/cras/src/server/cras_control_rclient.c @@ -15,6 +15,7 @@ #include "cras_dsp.h" #include "cras_iodev.h" #include "cras_iodev_list.h" +#include "cras_hfp_ag_profile.h" #include "cras_main_thread_log.h" #include "cras_messages.h" #include "cras_observer.h" @@ -298,15 +299,11 @@ static int ccr_handle_message_from_client(struct cras_rclient *client, switch (msg->id) { case CRAS_SERVER_CONNECT_STREAM: { int client_shm_fd = num_fds > 1 ? fds[1] : -1; - struct cras_connect_message cmsg; if (MSG_LEN_VALID(msg, struct cras_connect_message)) { rclient_handle_client_stream_connect( client, (const struct cras_connect_message *)msg, fd, client_shm_fd); - } else if (!convert_connect_message_old(msg, &cmsg)) { - rclient_handle_client_stream_connect(client, &cmsg, fd, - client_shm_fd); } else { return -EINVAL; } @@ -422,10 +419,15 @@ static int ccr_handle_message_from_client(struct cras_rclient *client, state = cras_system_state_get_no_lock(); #ifdef CRAS_DBUS memcpy(&state->bt_debug_info.bt_log, btlog, - sizeof(struct cras_bt_debug_info)); + sizeof(struct cras_bt_event_log)); + memcpy(&state->bt_debug_info.wbs_logger, + cras_hfp_ag_get_wbs_logger(), + sizeof(struct packet_status_logger)); #else memset(&state->bt_debug_info.bt_log, 0, sizeof(struct cras_bt_debug_info)); + memset(&state->bt_debug_info.wbs_logger, 0, + sizeof(struct packet_status_logger)); #endif cras_fill_client_audio_debug_info_ready(&msg); diff --git a/cras/src/server/cras_dbus_control.c b/cras/src/server/cras_dbus_control.c index 628ec221..3479c3c6 100644 --- a/cras/src/server/cras_dbus_control.c +++ b/cras/src/server/cras_dbus_control.c @@ -75,6 +75,9 @@ " <method name=\"GetSystemAecGroupId\">\n" \ " <arg name=\"group_id\" type=\"i\" direction=\"out\"/>\n" \ " </method>\n" \ + " <method name=\"GetDeprioritizeBtWbsMic\">\n" \ + " <arg name=\"deprioritized\" type=\"b\" direction=\"out\"/>\n" \ + " </method>\n" \ " <method name=\"SetActiveOutputNode\">\n" \ " <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n" \ " </method>\n" \ @@ -105,6 +108,9 @@ " <method name=\"GetNumberOfActiveInputStreams\">\n" \ " <arg name=\"num\" type=\"i\" direction=\"out\"/>\n" \ " </method>\n" \ + " <method name=\"GetNumberOfInputStreamsWithPermission\">\n" \ + " <arg name=\"num\" type=\"a{sv}\" direction=\"out\"/>\n" \ + " </method>\n" \ " <method name=\"SetGlobalOutputChannelRemix\">\n" \ " <arg name=\"num_channels\" type=\"i\" direction=\"in\"/>\n" \ " <arg name=\"coefficient\" type=\"ad\" direction=\"in\"/>\n" \ @@ -671,6 +677,27 @@ static DBusHandlerResult handle_get_system_aec_group_id(DBusConnection *conn, } static DBusHandlerResult +handle_get_deprioritize_bt_wbs_mic(DBusConnection *conn, DBusMessage *message, + void *arg) +{ + DBusMessage *reply; + dbus_uint32_t serial = 0; + dbus_bool_t deprioritized; + + reply = dbus_message_new_method_return(message); + + deprioritized = cras_system_get_deprioritize_bt_wbs_mic(); + dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &deprioritized, + DBUS_TYPE_INVALID); + + dbus_connection_send(conn, reply, &serial); + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult handle_set_active_node(DBusConnection *conn, DBusMessage *message, void *arg, enum CRAS_STREAM_DIRECTION direction) { @@ -784,6 +811,65 @@ handle_get_num_active_streams_use_output_hw(DBusConnection *conn, return DBUS_HANDLER_RESULT_HANDLED; } +static bool append_num_input_streams_with_permission( + DBusMessage *message, uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE]) +{ + DBusMessageIter array; + DBusMessageIter dict; + unsigned type; + + dbus_message_iter_init_append(message, &array); + for (type = 0; type < CRAS_NUM_CLIENT_TYPE; ++type) { + const char *client_type_str = cras_client_type_str(type); + if (!is_utf8_string(client_type_str)) { + syslog(LOG_ERR, + "Non-utf8 clinet_type_str '%s' cannot be sent " + "via dbus", + client_type_str); + client_type_str = ""; + } + + if (!dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY, + "{sv}", &dict)) + return false; + if (!append_key_value(&dict, "ClientType", DBUS_TYPE_STRING, + DBUS_TYPE_STRING_AS_STRING, + &client_type_str)) + return false; + if (!append_key_value(&dict, "NumStreamsWithPermission", + DBUS_TYPE_UINT32, + DBUS_TYPE_UINT32_AS_STRING, + &num_input_streams[type])) + return false; + if (!dbus_message_iter_close_container(&array, &dict)) + return false; + } + return true; +} + +static DBusHandlerResult +handle_get_num_input_streams_with_permission(DBusConnection *conn, + DBusMessage *message, void *arg) +{ + DBusMessage *reply; + dbus_uint32_t serial = 0; + uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE] = {}; + + reply = dbus_message_new_method_return(message); + + cras_system_state_get_input_streams_with_permission(num_input_streams); + if (!append_num_input_streams_with_permission(reply, num_input_streams)) + goto error; + + dbus_connection_send(conn, reply, &serial); + dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_HANDLED; + +error: + dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + static DBusHandlerResult handle_set_global_output_channel_remix(DBusConnection *conn, DBusMessage *message, void *arg) @@ -1052,6 +1138,9 @@ static DBusHandlerResult handle_control_message(DBusConnection *conn, "GetSystemAecGroupId")) { return handle_get_system_aec_group_id(conn, message, arg); } else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE, + "GetDeprioritizeBtWbsMic")) { + return handle_get_deprioritize_bt_wbs_mic(conn, message, arg); + } else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE, "SetActiveOutputNode")) { return handle_set_active_node(conn, message, arg, CRAS_STREAM_OUTPUT); @@ -1088,6 +1177,11 @@ static DBusHandlerResult handle_control_message(DBusConnection *conn, arg); } else if (dbus_message_is_method_call( message, CRAS_CONTROL_INTERFACE, + "GetNumberOfInputStreamsWithPermission")) { + return handle_get_num_input_streams_with_permission( + conn, message, arg); + } else if (dbus_message_is_method_call( + message, CRAS_CONTROL_INTERFACE, "GetNumberOfActiveOutputStreams")) { return handle_get_num_active_streams_use_output_hw( conn, message, arg); @@ -1315,6 +1409,25 @@ static void signal_num_active_streams_changed(void *context, dbus_message_unref(msg); } +static void signal_num_input_streams_with_permission_changed( + void *context, uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE]) +{ + struct cras_dbus_control *control = (struct cras_dbus_control *)context; + dbus_uint32_t serial = 0; + DBusMessage *msg; + + msg = create_dbus_message("NumberOfInputStreamsWithPermissionChanged"); + if (!msg) + return; + + if (!append_num_input_streams_with_permission(msg, num_input_streams)) + goto error; + + dbus_connection_send(control->conn, msg, &serial); +error: + dbus_message_unref(msg); +} + static void signal_hotword_triggered(void *context, int64_t tv_sec, int64_t tv_nsec) { @@ -1399,6 +1512,8 @@ void cras_dbus_control_start(DBusConnection *conn) observer_ops.capture_mute_changed = signal_capture_mute; observer_ops.num_active_streams_changed = signal_num_active_streams_changed; + observer_ops.num_input_streams_with_permission_changed = + signal_num_input_streams_with_permission_changed; observer_ops.nodes_changed = signal_nodes_changed; observer_ops.active_node_changed = signal_active_node_changed; observer_ops.input_node_gain_changed = signal_node_capture_gain_changed; diff --git a/cras/src/server/cras_dsp.c b/cras/src/server/cras_dsp.c index d0b26264..9c4cc7b5 100644 --- a/cras/src/server/cras_dsp.c +++ b/cras/src/server/cras_dsp.c @@ -208,15 +208,15 @@ void cras_dsp_load_pipeline(struct cras_dsp_context *ctx) cmd_load_pipeline(ctx, global_ini); } -void cras_dsp_load_dummy_pipeline(struct cras_dsp_context *ctx, - unsigned int num_channels) +void cras_dsp_load_mock_pipeline(struct cras_dsp_context *ctx, + unsigned int num_channels) { - struct ini *dummy_ini; - dummy_ini = create_dummy_ini(ctx->purpose, num_channels); - if (dummy_ini == NULL) - syslog(LOG_ERR, "Failed to create dummy ini"); + struct ini *mock_ini; + mock_ini = create_mock_ini(ctx->purpose, num_channels); + if (mock_ini == NULL) + syslog(LOG_ERR, "Failed to create mock ini"); else - cmd_load_pipeline(ctx, dummy_ini); + cmd_load_pipeline(ctx, mock_ini); } struct pipeline *cras_dsp_get_pipeline(struct cras_dsp_context *ctx) diff --git a/cras/src/server/cras_dsp.h b/cras/src/server/cras_dsp.h index 9a72f42b..366e2e67 100644 --- a/cras/src/server/cras_dsp.h +++ b/cras/src/server/cras_dsp.h @@ -54,11 +54,11 @@ void cras_dsp_set_variable_boolean(struct cras_dsp_context *ctx, * blocking the audio thread. */ void cras_dsp_load_pipeline(struct cras_dsp_context *ctx); -/* Loads a dummy pipeline of source directly connects to sink, of given +/* Loads a mock pipeline of source directly connects to sink, of given * number of channels. */ -void cras_dsp_load_dummy_pipeline(struct cras_dsp_context *ctx, - unsigned int num_channels); +void cras_dsp_load_mock_pipeline(struct cras_dsp_context *ctx, + unsigned int num_channels); /* Locks the pipeline in the context for access. Returns NULL if the * pipeline is still being loaded or cannot be loaded. */ diff --git a/cras/src/server/cras_dsp_ini.c b/cras/src/server/cras_dsp_ini.c index 0b844d16..a331acf8 100644 --- a/cras/src/server/cras_dsp_ini.c +++ b/cras/src/server/cras_dsp_ini.c @@ -11,7 +11,7 @@ #define MAX_NR_PORT 128 /* the max number of ports for a plugin */ #define MAX_PORT_NAME_LENGTH 20 /* names like "output_32" */ -#define MAX_DUMMY_INI_CH 20 /* Max number of channels to create dummy ini */ +#define MAX_MOCK_INI_CH 20 /* Max number of channels to create mock ini */ /* Format of the ini file (See dsp.ini.sample for an example). @@ -305,22 +305,21 @@ static int insert_swap_lr_plugin(struct ini *ini) return 0; } -struct ini *create_dummy_ini(const char *purpose, unsigned int num_channels) +struct ini *create_mock_ini(const char *purpose, unsigned int num_channels) { - static char dummy_flow_names[MAX_DUMMY_INI_CH][9] = { - "{tmp:0}", "{tmp:1}", "{tmp:2}", "{tmp:3}", - "{tmp:4}", "{tmp:5}", "{tmp:6}", "{tmp:7}", - "{tmp:8}", "{tmp:9}", "{tmp:10}", "{tmp:11}", - "{tmp:12}", "{tmp:13}", "{tmp:14}", "{tmp:15}", - "{tmp:16}", "{tmp:17}", "{tmp:18}", "{tmp:19}", + static char mock_flow_names[MAX_MOCK_INI_CH][9] = { + "{tmp:0}", "{tmp:1}", "{tmp:2}", "{tmp:3}", "{tmp:4}", + "{tmp:5}", "{tmp:6}", "{tmp:7}", "{tmp:8}", "{tmp:9}", + "{tmp:10}", "{tmp:11}", "{tmp:12}", "{tmp:13}", "{tmp:14}", + "{tmp:15}", "{tmp:16}", "{tmp:17}", "{tmp:18}", "{tmp:19}", }; struct ini *ini; struct plugin *source, *sink; - int tmp_flow_ids[MAX_DUMMY_INI_CH]; + int tmp_flow_ids[MAX_MOCK_INI_CH]; int i; - if (num_channels > MAX_DUMMY_INI_CH) { - syslog(LOG_ERR, "Unable to create %u channels of dummy ini", + if (num_channels > MAX_MOCK_INI_CH) { + syslog(LOG_ERR, "Unable to create %u channels of mock ini", num_channels); return NULL; } @@ -332,7 +331,7 @@ struct ini *create_dummy_ini(const char *purpose, unsigned int num_channels) } for (i = 0; i < num_channels; i++) - tmp_flow_ids[i] = add_new_flow(ini, dummy_flow_names[i]); + tmp_flow_ids[i] = add_new_flow(ini, mock_flow_names[i]); source = ARRAY_APPEND_ZERO(&ini->plugins); source->title = "source"; diff --git a/cras/src/server/cras_dsp_ini.h b/cras/src/server/cras_dsp_ini.h index 51deefd6..c839d4b0 100644 --- a/cras/src/server/cras_dsp_ini.h +++ b/cras/src/server/cras_dsp_ini.h @@ -71,7 +71,7 @@ struct ini { }; /* - * Creates a dummy ini structure equivalent to: + * Creates a mock ini structure equivalent to: * * [src] * out0={tmp:0} @@ -86,7 +86,7 @@ struct ini { * The caller of this function is responsible to free the returned * ini by calling cras_dsp_ini_free(). */ -struct ini *create_dummy_ini(const char *purpose, unsigned int num_channels); +struct ini *create_mock_ini(const char *purpose, unsigned int num_channels); /* Reads the ini file into the ini structure */ struct ini *cras_dsp_ini_create(const char *ini_filename); diff --git a/cras/src/server/cras_empty_iodev.c b/cras/src/server/cras_empty_iodev.c index 76eab6c1..3471c756 100644 --- a/cras/src/server/cras_empty_iodev.c +++ b/cras/src/server/cras_empty_iodev.c @@ -199,7 +199,7 @@ struct cras_iodev *empty_iodev_create(enum CRAS_STREAM_DIRECTION direction, iodev->update_active_node = update_active_node; iodev->no_stream = cras_iodev_default_no_stream_playback; - /* Create a dummy ionode */ + /* Create an empty ionode */ node = (struct cras_ionode *)calloc(1, sizeof(*node)); node->dev = iodev; node->type = node_type; diff --git a/cras/src/server/cras_fmt_conv.c b/cras/src/server/cras_fmt_conv.c index 478452d4..509db1eb 100644 --- a/cras/src/server/cras_fmt_conv.c +++ b/cras/src/server/cras_fmt_conv.c @@ -9,6 +9,7 @@ #include <syslog.h> #include <endian.h> #include <limits.h> +#include <math.h> #include "cras_fmt_conv.h" #include "cras_fmt_conv_ops.h" @@ -58,21 +59,35 @@ static int is_channel_layout_equal(const struct cras_audio_format *a, return 1; } -static void normalize_buf(float *buf, size_t size) +/* + * Calculates the normalize_factor abs_sum(ci) from given coefficients. + * Since sum(ci / abs_sum(ci)) <= 1, this could prevent sample overflow while + * upmixing or downmixing. + */ +static float normalize_factor(float *buf, size_t n) { int i; - float squre_sum = 0.0; - for (i = 0; i < size; i++) - squre_sum += buf[i] * buf[i]; + float abs_sum = 0.0; + for (i = 0; i < n; i++) + abs_sum += fabs(buf[i]); - if (squre_sum == 0.0) - return; + return 1.0 / abs_sum; +} - for (i = 0; i < size; i++) - buf[i] /= squre_sum; +/* + * Normalize all channels with the same factor to maintain + * the energy ratio between original channels. + */ +static void normalize(float **mtx, size_t m, size_t n, float factor) +{ + int i, j; + for (i = 0; i < m; i++) + for (j = 0; j < n; j++) + mtx[i][j] *= factor; } -/* Populates the down mix matrix by rules: +/* + * Populates the down mix matrix by rules: * 1. Front/side left(right) channel will mix to left(right) of * full scale. * 2. Center and LFE will be split equally to left and right. @@ -106,9 +121,46 @@ static void surround51_to_stereo_downmix_mtx(float **mtx, mtx[STEREO_L][layout[CRAS_CH_LFE]] = 0.707; mtx[STEREO_R][layout[CRAS_CH_LFE]] = 0.707; } + normalize(mtx, 2, 6, normalize_factor(mtx[STEREO_L], 6)); +} + +/* Populates the down mix matrix by rules: + * 1. Front left(right) channel will mix to the front left(right) of + * full scale. + * 2. Rear and side left(right) channel will mix to the rear left(right) of + * full scale. + * 3. Center will be split equally to the front left and right. + * 4. LFE will be split equally to the other channels. + */ +static void surround51_to_quad_downmix_mtx(float **mtx, + int8_t layout[CRAS_CH_MAX]) +{ + if (layout[CRAS_CH_FL] != -1 && layout[CRAS_CH_FR] != -1) { + mtx[CRAS_CH_FL][layout[CRAS_CH_FL]] = 1.0; + mtx[CRAS_CH_FR][layout[CRAS_CH_FR]] = 1.0; + } + if (layout[CRAS_CH_RL] != -1 && layout[CRAS_CH_RR] != -1) { + mtx[CRAS_CH_RL][layout[CRAS_CH_RL]] = 1.0; + mtx[CRAS_CH_RR][layout[CRAS_CH_RR]] = 1.0; + } + if (layout[CRAS_CH_SL] != -1 && layout[CRAS_CH_SR] != -1) { + mtx[CRAS_CH_RL][layout[CRAS_CH_SL]] = 1.0; + mtx[CRAS_CH_RR][layout[CRAS_CH_SR]] = 1.0; + } + if (layout[CRAS_CH_FC] != -1) { + /* Split 1/2 power to the front L/R */ + mtx[CRAS_CH_FL][layout[CRAS_CH_FC]] = 0.707; + mtx[CRAS_CH_FR][layout[CRAS_CH_FC]] = 0.707; + } + if (layout[CRAS_CH_LFE] != -1) { + /* Split 1/4 power to the other channel */ + mtx[CRAS_CH_FL][layout[CRAS_CH_LFE]] = 0.5; + mtx[CRAS_CH_FR][layout[CRAS_CH_LFE]] = 0.5; + mtx[CRAS_CH_RL][layout[CRAS_CH_LFE]] = 0.5; + mtx[CRAS_CH_RR][layout[CRAS_CH_LFE]] = 0.5; + } - normalize_buf(mtx[STEREO_L], 6); - normalize_buf(mtx[STEREO_R], 6); + normalize(mtx, 4, 6, normalize_factor(mtx[CRAS_CH_FL], 6)); } static int is_supported_format(const struct cras_audio_format *fmt) @@ -170,6 +222,12 @@ static size_t _51_to_stereo(struct cras_fmt_conv *conv, const uint8_t *in, return s16_51_to_stereo(in, in_frames, out); } +static size_t _51_to_quad(struct cras_fmt_conv *conv, const uint8_t *in, + size_t in_frames, uint8_t *out) +{ + return s16_51_to_quad(in, in_frames, out); +} + static size_t stereo_to_quad(struct cras_fmt_conv *conv, const uint8_t *in, size_t in_frames, uint8_t *out) { @@ -340,7 +398,8 @@ struct cras_fmt_conv *cras_fmt_conv_create(const struct cras_audio_format *in, conv->channel_converter = quad_to_stereo; } else if (in->num_channels == 2 && out->num_channels == 6) { conv->channel_converter = stereo_to_51; - } else if (in->num_channels == 6 && out->num_channels == 2) { + } else if (in->num_channels == 6 && + (out->num_channels == 2 || out->num_channels == 4)) { int in_channel_layout_set = 0; /* Checks if channel_layout is set in the incoming format */ @@ -361,11 +420,20 @@ struct cras_fmt_conv *cras_fmt_conv_create(const struct cras_audio_format *in, return NULL; } conv->channel_converter = convert_channels; - surround51_to_stereo_downmix_mtx( - conv->ch_conv_mtx, - conv->in_fmt.channel_layout); + if (out->num_channels == 4) { + surround51_to_quad_downmix_mtx( + conv->ch_conv_mtx, + conv->in_fmt.channel_layout); + } else { + surround51_to_stereo_downmix_mtx( + conv->ch_conv_mtx, + conv->in_fmt.channel_layout); + } } else { - conv->channel_converter = _51_to_stereo; + if (out->num_channels == 4) + conv->channel_converter = _51_to_quad; + else + conv->channel_converter = _51_to_stereo; } } else if (in->num_channels <= 8 && out->num_channels <= 8) { // For average channel counts mix from all to all. diff --git a/cras/src/server/cras_fmt_conv_ops.c b/cras/src/server/cras_fmt_conv_ops.c index 87358af2..a306d216 100644 --- a/cras/src/server/cras_fmt_conv_ops.c +++ b/cras/src/server/cras_fmt_conv_ops.c @@ -235,20 +235,69 @@ size_t s16_51_to_stereo(const uint8_t *_in, size_t in_frames, uint8_t *_out) int16_t *out = (int16_t *)_out; static const unsigned int left_idx = 0; static const unsigned int right_idx = 1; - /* static const unsigned int left_surround_idx = 2; */ - /* static const unsigned int right_surround_idx = 3; */ - static const unsigned int center_idx = 4; - /* static const unsigned int lfe_idx = 5; */ - size_t i; + static const unsigned int center_idx = 2; + /* static const unsigned int lfe_idx = 3; */ + /* static const unsigned int left_surround_idx = 4; */ + /* static const unsigned int right_surround_idx = 5; */ + size_t i; + int16_t half_center; + /* Use the normalized_factor from the left channel = 1 / (|1| + |0.707|) + * to prevent mixing overflow. + */ + const float normalized_factor = 0.585; for (i = 0; i < in_frames; i++) { - unsigned int half_center; - - half_center = in[6 * i + center_idx] / 2; + half_center = + in[6 * i + center_idx] * 0.707 * normalized_factor; out[2 * i + left_idx] = - s16_add_and_clip(in[6 * i + left_idx], half_center); + in[6 * i + left_idx] * normalized_factor + half_center; out[2 * i + right_idx] = - s16_add_and_clip(in[6 * i + right_idx], half_center); + in[6 * i + right_idx] * normalized_factor + half_center; + } + return in_frames; +} + +/* + * Channel converter: 5.1 surround to quad (front L/R, rear L/R). + * + * The out buffer can have room for just quad samples. This convert function + * is used as the default behavior when channel layout is not set from the + * client side. + */ +size_t s16_51_to_quad(const uint8_t *_in, size_t in_frames, uint8_t *_out) +{ + const int16_t *in = (const int16_t *)_in; + int16_t *out = (int16_t *)_out; + static const unsigned int l_quad = 0; + static const unsigned int r_quad = 1; + static const unsigned int rl_quad = 2; + static const unsigned int rr_quad = 3; + + static const unsigned int l_51 = 0; + static const unsigned int r_51 = 1; + static const unsigned int center_51 = 2; + static const unsigned int lfe_51 = 3; + static const unsigned int rl_51 = 4; + static const unsigned int rr_51 = 5; + + /* Use normalized_factor from the left channel = 1 / (|1| + |0.707| + |0.5|) + * to prevent overflow. */ + const float normalized_factor = 0.453; + size_t i; + for (i = 0; i < in_frames; i++) { + int16_t half_center; + int16_t lfe; + + half_center = in[6 * i + center_51] * 0.707 * normalized_factor; + lfe = in[6 * i + lfe_51] * 0.5 * normalized_factor; + out[4 * i + l_quad] = normalized_factor * in[6 * i + l_51] + + half_center + lfe; + out[4 * i + r_quad] = normalized_factor * in[6 * i + r_51] + + half_center + lfe; + out[4 * i + rl_quad] = + normalized_factor * in[6 * i + rl_51] + lfe; + out[4 * i + rr_quad] = + normalized_factor * in[6 * i + rr_51] + lfe; } return in_frames; } diff --git a/cras/src/server/cras_fmt_conv_ops.h b/cras/src/server/cras_fmt_conv_ops.h index 8042ad59..a1a57487 100644 --- a/cras/src/server/cras_fmt_conv_ops.h +++ b/cras/src/server/cras_fmt_conv_ops.h @@ -51,6 +51,11 @@ size_t s16_stereo_to_51(size_t left, size_t right, size_t center, size_t s16_51_to_stereo(const uint8_t *in, size_t in_frames, uint8_t *out); /* + * Channel converter: 5.1 surround to quad. + */ +size_t s16_51_to_quad(const uint8_t *in, size_t in_frames, uint8_t *out); + +/* * Channel converter: stereo to quad (front L/R, rear L/R). */ size_t s16_stereo_to_quad(size_t front_left, size_t front_right, diff --git a/cras/src/server/cras_hfp_ag_profile.c b/cras/src/server/cras_hfp_ag_profile.c index 8cf4a431..9d59d40e 100644 --- a/cras/src/server/cras_hfp_ag_profile.c +++ b/cras/src/server/cras_hfp_ag_profile.c @@ -22,6 +22,7 @@ #include "cras_iodev_list.h" #include "cras_observer.h" #include "utlist.h" +#include "packet_status_logger.h" #define HFP_AG_PROFILE_NAME "Hands-Free Voice gateway" #define HFP_AG_PROFILE_PATH "/org/chromium/Cras/Bluetooth/HFPAG" @@ -103,6 +104,7 @@ struct audio_gateway { }; static struct audio_gateway *connected_ags; +static struct packet_status_logger wbs_logger; static int need_go_sco_pcm(struct cras_bt_device *device) { @@ -411,6 +413,7 @@ int cras_hfp_ag_start(struct cras_bt_device *device) ag->slc_handle, ag->profile); } else { ag->info = hfp_info_create(); + hfp_info_set_wbs_logger(ag->info, &wbs_logger); ag->idev = hfp_iodev_create(CRAS_STREAM_INPUT, ag->device, ag->slc_handle, ag->profile, ag->info); @@ -453,6 +456,11 @@ struct hfp_slc_handle *cras_hfp_ag_get_slc(struct cras_bt_device *device) return NULL; } +struct packet_status_logger *cras_hfp_ag_get_wbs_logger() +{ + return &wbs_logger; +} + void cras_hfp_ag_resend_device_battery_level() { struct audio_gateway *ag; diff --git a/cras/src/server/cras_hfp_ag_profile.h b/cras/src/server/cras_hfp_ag_profile.h index fb58efb6..50d27e05 100644 --- a/cras/src/server/cras_hfp_ag_profile.h +++ b/cras/src/server/cras_hfp_ag_profile.h @@ -53,6 +53,9 @@ struct hfp_slc_handle *cras_hfp_ag_get_active_handle(); /* Gets the SLC handle for given cras_bt_device. */ struct hfp_slc_handle *cras_hfp_ag_get_slc(struct cras_bt_device *device); +/* Gets the logger for WBS packet status. */ +struct packet_status_logger *cras_hfp_ag_get_wbs_logger(); + /* Iterate all possible AGs (theoratically only one) and signal its battery * level */ void cras_hfp_ag_resend_device_battery_level(); diff --git a/cras/src/server/cras_hfp_alsa_iodev.c b/cras/src/server/cras_hfp_alsa_iodev.c index 532b6c40..b80a88c7 100644 --- a/cras/src/server/cras_hfp_alsa_iodev.c +++ b/cras/src/server/cras_hfp_alsa_iodev.c @@ -309,6 +309,10 @@ struct cras_iodev *hfp_alsa_iodev_create(struct cras_iodev *aio, /* Record max supported channels into cras_iodev_info. */ iodev->info.max_supported_channels = 1; + /* Specifically disable EWMA calculation on this and the child iodev. */ + ewma_power_disable(&iodev->ewma); + ewma_power_disable(&aio->ewma); + return iodev; } diff --git a/cras/src/server/cras_hfp_info.c b/cras/src/server/cras_hfp_info.c index 550de586..fc407b29 100644 --- a/cras/src/server/cras_hfp_info.c +++ b/cras/src/server/cras_hfp_info.c @@ -19,6 +19,7 @@ #include "cras_sbc_codec.h" #include "cras_server_metrics.h" #include "utlist.h" +#include "packet_status_logger.h" /* The max buffer size. Note that the actual used size must set to multiple * of SCO packet size, and the packet size does not necessarily be equal to @@ -93,6 +94,7 @@ static const uint8_t h2_header_frames_count[] = { 0x08, 0x38, 0xc8, 0xf8 }; * read_align_cb - Callback used to align mSBC frame reading with read buf. * msbc_read_current_corrupted - Flag to mark if the current mSBC frame * read is corrupted. + * wbs_logger - The logger for packet status in WBS. */ struct hfp_info { int fd; @@ -119,6 +121,7 @@ struct hfp_info { size_t read_rp; int (*read_align_cb)(uint8_t *buf); bool msbc_read_current_corrupted; + struct packet_status_logger *wbs_logger; }; int hfp_info_add_iodev(struct hfp_info *info, @@ -403,6 +406,20 @@ static const uint8_t *extract_msbc_frame(const uint8_t *input, int len, return NULL; } +/* Log value 0 when packet is received. */ +static void log_wbs_packet_received(struct hfp_info *info) +{ + if (info->wbs_logger) + packet_status_logger_update(info->wbs_logger, 0); +} + +/* Log value 1 when packet is lost. */ +static void log_wbs_packet_lost(struct hfp_info *info) +{ + if (info->wbs_logger) + packet_status_logger_update(info->wbs_logger, 1); +} + /* * Handle the case when mSBC frame is considered lost. * Args: @@ -419,6 +436,8 @@ static int handle_packet_loss(struct hfp_info *info) info->msbc_num_in_frames++; info->msbc_num_lost_frames++; + log_wbs_packet_lost(info); + in_bytes = buf_write_pointer_size(info->capture_buf, &pcm_avail); if (pcm_avail < MSBC_CODE_SIZE) return 0; @@ -580,6 +599,7 @@ recv_msbc_bytes: pcm_read += err; } else { /* Good mSBC frame decoded. */ + log_wbs_packet_received(info); buf_increment_write(info->capture_buf, pcm_decoded); info->msbc_num_in_frames++; cras_msbc_plc_handle_good_frames(info->msbc_plc, capture_buf, @@ -723,6 +743,12 @@ error: return NULL; } +void hfp_info_set_wbs_logger(struct hfp_info *info, + struct packet_status_logger *wbs_logger) +{ + info->wbs_logger = wbs_logger; +} + int hfp_info_running(struct hfp_info *info) { return info->started; @@ -760,6 +786,8 @@ int hfp_info_start(int fd, unsigned int mtu, int codec, struct hfp_info *info) info->msbc_read = cras_msbc_codec_create(); info->msbc_write = cras_msbc_codec_create(); info->msbc_plc = cras_msbc_plc_create(); + + packet_status_logger_init(info->wbs_logger); } else { info->write_cb = hfp_write; info->read_cb = hfp_read; diff --git a/cras/src/server/cras_hfp_info.h b/cras/src/server/cras_hfp_info.h index 96110e2a..3472aeab 100644 --- a/cras/src/server/cras_hfp_info.h +++ b/cras/src/server/cras_hfp_info.h @@ -30,6 +30,10 @@ struct hfp_info *hfp_info_create(); /* Destroys given hfp_info instance. */ void hfp_info_destroy(struct hfp_info *info); +/* Sets the wbs_logger to hfp_info instance. */ +void hfp_info_set_wbs_logger(struct hfp_info *info, + struct packet_status_logger *wbs_logger); + /* Checks if given hfp_info is running. */ int hfp_info_running(struct hfp_info *info); diff --git a/cras/src/server/cras_hfp_iodev.c b/cras/src/server/cras_hfp_iodev.c index bf609cfb..7cce3736 100644 --- a/cras/src/server/cras_hfp_iodev.c +++ b/cras/src/server/cras_hfp_iodev.c @@ -350,6 +350,8 @@ struct cras_iodev *hfp_iodev_create(enum CRAS_STREAM_DIRECTION dir, /* Record max supported channels into cras_iodev_info. */ iodev->info.max_supported_channels = 1; + ewma_power_disable(&iodev->ewma); + return iodev; error: diff --git a/cras/src/server/cras_hfp_slc.c b/cras/src/server/cras_hfp_slc.c index 1cf003a1..e4f0127d 100644 --- a/cras/src/server/cras_hfp_slc.c +++ b/cras/src/server/cras_hfp_slc.c @@ -708,7 +708,14 @@ static int indicator_support(struct hfp_slc_handle *handle, const char *cmd) * check the Bluetooth SIG Assigned Numbers web page. */ BTLOG(btlog, BT_HFP_HF_INDICATOR, 1, 0); - err = hfp_send(handle, AT_CMD("+BIND: (2)")); + /* "2" is for HF Battery Level that we support. We don't + * support "1" but this is a workaround for Pixel Buds 2 + * which expects this exact combination for battery + * reporting (HFP 1.7 standard) to work. This workaround + * is fine since we don't enable Safety Drive with + * +BIND: 1,1 (b/172680041). + */ + err = hfp_send(handle, AT_CMD("+BIND: (1,2)")); if (err < 0) return err; } @@ -738,6 +745,14 @@ static int indicator_support(struct hfp_slc_handle *handle, const char *cmd) * indicator * 1 = enabled, value changes may be sent for this indicator */ + + /* We don't support Enhanced Driver Status, so explicitly + * disable it (b/172680041). + */ + err = hfp_send(handle, AT_CMD("+BIND: 1,0")); + if (err < 0) + return err; + BTLOG(btlog, BT_HFP_HF_INDICATOR, 0, 0); err = hfp_send(handle, AT_CMD("+BIND: 2,1")); @@ -927,7 +942,7 @@ static int terminate_call(struct hfp_slc_handle *handle, const char *cmd) * * An initialized service level connection is the pre-condition for all * call related procedures. Note that for the call related commands, - * we are good to just respond with a dummy "OK". + * we are good to just respond with a meaningless "OK". * * The procedure to establish a service level connection is described below: * diff --git a/cras/src/server/cras_iodev.c b/cras/src/server/cras_iodev.c index f426eb5f..fd1ce805 100644 --- a/cras/src/server/cras_iodev.c +++ b/cras/src/server/cras_iodev.c @@ -239,7 +239,7 @@ int cras_iodev_is_zero_volume(const struct cras_iodev *odev) * | ---------------- | device from * | | S1 Open | | audio_thread and * | ---------------- | closes device - * | Device with dummy start | | + * | Device with empty start | | * | ops transits into | Sample is ready | * | no stream state right V | * | after open. ---------------- | @@ -527,8 +527,8 @@ static void add_ext_dsp_module_to_pipeline(struct cras_iodev *iodev) if (!pipeline) { cras_iodev_alloc_dsp(iodev); - cras_dsp_load_dummy_pipeline(iodev->dsp_context, - iodev->format->num_channels); + cras_dsp_load_mock_pipeline(iodev->dsp_context, + iodev->format->num_channels); pipeline = cras_dsp_get_pipeline(iodev->dsp_context); } /* dsp_context mutex locked. Now it's safe to modify dsp @@ -953,6 +953,8 @@ int cras_iodev_open(struct cras_iodev *iodev, unsigned int cb_level, iodev->highest_hw_level = 0; iodev->input_dsp_offset = 0; + ewma_power_init(&iodev->ewma, iodev->format->frame_rate); + if (iodev->direction == CRAS_STREAM_OUTPUT) { /* If device supports start ops, device can be in open state. * Otherwise, device starts running right after opening. */ @@ -1097,6 +1099,9 @@ int cras_iodev_put_output_buffer(struct cras_iodev *iodev, uint8_t *frames, loopback->cb_data); } + ewma_power_calculate(&iodev->ewma, (int16_t *)frames, + iodev->format->num_channels, nframes); + rc = apply_dsp(iodev, frames, nframes); if (rc) return rc; @@ -1200,6 +1205,11 @@ int cras_iodev_get_input_buffer(struct cras_iodev *iodev, unsigned int *frames) *frames - iodev->input_dsp_offset); if (rc) return rc; + ewma_power_calculate_area( + &iodev->ewma, + (int16_t *)(hw_buffer + + iodev->input_dsp_offset * frame_bytes), + data->area, *frames - iodev->input_dsp_offset); } if (cras_system_get_capture_mute()) diff --git a/cras/src/server/cras_iodev.h b/cras/src/server/cras_iodev.h index 553cb807..db16a0f8 100644 --- a/cras/src/server/cras_iodev.h +++ b/cras/src/server/cras_iodev.h @@ -19,6 +19,7 @@ #include "cras_dsp.h" #include "cras_iodev_info.h" #include "cras_messages.h" +#include "ewma_power.h" struct buffer_share; struct cras_fmt_conv; @@ -237,7 +238,7 @@ struct cras_ionode { * stream side processing. * initial_ramp_request - The value indicates which type of ramp the device * should perform when some samples are ready for playback. - * + * ewma - The ewma instance to calculate iodev volume. */ struct cras_iodev { void (*set_volume)(struct cras_iodev *iodev); @@ -312,6 +313,7 @@ struct cras_iodev { unsigned int input_dsp_offset; unsigned int initial_ramp_request; struct input_data *input_data; + struct ewma_power ewma; struct cras_iodev *prev, *next; }; diff --git a/cras/src/server/cras_iodev_list.c b/cras/src/server/cras_iodev_list.c index bba793d1..ada29719 100644 --- a/cras/src/server/cras_iodev_list.c +++ b/cras/src/server/cras_iodev_list.c @@ -361,7 +361,8 @@ static void possibly_enable_echo_reference(struct cras_iodev *dev) if (dev->echo_reference_dev == NULL) return; - server_stream_create(stream_list, dev->echo_reference_dev->info.idx); + server_stream_create(stream_list, dev->echo_reference_dev->info.idx, + dev->format); } /* diff --git a/cras/src/server/cras_loopback_iodev.c b/cras/src/server/cras_loopback_iodev.c index 0947313f..cf3ba4ae 100644 --- a/cras/src/server/cras_loopback_iodev.c +++ b/cras/src/server/cras_loopback_iodev.c @@ -331,7 +331,7 @@ struct cras_iodev *loopback_iodev_create(enum CRAS_LOOPBACK_TYPE type) if (iodev == NULL) return NULL; - /* Create a dummy ionode */ + /* Create an empty ionode */ node = (struct cras_ionode *)calloc(1, sizeof(*node)); node->dev = iodev; node->type = node_type; diff --git a/cras/src/server/cras_observer.c b/cras/src/server/cras_observer.c index a73bcccf..0f17dc92 100644 --- a/cras/src/server/cras_observer.c +++ b/cras/src/server/cras_observer.c @@ -7,6 +7,7 @@ #include "cras_alert.h" #include "cras_iodev_list.h" +#include "cras_types.h" #include "utlist.h" struct cras_observer_client { @@ -35,6 +36,7 @@ struct cras_observer_alerts { struct cras_alert *num_active_streams[CRAS_NUM_DIRECTIONS]; struct cras_alert *non_empty_audio_state_changed; struct cras_alert *bt_battery_changed; + struct cras_alert *num_input_streams_with_permission; }; struct cras_observer_server { @@ -76,6 +78,10 @@ struct cras_observer_alert_data_streams { uint32_t num_active_streams; }; +struct cras_observer_alert_data_input_streams { + uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE]; +}; + struct cras_observer_alert_data_hotword_triggered { int64_t tv_sec; int64_t tv_nsec; @@ -253,6 +259,20 @@ static void num_active_streams_alert(void *arg, void *data) } } +static void num_input_streams_with_permission_alert(void *arg, void *data) +{ + struct cras_observer_client *client; + struct cras_observer_alert_data_input_streams *input_streams_data = + (struct cras_observer_alert_data_input_streams *)data; + + DL_FOREACH (g_observer->clients, client) { + if (client->ops.num_input_streams_with_permission_changed) + client->ops.num_input_streams_with_permission_changed( + client->context, + input_streams_data->num_input_streams); + } +} + static void hotword_triggered_alert(void *arg, void *data) { struct cras_observer_client *client; @@ -353,6 +373,7 @@ int cras_observer_server_init() CRAS_OBSERVER_SET_ALERT(hotword_triggered, NULL, 0); CRAS_OBSERVER_SET_ALERT(non_empty_audio_state_changed, NULL, 0); CRAS_OBSERVER_SET_ALERT(bt_battery_changed, NULL, 0); + CRAS_OBSERVER_SET_ALERT(num_input_streams_with_permission, NULL, 0); CRAS_OBSERVER_SET_ALERT_WITH_DIRECTION(num_active_streams, CRAS_STREAM_OUTPUT); @@ -386,6 +407,8 @@ void cras_observer_server_free() cras_alert_destroy(g_observer->alerts.non_empty_audio_state_changed); cras_alert_destroy(g_observer->alerts.bt_battery_changed); cras_alert_destroy( + g_observer->alerts.num_input_streams_with_permission); + cras_alert_destroy( g_observer->alerts.num_active_streams[CRAS_STREAM_OUTPUT]); cras_alert_destroy( g_observer->alerts.num_active_streams[CRAS_STREAM_INPUT]); @@ -562,6 +585,21 @@ void cras_observer_notify_num_active_streams(enum CRAS_STREAM_DIRECTION dir, cras_alert_pending_data(alert, &data, sizeof(data)); } +void cras_observer_notify_input_streams_with_permission( + uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE]) +{ + struct cras_observer_alert_data_input_streams data; + struct cras_alert *alert; + + memcpy(&data.num_input_streams, num_input_streams, + sizeof(*num_input_streams) * CRAS_NUM_CLIENT_TYPE); + alert = g_observer->alerts.num_input_streams_with_permission; + if (!alert) + return; + + cras_alert_pending_data(alert, &data, sizeof(data)); +} + void cras_observer_notify_hotword_triggered(int64_t tv_sec, int64_t tv_nsec) { struct cras_observer_alert_data_hotword_triggered data; diff --git a/cras/src/server/cras_observer.h b/cras/src/server/cras_observer.h index 1da5eeaf..2dd013b8 100644 --- a/cras/src/server/cras_observer.h +++ b/cras/src/server/cras_observer.h @@ -92,6 +92,10 @@ void cras_observer_notify_suspend_changed(int suspended); void cras_observer_notify_num_active_streams(enum CRAS_STREAM_DIRECTION dir, uint32_t num_active_streams); +/* Notify observers of the number of input streams with permission. */ +void cras_observer_notify_input_streams_with_permission( + uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE]); + /* Notify observers of the timestamp when hotword triggered. */ void cras_observer_notify_hotword_triggered(int64_t tv_sec, int64_t tv_nsec); diff --git a/cras/src/server/cras_rclient.c b/cras/src/server/cras_rclient.c index 22fbb05c..d2646e75 100644 --- a/cras/src/server/cras_rclient.c +++ b/cras/src/server/cras_rclient.c @@ -59,9 +59,16 @@ int cras_rclient_send_message(const struct cras_rclient *client, return client->ops->send_message_to_client(client, msg, fds, num_fds); } +static void cras_rclient_set_client_type(struct cras_rclient *client, + enum CRAS_CLIENT_TYPE client_type) +{ + client->client_type = client_type; +} + struct cras_rclient *cras_rclient_create(int fd, size_t id, enum CRAS_CONNECTION_TYPE conn_type) { + struct cras_rclient *client; if (!cras_validate_connection_type(conn_type)) goto error; @@ -76,6 +83,14 @@ struct cras_rclient *cras_rclient_create(int fd, size_t id, return cras_playback_rclient_create(fd, id); case CRAS_VMS_UNIFIED: return cras_unified_rclient_create(fd, id); + case CRAS_PLUGIN_PLAYBACK: + client = cras_playback_rclient_create(fd, id); + cras_rclient_set_client_type(client, CRAS_CLIENT_TYPE_PLUGIN); + return client; + case CRAS_PLUGIN_UNIFIED: + client = cras_unified_rclient_create(fd, id); + cras_rclient_set_client_type(client, CRAS_CLIENT_TYPE_PLUGIN); + return client; default: goto error; } diff --git a/cras/src/server/cras_rclient.h b/cras/src/server/cras_rclient.h index 6cffb7d8..3a3988c2 100644 --- a/cras/src/server/cras_rclient.h +++ b/cras/src/server/cras_rclient.h @@ -20,6 +20,9 @@ struct cras_server_message; * fd - Connection for client communication. * ops - cras_rclient_ops for the cras_rclient. * supported_directions - Bit mask for supported stream directions. + * client_type - Client type of this rclient. If this is set to value other + * than CRAS_CLIENT_TYPE_UNKNOWN, rclient will overwrite incoming + * messages' client type. */ struct cras_rclient { struct cras_observer_client *observer; @@ -27,6 +30,7 @@ struct cras_rclient { int fd; const struct cras_rclient_ops *ops; int supported_directions; + enum CRAS_CLIENT_TYPE client_type; }; /* Operations for cras_rclient. diff --git a/cras/src/server/cras_rclient_util.c b/cras/src/server/cras_rclient_util.c index da991282..def645e3 100644 --- a/cras/src/server/cras_rclient_util.c +++ b/cras/src/server/cras_rclient_util.c @@ -80,6 +80,13 @@ rclient_validate_stream_connect_message(const struct cras_rclient *client, msg->direction, client->id); return -EINVAL; } + + if (!cras_validate_client_type(msg->client_type)) { + syslog(LOG_ERR, + "stream_connect: invalid stream client_type: %x for " + "client: %zx.\n", + msg->client_type, client->id); + } return 0; } @@ -155,6 +162,9 @@ int rclient_handle_client_stream_connect(struct cras_rclient *client, stream_config = cras_rstream_config_init_with_message( client, msg, &aud_fd, &client_shm_fd, &remote_fmt); + /* Overwrite client_type if client->client_type is set. */ + if (client->client_type != CRAS_CLIENT_TYPE_UNKNOWN) + stream_config.client_type = client->client_type; rc = stream_list_add(cras_iodev_list_get_stream_list(), &stream_config, &stream); if (rc) @@ -279,15 +289,11 @@ int rclient_handle_message_from_client(struct cras_rclient *client, switch (msg->id) { case CRAS_SERVER_CONNECT_STREAM: { int client_shm_fd = num_fds > 1 ? fds[1] : -1; - struct cras_connect_message cmsg; if (MSG_LEN_VALID(msg, struct cras_connect_message)) { rclient_handle_client_stream_connect( client, (const struct cras_connect_message *)msg, fd, client_shm_fd); - } else if (!convert_connect_message_old(msg, &cmsg)) { - rclient_handle_client_stream_connect(client, &cmsg, fd, - client_shm_fd); } else { return -EINVAL; } diff --git a/cras/src/server/cras_rclient_util.h b/cras/src/server/cras_rclient_util.h index e00f87c9..089c2ecb 100644 --- a/cras/src/server/cras_rclient_util.h +++ b/cras/src/server/cras_rclient_util.h @@ -122,33 +122,4 @@ int rclient_handle_message_from_client(struct cras_rclient *client, const struct cras_server_message *msg, int *fds, unsigned int num_fds); -/* - * Converts an old version of connect message to the correct - * cras_connect_message. Returns zero on success, negative on failure. - * Note that this is special check only for libcras transition in - * clients, from CRAS_PROTO_VER 5 to 7. - * TODO(fletcherw): clean up the function once transition is done. - */ -static inline int -convert_connect_message_old(const struct cras_server_message *msg, - struct cras_connect_message *cmsg) -{ - struct cras_connect_message_old *old; - - if (!MSG_LEN_VALID(msg, struct cras_connect_message_old)) - return -EINVAL; - - old = (struct cras_connect_message_old *)msg; - if (old->proto_version != 5 || CRAS_PROTO_VER != 7) - return -EINVAL; - - // We want to copy everything except the client_shm_size field, since - // that overlaps slightly with the now larger client_shm_size. - memcpy(cmsg, old, sizeof(*old) - sizeof(old->client_shm_size)); - cmsg->client_shm_size = old->client_shm_size; - cmsg->buffer_offsets[0] = 0; - cmsg->buffer_offsets[1] = 0; - return 0; -} - #endif /* CRAS_RCLIENT_UTIL_H_ */ diff --git a/cras/src/server/cras_rstream.c b/cras/src/server/cras_rstream.c index b5a64901..94adcead 100644 --- a/cras/src/server/cras_rstream.c +++ b/cras/src/server/cras_rstream.c @@ -292,6 +292,7 @@ int cras_rstream_create(struct cras_rstream_config *config, stream->num_missed_cb = 0; stream->is_pinned = (config->dev_idx != NO_DEVICE); stream->pinned_dev_idx = config->dev_idx; + ewma_power_init(&stream->ewma, stream->format.frame_rate); rc = setup_shm_area(stream, config); if (rc < 0) { @@ -312,7 +313,7 @@ int cras_rstream_create(struct cras_rstream_config *config, config->stream_id, config->buffer_frames, config->cb_threshold); *stream_out = stream; - cras_system_state_stream_added(stream->direction); + cras_system_state_stream_added(stream->direction, stream->client_type); clock_gettime(CLOCK_MONOTONIC_RAW, &stream->start_ts); @@ -324,7 +325,8 @@ int cras_rstream_create(struct cras_rstream_config *config, void cras_rstream_destroy(struct cras_rstream *stream) { cras_server_metrics_stream_destroy(stream); - cras_system_state_stream_removed(stream->direction); + cras_system_state_stream_removed(stream->direction, + stream->client_type); close(stream->fd); cras_audio_shm_destroy(stream->shm); cras_audio_area_destroy(stream->audio_area); @@ -472,9 +474,16 @@ void cras_rstream_update_input_write_pointer(struct cras_rstream *rstream) void cras_rstream_update_output_read_pointer(struct cras_rstream *rstream) { + size_t nfr = 0; + uint8_t *src; unsigned int nwritten = buffer_share_get_new_write_point(rstream->buf_state); + /* Retrieve the read pointer |src| start from which to calculate + * the EWMA power. */ + src = cras_shm_get_readable_frames(rstream->shm, 0, &nfr); + ewma_power_calculate(&rstream->ewma, (int16_t *)src, + rstream->format.num_channels, nwritten); cras_shm_buffer_read(rstream->shm, nwritten); } diff --git a/cras/src/server/cras_rstream.h b/cras/src/server/cras_rstream.h index 059f2bb7..3bf7df0b 100644 --- a/cras/src/server/cras_rstream.h +++ b/cras/src/server/cras_rstream.h @@ -14,6 +14,7 @@ #include "cras_shm.h" #include "cras_types.h" #include "cras_rstream_config.h" +#include "ewma_power.h" struct cras_connect_message; struct cras_rclient; @@ -55,6 +56,7 @@ struct master_dev_info { * first_missed_cb_ts - The time when the first missed callback happens. * buf_state - State of the buffer from all devices for this stream. * apm_list - List of audio processing module instances. + * ewma - The ewma instance to calculate stream volume. * num_attached_devs - Number of iodevs this stream has attached to. * num_missed_cb - Number of callback schedules have been missed. * queued_frames - Cached value of the number of queued frames in shm. @@ -85,6 +87,7 @@ struct cras_rstream { struct timespec first_missed_cb_ts; struct buffer_share *buf_state; struct cras_apm_list *apm_list; + struct ewma_power ewma; int num_attached_devs; int num_missed_cb; int queued_frames; diff --git a/cras/src/server/cras_server_metrics.c b/cras/src/server/cras_server_metrics.c index 91556d86..ef4011bd 100644 --- a/cras/src/server/cras_server_metrics.c +++ b/cras/src/server/cras_server_metrics.c @@ -268,7 +268,7 @@ metrics_device_type_str(enum CRAS_METRICS_DEVICE_TYPE device_type) return "NoDevice"; case CRAS_METRICS_DEVICE_ALSA_LOOPBACK: return "AlsaLoopback"; - /* Other dummy devices. */ + /* Other fallback devices. */ case CRAS_METRICS_DEVICE_NORMAL_FALLBACK: return "NormalFallback"; case CRAS_METRICS_DEVICE_ABNORMAL_FALLBACK: diff --git a/cras/src/server/cras_system_state.c b/cras/src/server/cras_system_state.c index a5834197..331ecb11 100644 --- a/cras/src/server/cras_system_state.c +++ b/cras/src/server/cras_system_state.c @@ -156,6 +156,8 @@ void cras_system_state_init(const char *device_config_dir, const char *shm_name, exp_state->aec_supported = board_config.aec_supported; exp_state->aec_group_id = board_config.aec_group_id; exp_state->bt_wbs_enabled = board_config.bt_wbs_enabled; + exp_state->deprioritize_bt_wbs_mic = + board_config.deprioritize_bt_wbs_mic; if ((rc = pthread_mutex_init(&state.update_lock, 0) != 0)) { syslog(LOG_ERR, "Fatal: system state mutex init"); @@ -382,6 +384,11 @@ bool cras_system_get_bt_wbs_enabled() return !!state.exp_state->bt_wbs_enabled; } +bool cras_system_get_deprioritize_bt_wbs_mic() +{ + return !!state.exp_state->deprioritize_bt_wbs_mic; +} + void cras_system_set_bt_fix_a2dp_packet_size_enabled(bool enabled) { state.bt_fix_a2dp_packet_size = enabled; @@ -511,7 +518,8 @@ void cras_system_rm_select_fd(int fd) state.fd_rm(fd, state.select_data); } -void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction) +void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction, + enum CRAS_CLIENT_TYPE client_type) { struct cras_server_state *s; @@ -521,13 +529,19 @@ void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction) s->num_active_streams[direction]++; s->num_streams_attached++; + if (direction == CRAS_STREAM_INPUT) { + s->num_input_streams_with_permission[client_type]++; + cras_observer_notify_input_streams_with_permission( + s->num_input_streams_with_permission); + } cras_system_state_update_complete(); cras_observer_notify_num_active_streams( direction, s->num_active_streams[direction]); } -void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction) +void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction, + enum CRAS_CLIENT_TYPE client_type) { struct cras_server_state *s; unsigned i, sum; @@ -545,6 +559,11 @@ void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction) cras_clock_gettime(CLOCK_MONOTONIC_RAW, &s->last_active_stream_time); s->num_active_streams[direction]--; + if (direction == CRAS_STREAM_INPUT) { + s->num_input_streams_with_permission[client_type]--; + cras_observer_notify_input_streams_with_permission( + s->num_input_streams_with_permission); + } cras_system_state_update_complete(); cras_observer_notify_num_active_streams( @@ -566,6 +585,15 @@ unsigned cras_system_state_get_active_streams_by_direction( return state.exp_state->num_active_streams[direction]; } +void cras_system_state_get_input_streams_with_permission( + uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE]) +{ + unsigned type; + for (type = 0; type < CRAS_NUM_CLIENT_TYPE; ++type) + num_input_streams[type] = + state.exp_state->num_input_streams_with_permission[type]; +} + void cras_system_state_get_last_stream_active_time(struct cras_timespec *ts) { *ts = state.exp_state->last_active_stream_time; diff --git a/cras/src/server/cras_system_state.h b/cras/src/server/cras_system_state.h index 7f92625e..ff04606a 100644 --- a/cras/src/server/cras_system_state.h +++ b/cras/src/server/cras_system_state.h @@ -122,6 +122,12 @@ void cras_system_set_bt_wbs_enabled(bool enabled); /* Gets the elable flag of bluetooth wideband speech feature. */ bool cras_system_get_bt_wbs_enabled(); +/* + * Returns if Bluetooth WBS mic should be deprioritized for selecting + * as default audio input option. + */ +bool cras_system_get_deprioritize_bt_wbs_mic(); + /* Sets the flag to enable or disable Bluetooth fixed A2DP packet size. */ void cras_system_set_bt_fix_a2dp_packet_size_enabled(bool enabled); @@ -226,16 +232,20 @@ int cras_system_add_task(void (*callback)(void *data), void *callback_data); * subsystem is idle. * Args: * direction - Directions of audio streams. + * client_type - CRAS_CLIENT_TYPE of the audio stream. */ -void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction); +void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction, + enum CRAS_CLIENT_TYPE client_type); /* Signals that an audio input or output stream has been removed from the * system. This allows the count of active streams can be used to notice when * the audio subsystem is idle. * Args: * direction - Directions of audio stream. + * client_type - CRAS_CLIENT_TYPE of the audio stream. */ -void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction); +void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction, + enum CRAS_CLIENT_TYPE client_type); /* Returns the number of active playback and capture streams. */ unsigned cras_system_state_get_active_streams(); @@ -247,6 +257,16 @@ unsigned cras_system_state_get_active_streams(); unsigned cras_system_state_get_active_streams_by_direction( enum CRAS_STREAM_DIRECTION direction); +/* Returns the number of input streams with permission per CRAS_CLIENT_TYPE. + * + * Returns: + * num_input_streams - An array with length = CRAS_NUM_CLIENT_TYPE and each + * element is the number of the current input + * streams with permission in each client type. + */ +void cras_system_state_get_input_streams_with_permission( + uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE]); + /* Fills ts with the time the last stream was removed from the system, the time * the stream count went to zero. */ diff --git a/cras/src/server/dev_io.c b/cras/src/server/dev_io.c index 20b25f7e..42fe9558 100644 --- a/cras/src/server/dev_io.c +++ b/cras/src/server/dev_io.c @@ -126,6 +126,19 @@ static bool is_time_to_fetch(const struct dev_stream *dev_stream, return 0; } +/* The log only accepts uint32 arguments, so the float power + * must be written as bits and assumed to have a float when + * parsing the log. + */ +static uint32_t get_ewma_power_as_int(struct ewma_power *ewma) +{ + uint32_t pow_as_int = 0; + + if (sizeof(uint32_t) == sizeof(float)) + memcpy(&pow_as_int, &ewma->power, sizeof(uint32_t)); + return pow_as_int; +} + /* Asks any stream with room for more data. Sets the time stamp for all streams. * Args: * adev - The output device streams are attached to. @@ -194,7 +207,8 @@ static int fetch_streams(struct open_dev *adev) dev_stream_set_delay(dev_stream, delay); ATLOG(atlog, AUDIO_THREAD_FETCH_STREAM, rstream->stream_id, - cras_rstream_get_cb_threshold(rstream), delay); + cras_rstream_get_cb_threshold(rstream), + get_ewma_power_as_int(&rstream->ewma)); rc = dev_stream_request_playback_samples(dev_stream, &now); if (rc < 0) { @@ -550,7 +564,8 @@ static int capture_to_streams(struct open_dev *adev) break; } - ATLOG(atlog, AUDIO_THREAD_READ_AUDIO_DONE, remainder, 0, 0); + ATLOG(atlog, AUDIO_THREAD_READ_AUDIO_DONE, remainder, + get_ewma_power_as_int(&idev->ewma), 0); return 0; } @@ -733,7 +748,8 @@ int write_output_samples(struct open_dev **odevs, struct open_dev *adev, if (cras_iodev_update_rate(odev, hw_level, &hw_tstamp)) update_estimated_rate(adev); } - ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO, adev->dev->info.idx, hw_level, 0); + ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO, adev->dev->info.idx, hw_level, + odev->min_cb_level); /* Don't request more than hardware can hold. Note that min_buffer_level * has been subtracted from the actual hw_level so we need to take it @@ -797,7 +813,7 @@ int write_output_samples(struct open_dev **odevs, struct open_dev *adev, } ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO_DONE, hw_level, total_written, - odev->min_cb_level); + get_ewma_power_as_int(&odev->ewma)); return total_written; } diff --git a/cras/src/server/dev_stream.c b/cras/src/server/dev_stream.c index f0fcb71e..025aeddd 100644 --- a/cras/src/server/dev_stream.c +++ b/cras/src/server/dev_stream.c @@ -87,9 +87,12 @@ struct dev_stream *dev_stream_create(struct cras_rstream *stream, } else { /* * For input, take into account the stream specific processing - * like AEC. Use the post processing format to configure format - * converter. + * like AEC. APM exists only in input path, and has no dependency + * to dev_stream. Starts APM in dev_stream's constructor just to + * align with its life cycle, and then gets the post processing + * format to configure format converter. */ + cras_apm_list_start_apm(stream->apm_list, dev_ptr); ofmt = cras_rstream_post_processing_format(stream, dev_ptr) ?: dev_fmt, rc = config_format_converter(&out->conv, stream->direction, @@ -123,9 +126,8 @@ struct dev_stream *dev_stream_create(struct cras_rstream *stream, stream_fmt->frame_rate, &stream->sleep_interval_ts); stream->next_cb_ts = *cb_ts; - /* Sets up the stream & dev pair and then start APM. */ + /* Sets up the stream & dev pair. */ cras_rstream_dev_attach(stream, dev_id, dev_ptr); - cras_apm_list_start_apm(stream->apm_list, dev_ptr); return out; } diff --git a/cras/src/server/ewma_power.c b/cras/src/server/ewma_power.c new file mode 100644 index 00000000..5270ef0e --- /dev/null +++ b/cras/src/server/ewma_power.c @@ -0,0 +1,81 @@ +/* Copyright 2020 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "ewma_power.h" +#include "math.h" + +/* One sample per 1ms. */ +#define EWMA_SAMPLE_RATE 1000 + +/* Smooth factor for EWMA, 1 - expf(-1.0/(rate * 0.01)) + * where the 0.01 corresponds to 10ms interval that is chosen and + * being used in Chrome for a long time. + * Here the |rate| is set to the down sampled EWMA_SAMPLE_RATE and + * whenever it changes the calculated |smooth_factor| should be updated + * accordingly. + */ +const static float smooth_factor = 0.095; + +void ewma_power_disable(struct ewma_power *ewma) +{ + ewma->enabled = 0; +} + +void ewma_power_init(struct ewma_power *ewma, unsigned int rate) +{ + ewma->enabled = 1; + ewma->power_set = 0; + ewma->step_fr = rate / EWMA_SAMPLE_RATE; +} + +void ewma_power_calculate(struct ewma_power *ewma, const int16_t *buf, + unsigned int channels, unsigned int size) +{ + int i, ch; + float power, f; + + if (!ewma->enabled) + return; + for (i = 0; i < size; i += ewma->step_fr * channels) { + power = 0.0f; + for (ch = 0; ch < channels; ch++) { + f = buf[i + ch] / 32768.0f; + power += f * f / channels; + } + if (!ewma->power_set) { + ewma->power = power; + ewma->power_set = 1; + } else { + ewma->power = smooth_factor * power + + (1 - smooth_factor) * ewma->power; + } + } +} + +void ewma_power_calculate_area(struct ewma_power *ewma, const int16_t *buf, + struct cras_audio_area *area, unsigned int size) +{ + int i, ch; + float power, f; + + if (!ewma->enabled) + return; + for (i = 0; i < size; i += ewma->step_fr * area->num_channels) { + power = 0.0f; + for (ch = 0; ch < area->num_channels; ch++) { + if (area->channels[ch].ch_set == 0) + continue; + f = buf[i + ch] / 32768.0f; + power += f * f / area->num_channels; + } + if (!ewma->power_set) { + ewma->power = power; + ewma->power_set = 1; + } else { + ewma->power = smooth_factor * power + + (1 - smooth_factor) * ewma->power; + } + } +} diff --git a/cras/src/server/ewma_power.h b/cras/src/server/ewma_power.h new file mode 100644 index 00000000..78d2e504 --- /dev/null +++ b/cras/src/server/ewma_power.h @@ -0,0 +1,65 @@ +/* Copyright 2020 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef EWMA_POWER_H_ +#define EWMA_POWER_H_ + +#include <stdbool.h> +#include <stdint.h> + +#include "cras_audio_area.h" + +/* + * The exponentially weighted moving average power module used to + * calculate the energe level in audio stream. + * Members: + * power_set - Flag to note if the first power value has set. + * enabled - Flag to enable ewma calculation. Set to false to + * make all calculations no-ops. + * power - The power value. + * step_fr - How many frames to sample one for EWMA calculation. + */ +struct ewma_power { + bool power_set; + bool enabled; + float power; + unsigned int step_fr; +}; + +/* + * Disables the ewma instance. + */ +void ewma_power_disable(struct ewma_power *ewma); + +/* + * Initializes the ewma_power object. + * Args: + * ewma - The ewma_power object to initialize. + * rate - The sample rate of the audio data that the ewma object + * will calculate power from. + */ +void ewma_power_init(struct ewma_power *ewma, unsigned int rate); + +/* + * Feeds an audio buffer to ewma_power object to calculate the + * latest power value. + * Args: + * ewma - The ewma_power object to calculate power. + * buf - Pointer to the audio data. + * channels - Number of channels of the audio data. + * size - Length in frames of the audio data. + */ +void ewma_power_calculate(struct ewma_power *ewma, const int16_t *buf, + unsigned int channels, unsigned int size); + +/* + * Feeds non-interleaved audio data to ewma_power to calculate the + * latest power value. This is similar to ewma_power_calculate but + * accepts cras_audio_area. + */ +void ewma_power_calculate_area(struct ewma_power *ewma, const int16_t *buf, + struct cras_audio_area *area, unsigned int size); + +#endif /* EWMA_POWER_H_ */ diff --git a/cras/src/server/server_stream.c b/cras/src/server/server_stream.c index 48dd6a30..6644c469 100644 --- a/cras/src/server/server_stream.c +++ b/cras/src/server/server_stream.c @@ -16,18 +16,6 @@ static unsigned int server_stream_block_size = 480; /* - * Server stream doesn't care what format is used, because no - * client is reading data from stream. The main point is to - * make pinned device open and let data flow through its dsp - * pipeline. - */ -static struct cras_audio_format format = { - SND_PCM_FORMAT_S16_LE, - 48000, - 2, -}; - -/* * Information of a stream created by server. Currently only * one server stream is allowed, for echo reference use. */ @@ -45,7 +33,8 @@ static void server_stream_add_cb(void *data) stream_list_add(stream_list, stream_config, &stream); } -void server_stream_create(struct stream_list *stream_list, unsigned int dev_idx) +void server_stream_create(struct stream_list *stream_list, unsigned int dev_idx, + struct cras_audio_format *format) { int audio_fd = -1; int client_shm_fd = -1; @@ -64,7 +53,7 @@ void server_stream_create(struct stream_list *stream_list, unsigned int dev_idx) CRAS_STREAM_TYPE_DEFAULT, CRAS_CLIENT_TYPE_SERVER_STREAM, CRAS_STREAM_INPUT, dev_idx, /*flags=*/SERVER_ONLY, - /*effects=*/0, &format, server_stream_block_size, + /*effects=*/0, format, server_stream_block_size, server_stream_block_size, &audio_fd, &client_shm_fd, /*client_shm_size=*/0, buffer_offsets, stream_config); diff --git a/cras/src/server/server_stream.h b/cras/src/server/server_stream.h index 595987cb..e1eb8e10 100644 --- a/cras/src/server/server_stream.h +++ b/cras/src/server/server_stream.h @@ -14,8 +14,8 @@ struct stream_list; * stream_list - List of stream to add new server stream to. * dev_idx - The id of the device that new server stream will pin to. */ -void server_stream_create(struct stream_list *stream_list, - unsigned int dev_idx); +void server_stream_create(struct stream_list *stream_list, unsigned int dev_idx, + struct cras_audio_format *format); /* * Asynchronously destroys existing server stream pinned to device of given idx. diff --git a/cras/src/server/test_iodev.c b/cras/src/server/test_iodev.c index 266b62a8..cb7d5f3a 100644 --- a/cras/src/server/test_iodev.c +++ b/cras/src/server/test_iodev.c @@ -201,7 +201,7 @@ struct cras_iodev *test_iodev_create(enum CRAS_STREAM_DIRECTION direction, */ iodev->info.max_supported_channels = 1; - /* Create a dummy ionode */ + /* Create an empty ionode */ node = (struct cras_ionode *)calloc(1, sizeof(*node)); node->dev = iodev; node->plugged = 1; |