diff options
Diffstat (limited to 'cras/src/server/cras_iodev.c')
-rw-r--r-- | cras/src/server/cras_iodev.c | 1719 |
1 files changed, 0 insertions, 1719 deletions
diff --git a/cras/src/server/cras_iodev.c b/cras/src/server/cras_iodev.c deleted file mode 100644 index 651cef71..00000000 --- a/cras/src/server/cras_iodev.c +++ /dev/null @@ -1,1719 +0,0 @@ -/* Copyright (c) 2012 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 <pthread.h> -#include <stdbool.h> -#include <stdlib.h> -#include <sys/param.h> -#include <sys/time.h> -#include <syslog.h> -#include <time.h> - -#include "audio_thread.h" -#include "audio_thread_log.h" -#include "buffer_share.h" -#include "cras_audio_area.h" -#include "cras_audio_thread_monitor.h" -#include "cras_device_monitor.h" -#include "cras_dsp.h" -#include "cras_dsp_pipeline.h" -#include "cras_fmt_conv.h" -#include "cras_iodev.h" -#include "cras_main_thread_log.h" -#include "cras_iodev_list.h" -#include "cras_mix.h" -#include "cras_ramp.h" -#include "cras_rstream.h" -#include "cras_server_metrics.h" -#include "cras_system_state.h" -#include "cras_util.h" -#include "dev_stream.h" -#include "input_data.h" -#include "utlist.h" -#include "rate_estimator.h" -#include "softvol_curve.h" - -static const float RAMP_UNMUTE_DURATION_SECS = 0.5; -static const float RAMP_NEW_STREAM_DURATION_SECS = 0.01; -static const float RAMP_MUTE_DURATION_SECS = 0.1; -static const float RAMP_RESUME_MUTE_DURATION_SECS = 1; -static const float RAMP_SWITCH_MUTE_DURATION_SECS = 0.5; -static const float RAMP_VOLUME_CHANGE_DURATION_SECS = 0.1; - -/* - * It is the lastest time for the device to wake up when it is in the normal - * run state. It represents how many remaining frames in the device buffer. - */ -static const struct timespec dev_normal_run_wake_up_time = { - 0, 1 * 1000 * 1000 /* 1 msec. */ -}; - -/* - * It is the lastest time for the device to wake up when it is in the no stream - * state. It represents how many remaining frames in the device buffer. - */ -static const struct timespec dev_no_stream_wake_up_time = { - 0, 5 * 1000 * 1000 /* 5 msec. */ -}; - -/* - * Check issu b/72496547 and commit message for the history of - * rate estimator tuning. - */ -static const struct timespec rate_estimation_window_sz = { - 5, 0 /* 5 sec. */ -}; -static const double rate_estimation_smooth_factor = 0.3f; - -static void cras_iodev_alloc_dsp(struct cras_iodev *iodev); - -static int default_no_stream_playback(struct cras_iodev *odev) -{ - int rc; - unsigned int hw_level, fr_to_write; - unsigned int target_hw_level = odev->min_cb_level * 2; - struct timespec hw_tstamp; - - /* The default action for no stream playback is to fill zeros. */ - rc = cras_iodev_frames_queued(odev, &hw_tstamp); - if (rc < 0) - return rc; - hw_level = rc; - - /* If underrun happened, handle underrun and get hw_level again. */ - if (hw_level == 0) { - rc = cras_iodev_output_underrun(odev, hw_level, 0); - if (rc < 0) - return rc; - - rc = cras_iodev_frames_queued(odev, &hw_tstamp); - if (rc < 0) - return rc; - hw_level = rc; - } - - ATLOG(atlog, AUDIO_THREAD_ODEV_DEFAULT_NO_STREAMS, odev->info.idx, - hw_level, target_hw_level); - - fr_to_write = cras_iodev_buffer_avail(odev, hw_level); - if (hw_level <= target_hw_level) { - fr_to_write = MIN(target_hw_level - hw_level, fr_to_write); - return cras_iodev_fill_odev_zeros(odev, fr_to_write); - } - return 0; -} - -static int cras_iodev_start(struct cras_iodev *iodev) -{ - int rc; - if (!cras_iodev_is_open(iodev)) - return -EPERM; - if (!iodev->start) { - syslog(LOG_ERR, - "start called on device %s not supporting start ops", - iodev->info.name); - return -EINVAL; - } - rc = iodev->start(iodev); - if (rc) - return rc; - iodev->state = CRAS_IODEV_STATE_NORMAL_RUN; - return 0; -} - -/* Gets the number of frames ready for this device to play. - * It is the minimum number of available samples in dev_streams. - */ -static unsigned int dev_playback_frames(struct cras_iodev *odev) -{ - struct dev_stream *curr; - int frames = 0; - - DL_FOREACH (odev->streams, curr) { - int dev_frames; - - /* Skip stream which hasn't started running yet. */ - if (!dev_stream_is_running(curr)) - continue; - - /* If this is a single output dev stream, updates the latest - * number of frames for playback. */ - if (dev_stream_attached_devs(curr) == 1) - dev_stream_update_frames(curr); - - dev_frames = dev_stream_playback_frames(curr); - /* Do not handle stream error or end of draining in this - * function because they should be handled in write_streams. */ - if (dev_frames < 0) - continue; - if (!dev_frames) { - if (cras_rstream_get_is_draining(curr->stream)) - continue; - else - return 0; - } - if (frames == 0) - frames = dev_frames; - else - frames = MIN(dev_frames, frames); - } - return frames; -} - -/* Let device enter/leave no stream playback. - * Args: - * iodev[in] - The output device. - * enable[in] - 1 to enter no stream playback, 0 to leave. - * Returns: - * 0 on success. Negative error code on failure. - */ -static int cras_iodev_no_stream_playback_transition(struct cras_iodev *odev, - int enable) -{ - int rc; - - if (odev->direction != CRAS_STREAM_OUTPUT) - return -EINVAL; - - /* This function is for transition between normal run and - * no stream run state. - */ - if ((odev->state != CRAS_IODEV_STATE_NORMAL_RUN) && - (odev->state != CRAS_IODEV_STATE_NO_STREAM_RUN)) - return -EINVAL; - - if (enable) { - ATLOG(atlog, AUDIO_THREAD_ODEV_NO_STREAMS, odev->info.idx, 0, - 0); - } else { - ATLOG(atlog, AUDIO_THREAD_ODEV_LEAVE_NO_STREAMS, odev->info.idx, - 0, 0); - } - - rc = odev->no_stream(odev, enable); - if (rc < 0) - return rc; - if (enable) - odev->state = CRAS_IODEV_STATE_NO_STREAM_RUN; - else - odev->state = CRAS_IODEV_STATE_NORMAL_RUN; - return 0; -} - -/* Determines if the output device should mute. It considers system mute, - * system volume, and active node volume on the device. */ -static int output_should_mute(struct cras_iodev *odev) -{ - /* System mute has highest priority. */ - if (cras_system_get_mute()) - return 1; - - /* consider system volume and active node volume. */ - return cras_iodev_is_zero_volume(odev); -} - -int cras_iodev_is_zero_volume(const struct cras_iodev *odev) -{ - size_t system_volume; - unsigned int adjusted_node_volume; - - system_volume = cras_system_get_volume(); - if (odev->active_node) { - adjusted_node_volume = cras_iodev_adjust_node_volume( - odev->active_node, system_volume); - return (adjusted_node_volume == 0); - } - return (system_volume == 0); -} - -/* Output device state transition diagram: - * - * ---------------- - * -------------<-----------| S0 Closed |------<-------. - * | ---------------- | - * | | iodev_list enables | - * | | device and adds to | - * | V audio thread | iodev_list removes - * | ---------------- | device from - * | | S1 Open | | audio_thread and - * | ---------------- | closes device - * | Device with empty start | | - * | ops transits into | Sample is ready | - * | no stream state right V | - * | after open. ---------------- | - * | | S2 Normal | | - * | ---------------- | - * | | ^ | - * | There is no stream | | Sample is ready | - * | V | | - * | ---------------- | - * ------------->-----------| S3 No Stream |------->------ - * ---------------- - * - * Device in open_devs can be in one of S1, S2, S3. - * - * cras_iodev_output_event_sample_ready change device state from S1 or S3 into - * S2. - */ -static int cras_iodev_output_event_sample_ready(struct cras_iodev *odev) -{ - if (odev->state == CRAS_IODEV_STATE_OPEN || - odev->state == CRAS_IODEV_STATE_NO_STREAM_RUN) { - /* Starts ramping up if device should not be muted. - * Both mute and volume are taken into consideration. - */ - if (odev->ramp && !output_should_mute(odev)) { - cras_iodev_start_ramp(odev, odev->initial_ramp_request); - } - } - - if (odev->state == CRAS_IODEV_STATE_OPEN) { - /* S1 => S2: - * If device is not started yet, and there is sample ready from - * stream, fill 1 min_cb_level of zeros first and fill sample - * from stream later. - * Starts the device here to finish state transition. */ - cras_iodev_fill_odev_zeros(odev, odev->min_cb_level); - ATLOG(atlog, AUDIO_THREAD_ODEV_START, odev->info.idx, - odev->min_cb_level, 0); - return cras_iodev_start(odev); - } else if (odev->state == CRAS_IODEV_STATE_NO_STREAM_RUN) { - /* S3 => S2: - * Device in no stream state get sample ready. Leave no stream - * state and transit to normal run state.*/ - return cras_iodev_no_stream_playback_transition(odev, 0); - } else { - syslog(LOG_ERR, - "Device %s in state %d received sample ready event", - odev->info.name, odev->state); - return -EINVAL; - } - return 0; -} - -/* - * Exported Interface. - */ - -/* Finds the supported sample rate that best suits the requested rate, "rrate". - * Exact matches have highest priority, then integer multiples, then the default - * rate for the device. */ -static size_t get_best_rate(struct cras_iodev *iodev, size_t rrate) -{ - size_t i; - size_t best; - - if (iodev->supported_rates[0] == 0) /* No rates supported */ - return 0; - - for (i = 0, best = 0; iodev->supported_rates[i] != 0; i++) { - if (rrate == iodev->supported_rates[i] && rrate >= 44100) - return rrate; - if (best == 0 && (rrate % iodev->supported_rates[i] == 0 || - iodev->supported_rates[i] % rrate == 0)) - best = iodev->supported_rates[i]; - } - - if (best) - return best; - return iodev->supported_rates[0]; -} - -/* Finds the best match for the channel count. The following match rules - * will apply in order and return the value once matched: - * 1. Match the exact given channel count. - * 2. Match the preferred channel count. - * 3. The first channel count in the list. - */ -static size_t get_best_channel_count(struct cras_iodev *iodev, size_t count) -{ - static const size_t preferred_channel_count = 2; - size_t i; - - assert(iodev->supported_channel_counts[0] != 0); - - for (i = 0; iodev->supported_channel_counts[i] != 0; i++) { - if (iodev->supported_channel_counts[i] == count) - return count; - } - - /* If provided count is not supported, search for preferred - * channel count to which we're good at converting. - */ - for (i = 0; iodev->supported_channel_counts[i] != 0; i++) { - if (iodev->supported_channel_counts[i] == - preferred_channel_count) - return preferred_channel_count; - } - - return iodev->supported_channel_counts[0]; -} - -/* finds the best match for the current format. If no exact match is - * found, use the first. */ -static snd_pcm_format_t get_best_pcm_format(struct cras_iodev *iodev, - snd_pcm_format_t fmt) -{ - size_t i; - - for (i = 0; iodev->supported_formats[i] != 0; i++) { - if (fmt == iodev->supported_formats[i]) - return fmt; - } - - return iodev->supported_formats[0]; -} - -/* Applies the DSP to the samples for the iodev if applicable. */ -static int apply_dsp(struct cras_iodev *iodev, uint8_t *buf, size_t frames) -{ - struct cras_dsp_context *ctx; - struct pipeline *pipeline; - int rc; - - ctx = iodev->dsp_context; - if (!ctx) - return 0; - - pipeline = cras_dsp_get_pipeline(ctx); - if (!pipeline) - return 0; - - rc = cras_dsp_pipeline_apply(pipeline, buf, iodev->format->format, - frames); - - cras_dsp_put_pipeline(ctx); - return rc; -} - -static void cras_iodev_free_dsp(struct cras_iodev *iodev) -{ - if (iodev->dsp_context) { - cras_dsp_context_free(iodev->dsp_context); - iodev->dsp_context = NULL; - } -} - -/* Modifies the number of channels in device format to the one that will be - * presented to the device after any channel changes from the DSP. */ -static inline void adjust_dev_channel_for_dsp(const struct cras_iodev *iodev) -{ - struct cras_dsp_context *ctx = iodev->dsp_context; - - if (!ctx || !cras_dsp_get_pipeline(ctx)) - return; - - if (iodev->direction == CRAS_STREAM_OUTPUT) - iodev->format->num_channels = cras_dsp_num_output_channels(ctx); - else - iodev->format->num_channels = cras_dsp_num_input_channels(ctx); - - cras_dsp_put_pipeline(ctx); -} - -/* Updates channel layout based on the number of channels set by a - * client stream. Set a default value to format if the update call - * fails. - */ -static void update_channel_layout(struct cras_iodev *iodev) -{ - int rc; - - if (iodev->update_channel_layout == NULL) - return; - - rc = iodev->update_channel_layout(iodev); - if (rc < 0) - cras_audio_format_set_default_channel_layout(iodev->format); -} - -/* - * For the specified format, removes any channels from the channel layout that - * are higher than the supported number of channels. Should be used when the - * number of channels of the format been reduced. - */ -static void trim_channel_layout(struct cras_audio_format *fmt) -{ - int i; - for (i = 0; i < CRAS_CH_MAX; i++) - if (fmt->channel_layout[i] >= fmt->num_channels) - fmt->channel_layout[i] = -1; -} - -int cras_iodev_set_format(struct cras_iodev *iodev, - const struct cras_audio_format *fmt) -{ - size_t actual_rate, actual_num_channels; - snd_pcm_format_t actual_format; - int rc; - - /* Update supported formats on iodev before negotiating the final value - * with what stream requested. - */ - if (iodev->update_supported_formats) { - rc = iodev->update_supported_formats(iodev); - if (rc) { - syslog(LOG_ERR, "Failed to update formats"); - return rc; - } - } - - /* If this device isn't already using a format, try to match the one - * requested in "fmt". */ - if (iodev->format == NULL) { - iodev->format = malloc(sizeof(struct cras_audio_format)); - if (!iodev->format) - return -ENOMEM; - *iodev->format = *fmt; - - /* Finds the actual rate of device before allocating DSP - * because DSP needs to use the rate of device, not rate of - * stream. */ - actual_rate = get_best_rate(iodev, fmt->frame_rate); - iodev->format->frame_rate = actual_rate; - - cras_iodev_alloc_dsp(iodev); - cras_iodev_update_dsp(iodev); - if (iodev->dsp_context) - adjust_dev_channel_for_dsp(iodev); - - actual_num_channels = get_best_channel_count( - iodev, iodev->format->num_channels); - actual_format = get_best_pcm_format(iodev, fmt->format); - if (actual_rate == 0 || actual_num_channels == 0 || - actual_format == 0) { - /* No compatible frame rate found. */ - rc = -EINVAL; - goto error; - } - iodev->format->format = actual_format; - if (iodev->format->num_channels != actual_num_channels) { - /* If the DSP for this device doesn't match, drop it. */ - iodev->format->num_channels = actual_num_channels; - trim_channel_layout(iodev->format); - cras_iodev_free_dsp(iodev); - } - - update_channel_layout(iodev); - - if (!iodev->rate_est) - iodev->rate_est = rate_estimator_create( - actual_rate, &rate_estimation_window_sz, - rate_estimation_smooth_factor); - else - rate_estimator_reset_rate(iodev->rate_est, actual_rate); - } - - return 0; - -error: - free(iodev->format); - iodev->format = NULL; - return rc; -} - -/* - * Configures the external dsp module and adds it to the existing dsp pipeline. - */ -static void add_ext_dsp_module_to_pipeline(struct cras_iodev *iodev) -{ - struct pipeline *pipeline; - - pipeline = iodev->dsp_context ? - cras_dsp_get_pipeline(iodev->dsp_context) : - NULL; - - if (!pipeline) { - cras_iodev_alloc_dsp(iodev); - 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 - * pipeline resources. */ - - if (iodev->ext_dsp_module) - iodev->ext_dsp_module->configure(iodev->ext_dsp_module, - iodev->buffer_size, - iodev->format->num_channels, - iodev->format->frame_rate); - - cras_dsp_pipeline_set_sink_ext_module(pipeline, iodev->ext_dsp_module); - - /* Unlock dsp_context mutex. */ - cras_dsp_put_pipeline(iodev->dsp_context); -} - -/* - * Releases the ext_dsp_module if it ever added to iodev's dsp pipeline. - */ -static void release_ext_dsp_module_from_pipeline(struct cras_iodev *iodev) -{ - struct pipeline *pipeline; - - if (iodev->dsp_context == NULL) - return; - - pipeline = cras_dsp_get_pipeline(iodev->dsp_context); - if (pipeline == NULL) - return; - /* dsp_context mutex locked. */ - - cras_dsp_pipeline_set_sink_ext_module(pipeline, NULL); - - /* Unlock dsp_context mutex. */ - cras_dsp_put_pipeline(iodev->dsp_context); -} - -void cras_iodev_set_ext_dsp_module(struct cras_iodev *iodev, - struct ext_dsp_module *ext) -{ - iodev->ext_dsp_module = ext; - - if (!cras_iodev_is_open(iodev)) - return; - - if (iodev->ext_dsp_module) - add_ext_dsp_module_to_pipeline(iodev); - else - release_ext_dsp_module_from_pipeline(iodev); -} - -void cras_iodev_update_dsp(struct cras_iodev *iodev) -{ - char swap_lr_disabled = 1; - - if (!iodev->dsp_context) - return; - - cras_dsp_set_variable_string(iodev->dsp_context, "dsp_name", - iodev->dsp_name ?: ""); - - if (iodev->active_node && iodev->active_node->left_right_swapped) - swap_lr_disabled = 0; - - cras_dsp_set_variable_boolean(iodev->dsp_context, "swap_lr_disabled", - swap_lr_disabled); - - cras_dsp_load_pipeline(iodev->dsp_context); -} - -int cras_iodev_dsp_set_swap_mode_for_node(struct cras_iodev *iodev, - struct cras_ionode *node, int enable) -{ - if (node->left_right_swapped == enable) - return 0; - - /* Sets left_right_swapped property on the node. It will be used - * when cras_iodev_update_dsp is called. */ - node->left_right_swapped = enable; - - /* Possibly updates dsp if the node is active on the device and there - * is dsp context. If dsp context is not created yet, - * cras_iodev_update_dsp returns right away. */ - if (iodev->active_node == node) - cras_iodev_update_dsp(iodev); - return 0; -} - -void cras_iodev_free_format(struct cras_iodev *iodev) -{ - free(iodev->format); - iodev->format = NULL; -} - -void cras_iodev_init_audio_area(struct cras_iodev *iodev, int num_channels) -{ - if (iodev->area) - cras_iodev_free_audio_area(iodev); - - iodev->area = cras_audio_area_create(num_channels); - cras_audio_area_config_channels(iodev->area, iodev->format); -} - -void cras_iodev_free_audio_area(struct cras_iodev *iodev) -{ - if (!iodev->area) - return; - - cras_audio_area_destroy(iodev->area); - iodev->area = NULL; -} - -void cras_iodev_free_resources(struct cras_iodev *iodev) -{ - cras_iodev_free_dsp(iodev); - rate_estimator_destroy(iodev->rate_est); - if (iodev->ramp) - cras_ramp_destroy(iodev->ramp); -} - -static void cras_iodev_alloc_dsp(struct cras_iodev *iodev) -{ - const char *purpose; - - if (iodev->direction == CRAS_STREAM_OUTPUT) - purpose = "playback"; - else - purpose = "capture"; - - cras_iodev_free_dsp(iodev); - iodev->dsp_context = - cras_dsp_context_new(iodev->format->frame_rate, purpose); -} - -void cras_iodev_fill_time_from_frames(size_t frames, size_t frame_rate, - struct timespec *ts) -{ - uint64_t to_play_usec; - - ts->tv_sec = 0; - /* adjust sleep time to target our callback threshold */ - to_play_usec = (uint64_t)frames * 1000000L / (uint64_t)frame_rate; - - while (to_play_usec > 1000000) { - ts->tv_sec++; - to_play_usec -= 1000000; - } - ts->tv_nsec = to_play_usec * 1000; -} - -/* This is called when a node is plugged/unplugged */ -void cras_iodev_set_node_plugged(struct cras_ionode *node, int plugged) -{ - if (node->plugged == plugged) - return; - node->plugged = plugged; - MAINLOG(main_log, MAIN_THREAD_NODE_PLUGGED, node->dev->info.idx, - plugged, 0); - if (plugged) { - gettimeofday(&node->plugged_time, NULL); - } else if (node == node->dev->active_node) { - /* - * Remove normal and pinned streams, when node unplugged. - * TODO(hychao): clean this up, per crbug.com/1006646 - */ - cras_iodev_list_disable_dev(node->dev, true); - } - cras_iodev_list_notify_nodes_changed(); -} - -void cras_iodev_add_node(struct cras_iodev *iodev, struct cras_ionode *node) -{ - DL_APPEND(iodev->nodes, node); - cras_iodev_list_notify_nodes_changed(); -} - -void cras_iodev_rm_node(struct cras_iodev *iodev, struct cras_ionode *node) -{ - DL_DELETE(iodev->nodes, node); - cras_iodev_list_notify_nodes_changed(); -} - -void cras_iodev_set_active_node(struct cras_iodev *iodev, - struct cras_ionode *node) -{ - iodev->active_node = node; - cras_iodev_list_notify_active_node_changed(iodev->direction); -} - -bool cras_iodev_is_aec_use_case(const struct cras_ionode *node) -{ - if ((node->type == CRAS_NODE_TYPE_INTERNAL_SPEAKER) || - (node->type == CRAS_NODE_TYPE_ECHO_REFERENCE)) - return true; - - if (node->type == CRAS_NODE_TYPE_MIC) - return (node->position == NODE_POSITION_INTERNAL) || - (node->position == NODE_POSITION_FRONT); - - return false; -} - -bool cras_iodev_is_on_internal_card(const struct cras_ionode *node) -{ - if (node->type == CRAS_NODE_TYPE_INTERNAL_SPEAKER) - return true; - if (node->type == CRAS_NODE_TYPE_HEADPHONE) - return true; - if (node->type == CRAS_NODE_TYPE_MIC) - return true; - return false; -} - -float cras_iodev_get_software_volume_scaler(struct cras_iodev *iodev) -{ - unsigned int volume; - - volume = cras_iodev_adjust_active_node_volume(iodev, - cras_system_get_volume()); - - if (iodev->active_node && iodev->active_node->softvol_scalers) - return iodev->active_node->softvol_scalers[volume]; - return softvol_get_scaler(volume); -} - -float cras_iodev_get_software_gain_scaler(const struct cras_iodev *iodev) -{ - if (cras_iodev_software_volume_needed(iodev)) - return convert_softvol_scaler_from_dB( - iodev->active_node->capture_gain); - return 1.0f; -} - -int cras_iodev_get_valid_frames(struct cras_iodev *odev, - struct timespec *hw_tstamp) -{ - int rc; - - if (odev->direction != CRAS_STREAM_OUTPUT) - return -EINVAL; - - if (odev->get_valid_frames) { - rc = odev->get_valid_frames(odev, hw_tstamp); - if (rc < 0) - return rc; - - if (rc < odev->min_buffer_level) - return 0; - - return rc - odev->min_buffer_level; - } else { - return cras_iodev_frames_queued(odev, hw_tstamp); - } -} - -int cras_iodev_add_stream(struct cras_iodev *iodev, struct dev_stream *stream) -{ - /* - * For input stream, start stream right after adding stream. - * For output stream, start stream after its first fetch such that it does not - * block other existing streams. - */ - DL_APPEND(iodev->streams, stream); - if (!iodev->buf_state) - iodev->buf_state = buffer_share_create(iodev->buffer_size); - if (stream->stream->direction == CRAS_STREAM_INPUT) - cras_iodev_start_stream(iodev, stream); - return 0; -} - -void cras_iodev_start_stream(struct cras_iodev *iodev, - struct dev_stream *stream) -{ - unsigned int cb_threshold = dev_stream_cb_threshold(stream); - - if (dev_stream_is_running(stream)) - return; - /* - * TRIGGER_ONLY streams do not want to receive data, so do not add them - * to buffer_share, otherwise they'll affect other streams to receive. - */ - if (!(stream->stream->flags & TRIGGER_ONLY)) - buffer_share_add_id(iodev->buf_state, stream->stream->stream_id, - NULL); - iodev->min_cb_level = MIN(iodev->min_cb_level, cb_threshold); - iodev->max_cb_level = MAX(iodev->max_cb_level, cb_threshold); - iodev->largest_cb_level = MAX(iodev->largest_cb_level, cb_threshold); - dev_stream_set_running(stream); -} - -struct dev_stream *cras_iodev_rm_stream(struct cras_iodev *iodev, - const struct cras_rstream *rstream) -{ - struct dev_stream *out; - struct dev_stream *ret = NULL; - unsigned int cb_threshold; - struct timespec earliest_next_cb_ts; - int set_earliest = 0; - - iodev->min_cb_level = iodev->buffer_size / 2; - iodev->max_cb_level = 0; - DL_FOREACH (iodev->streams, out) { - if (out->stream == rstream) { - buffer_share_rm_id(iodev->buf_state, - rstream->stream_id); - ret = out; - DL_DELETE(iodev->streams, out); - continue; - } - if (!dev_stream_is_running(out)) - continue; - cb_threshold = dev_stream_cb_threshold(out); - iodev->min_cb_level = MIN(iodev->min_cb_level, cb_threshold); - iodev->max_cb_level = MAX(iodev->max_cb_level, cb_threshold); - if (!set_earliest) { - set_earliest = 1; - earliest_next_cb_ts = out->stream->next_cb_ts; - } - if (timespec_after(&earliest_next_cb_ts, - &out->stream->next_cb_ts)) - earliest_next_cb_ts = out->stream->next_cb_ts; - } - - if (!iodev->streams) { - buffer_share_destroy(iodev->buf_state); - iodev->buf_state = NULL; - iodev->min_cb_level = iodev->buffer_size / 2; - /* Let output device transit into no stream state if it's - * in normal run state now. Leave input device in normal - * run state. */ - if ((iodev->direction == CRAS_STREAM_OUTPUT) && - (iodev->state == CRAS_IODEV_STATE_NORMAL_RUN)) - cras_iodev_no_stream_playback_transition(iodev, 1); - } - - if (!set_earliest) - return ret; - - DL_FOREACH (iodev->streams, out) { - if (!dev_stream_is_running(out)) - out->stream->next_cb_ts = earliest_next_cb_ts; - } - - return ret; -} - -unsigned int cras_iodev_stream_offset(struct cras_iodev *iodev, - struct dev_stream *stream) -{ - return buffer_share_id_offset(iodev->buf_state, - stream->stream->stream_id); -} - -void cras_iodev_stream_written(struct cras_iodev *iodev, - struct dev_stream *stream, unsigned int nwritten) -{ - buffer_share_offset_update(iodev->buf_state, stream->stream->stream_id, - nwritten); -} - -unsigned int cras_iodev_all_streams_written(struct cras_iodev *iodev) -{ - if (!iodev->buf_state) - return 0; - return buffer_share_get_new_write_point(iodev->buf_state); -} - -unsigned int cras_iodev_max_stream_offset(const struct cras_iodev *iodev) -{ - unsigned int max = 0; - struct dev_stream *curr; - - DL_FOREACH (iodev->streams, curr) { - /* Skip stream which hasn't started running yet. */ - if (!dev_stream_is_running(curr)) - continue; - - max = MAX(max, buffer_share_id_offset(iodev->buf_state, - curr->stream->stream_id)); - } - - return max; -} - -int cras_iodev_open(struct cras_iodev *iodev, unsigned int cb_level, - const struct cras_audio_format *fmt) -{ - struct cras_loopback *loopback; - int rc; - - if (iodev->pre_open_iodev_hook) - iodev->pre_open_iodev_hook(); - - DL_FOREACH (iodev->loopbacks, loopback) { - if (loopback->hook_control) - loopback->hook_control(true, loopback->cb_data); - } - - if (iodev->open_dev) { - rc = iodev->open_dev(iodev); - if (rc) - return rc; - } - - if (iodev->format == NULL) { - rc = cras_iodev_set_format(iodev, fmt); - if (rc) { - iodev->close_dev(iodev); - return rc; - } - } - - rc = iodev->configure_dev(iodev); - if (rc < 0) { - iodev->close_dev(iodev); - return rc; - } - - /* - * Convert cb_level from input format to device format - */ - cb_level = cras_frames_at_rate(fmt->frame_rate, cb_level, - iodev->format->frame_rate); - /* Make sure the min_cb_level doesn't get too large. */ - iodev->min_cb_level = MIN(iodev->buffer_size / 2, cb_level); - iodev->max_cb_level = 0; - iodev->largest_cb_level = 0; - iodev->num_underruns = 0; - - iodev->reset_request_pending = 0; - iodev->state = CRAS_IODEV_STATE_OPEN; - 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. */ - if (iodev->start) { - iodev->state = CRAS_IODEV_STATE_OPEN; - } else { - iodev->state = CRAS_IODEV_STATE_NO_STREAM_RUN; - cras_iodev_fill_odev_zeros(iodev, iodev->min_cb_level); - } - } else { - iodev->input_data = input_data_create(iodev); - /* If this is the echo reference dev, its ext_dsp_module will - * be set to APM reverse module. Do not override it to its - * input data. */ - if (iodev->ext_dsp_module == NULL) - iodev->ext_dsp_module = &iodev->input_data->ext; - - /* Input device starts running right after opening. - * No stream state is only for output device. Input device - * should be in normal run state. */ - iodev->state = CRAS_IODEV_STATE_NORMAL_RUN; - /* Initialize the input_streaming flag to zero.*/ - iodev->input_streaming = 0; - - /* - * The device specific gain scaler to be used in audio thread. - * It's expected to stick to 1.0f if device has hardware gain - * control. For alsa device, this gain value will be configured - * based on UCM labels IntrinsicSensitivity. - */ - iodev->software_gain_scaler = - cras_iodev_get_software_gain_scaler(iodev); - } - - add_ext_dsp_module_to_pipeline(iodev); - clock_gettime(CLOCK_MONOTONIC_RAW, &iodev->open_ts); - - return 0; -} - -enum CRAS_IODEV_STATE cras_iodev_state(const struct cras_iodev *iodev) -{ - return iodev->state; -} - -int cras_iodev_close(struct cras_iodev *iodev) -{ - struct cras_loopback *loopback; - int rc; - - if (!cras_iodev_is_open(iodev)) - return 0; - - if (iodev->active_node) { - cras_server_metrics_device_runtime(iodev); - cras_server_metrics_device_gain(iodev); - cras_server_metrics_device_volume(iodev); - } - - if (iodev->input_data) { - if (iodev->ext_dsp_module == &iodev->input_data->ext) - iodev->ext_dsp_module = NULL; - input_data_destroy(&iodev->input_data); - } - - rc = iodev->close_dev(iodev); - if (rc) - syslog(LOG_ERR, "Error closing dev %s, rc %d", iodev->info.name, - rc); - iodev->state = CRAS_IODEV_STATE_CLOSE; - if (iodev->ramp) - cras_ramp_reset(iodev->ramp); - - if (iodev->post_close_iodev_hook) - iodev->post_close_iodev_hook(); - - DL_FOREACH (iodev->loopbacks, loopback) { - if (loopback->hook_control) - loopback->hook_control(false, loopback->cb_data); - } - - return 0; -} - -int cras_iodev_put_input_buffer(struct cras_iodev *iodev) -{ - unsigned int min_frames; - unsigned int dsp_frames; - struct input_data *data = iodev->input_data; - int rc; - - if (iodev->streams) - min_frames = buffer_share_get_new_write_point(iodev->buf_state); - else - min_frames = data->area->frames; - - // Update the max number of frames has applied input dsp. - dsp_frames = MAX(iodev->input_frames_read, iodev->input_dsp_offset); - iodev->input_dsp_offset = dsp_frames - min_frames; - - input_data_set_all_streams_read(data, min_frames); - rate_estimator_add_frames(iodev->rate_est, -min_frames); - rc = iodev->put_buffer(iodev, min_frames); - if (rc < 0) - return rc; - return min_frames; -} - -int cras_iodev_put_output_buffer(struct cras_iodev *iodev, uint8_t *frames, - unsigned int nframes, int *is_non_empty, - struct cras_fmt_conv *remix_converter) -{ - const struct cras_audio_format *fmt = iodev->format; - struct cras_ramp_action ramp_action = { - .type = CRAS_RAMP_ACTION_NONE, - .scaler = 0.0f, - .increment = 0.0f, - .target = 1.0f, - }; - float software_volume_scaler = 1.0; - int software_volume_needed = cras_iodev_software_volume_needed(iodev); - int rc; - struct cras_loopback *loopback; - - /* Calculate whether the final output was non-empty, if requested. */ - if (is_non_empty) { - const size_t bytes = nframes * cras_get_format_bytes(fmt); - - /* - * Speed up checking frames are all zeros using memcmp. - * frames contains all zeros if both conditions are met: - * - frames[0] is 0. - * - frames[i] == frames[i+1] for i in [0, 1, ..., bytes - 2]. - */ - *is_non_empty = bytes ? (*frames || memcmp(frames, frames + 1, - bytes - 1)) : - 0; - } - - DL_FOREACH (iodev->loopbacks, loopback) { - if (loopback->type == LOOPBACK_POST_MIX_PRE_DSP) - loopback->hook_data(frames, nframes, iodev->format, - 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; - - DL_FOREACH (iodev->loopbacks, loopback) { - if (loopback->type == LOOPBACK_POST_DSP) - loopback->hook_data(frames, nframes, iodev->format, - loopback->cb_data); - } - - if (iodev->ramp) { - ramp_action = cras_ramp_get_current_action(iodev->ramp); - } - - /* Mute samples if adjusted volume is 0 or system is muted, plus - * that this device is not ramping. */ - if (output_should_mute(iodev) && - ramp_action.type != CRAS_RAMP_ACTION_PARTIAL) { - const unsigned int frame_bytes = cras_get_format_bytes(fmt); - cras_mix_mute_buffer(frames, frame_bytes, nframes); - } - - /* Compute scaler for software volume if needed. */ - if (software_volume_needed) { - software_volume_scaler = - cras_iodev_get_software_volume_scaler(iodev); - } - - if (ramp_action.type == CRAS_RAMP_ACTION_PARTIAL) { - /* Scale with increment for ramp and possibly - * software volume using cras_scale_buffer_increment.*/ - float starting_scaler = ramp_action.scaler; - float increment = ramp_action.increment; - float target = ramp_action.target; - - if (software_volume_needed) { - starting_scaler *= software_volume_scaler; - increment *= software_volume_scaler; - target *= software_volume_scaler; - } - - cras_scale_buffer_increment(fmt->format, frames, nframes, - starting_scaler, increment, target, - fmt->num_channels); - cras_ramp_update_ramped_frames(iodev->ramp, nframes); - } else if (!output_should_mute(iodev) && software_volume_needed) { - /* Just scale for software volume using - * cras_scale_buffer. */ - unsigned int nsamples = nframes * fmt->num_channels; - cras_scale_buffer(fmt->format, frames, nsamples, - software_volume_scaler); - } - - if (remix_converter) - cras_channel_remix_convert(remix_converter, iodev->format, - frames, nframes); - if (iodev->rate_est) - rate_estimator_add_frames(iodev->rate_est, nframes); - - return iodev->put_buffer(iodev, nframes); -} - -int cras_iodev_get_input_buffer(struct cras_iodev *iodev, unsigned int *frames) -{ - const unsigned int frame_bytes = cras_get_format_bytes(iodev->format); - struct input_data *data = iodev->input_data; - int rc; - uint8_t *hw_buffer; - unsigned frame_requested = *frames; - - rc = iodev->get_buffer(iodev, &data->area, frames); - if (rc < 0 || *frames == 0) - return rc; - - if (*frames > frame_requested) { - syslog(LOG_ERR, - "frames returned from get_buffer is greater than " - "requested: %u > %u", - *frames, frame_requested); - return -EINVAL; - } - - iodev->input_frames_read = *frames; - - /* TODO(hychao) - This assumes interleaved audio. */ - hw_buffer = data->area->channels[0].buf; - - /* - * input_dsp_offset records the position where input dsp has applied to - * last time. It's possible the requested |frames| count is smaller - * than the tracked offset. That could happen when client stream uses - * small buffer size and runs APM processing (which requires 10 ms - * equivalent of data to process). - * Only apply input dsp to the part of read buffer beyond where we've - * already applied dsp. - */ - if (*frames > iodev->input_dsp_offset) { - rc = apply_dsp(iodev, - hw_buffer + - iodev->input_dsp_offset * frame_bytes, - *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()) - cras_mix_mute_buffer(hw_buffer, frame_bytes, *frames); - - return rc; -} - -int cras_iodev_get_output_buffer(struct cras_iodev *iodev, - struct cras_audio_area **area, - unsigned *frames) -{ - int rc; - unsigned frame_requested = *frames; - - rc = iodev->get_buffer(iodev, area, frames); - if (*frames > frame_requested) { - syslog(LOG_ERR, - "frames returned from get_buffer is greater than " - "requested: %u > %u", - *frames, frame_requested); - return -EINVAL; - } - return rc; -} - -int cras_iodev_update_rate(struct cras_iodev *iodev, unsigned int level, - struct timespec *level_tstamp) -{ - /* If output underruns, reset to avoid incorrect estimated rate. */ - if ((iodev->direction == CRAS_STREAM_OUTPUT) && !level) - rate_estimator_reset_rate(iodev->rate_est, - iodev->format->frame_rate); - - return rate_estimator_check(iodev->rate_est, level, level_tstamp); -} - -int cras_iodev_reset_rate_estimator(const struct cras_iodev *iodev) -{ - rate_estimator_reset_rate(iodev->rate_est, iodev->format->frame_rate); - return 0; -} - -double cras_iodev_get_est_rate_ratio(const struct cras_iodev *iodev) -{ - return rate_estimator_get_rate(iodev->rate_est) / - iodev->format->frame_rate; -} - -int cras_iodev_get_dsp_delay(const struct cras_iodev *iodev) -{ - struct cras_dsp_context *ctx; - struct pipeline *pipeline; - int delay; - - ctx = iodev->dsp_context; - if (!ctx) - return 0; - - pipeline = cras_dsp_get_pipeline(ctx); - if (!pipeline) - return 0; - - delay = cras_dsp_pipeline_get_delay(pipeline); - - cras_dsp_put_pipeline(ctx); - return delay; -} - -int cras_iodev_frames_queued(struct cras_iodev *iodev, - struct timespec *hw_tstamp) -{ - int rc; - - rc = iodev->frames_queued(iodev, hw_tstamp); - if (rc < 0) - return rc; - - if (iodev->direction == CRAS_STREAM_INPUT) { - if (rc > 0) - iodev->input_streaming = 1; - return rc; - } - - if (rc < iodev->min_buffer_level) - return 0; - - return rc - iodev->min_buffer_level; -} - -int cras_iodev_buffer_avail(struct cras_iodev *iodev, unsigned hw_level) -{ - if (iodev->direction == CRAS_STREAM_INPUT) - return hw_level; - - if (hw_level + iodev->min_buffer_level > iodev->buffer_size) - return 0; - - return iodev->buffer_size - iodev->min_buffer_level - hw_level; -} - -int cras_iodev_fill_odev_zeros(struct cras_iodev *odev, unsigned int frames) -{ - struct cras_audio_area *area = NULL; - unsigned int frame_bytes, frames_written; - int rc; - uint8_t *buf; - - if (odev->direction != CRAS_STREAM_OUTPUT) - return -EINVAL; - - ATLOG(atlog, AUDIO_THREAD_FILL_ODEV_ZEROS, odev->info.idx, frames, 0); - - frame_bytes = cras_get_format_bytes(odev->format); - while (frames > 0) { - frames_written = frames; - rc = cras_iodev_get_output_buffer(odev, &area, &frames_written); - if (rc < 0) { - syslog(LOG_ERR, "fill zeros fail: %d", rc); - return rc; - } - - /* This assumes consecutive channel areas. */ - buf = area->channels[0].buf; - memset(buf, 0, (size_t)frames_written * (size_t)frame_bytes); - cras_iodev_put_output_buffer(odev, buf, frames_written, NULL, - NULL); - frames -= frames_written; - } - - return 0; -} - -int cras_iodev_output_underrun(struct cras_iodev *odev, unsigned int hw_level, - unsigned int frames_written) -{ - ATLOG(atlog, AUDIO_THREAD_UNDERRUN, odev->info.idx, hw_level, - frames_written); - odev->num_underruns++; - cras_audio_thread_event_underrun(); - if (odev->output_underrun) - return odev->output_underrun(odev); - else - return cras_iodev_fill_odev_zeros(odev, odev->min_cb_level); -} - -int cras_iodev_odev_should_wake(const struct cras_iodev *odev) -{ - if (odev->direction != CRAS_STREAM_OUTPUT) - return 0; - - if (odev->is_free_running && odev->is_free_running(odev)) - return 0; - - /* Do not wake up for device not started yet. */ - return (odev->state == CRAS_IODEV_STATE_NORMAL_RUN || - odev->state == CRAS_IODEV_STATE_NO_STREAM_RUN); -} - -unsigned int -cras_iodev_default_frames_to_play_in_sleep(struct cras_iodev *odev, - unsigned int *hw_level, - struct timespec *hw_tstamp) -{ - int rc = cras_iodev_frames_queued(odev, hw_tstamp); - unsigned int level = (rc < 0) ? 0 : rc; - unsigned int wakeup_frames; - *hw_level = level; - - if (odev->streams) { - /* - * We have two cases in this scope. The first one is if there are frames - * waiting to be played, audio thread will wake up when hw_level drops - * to min_cb_level. This situation only happens when hardware buffer is - * smaller than the client stream buffer. The second one is waking up - * when hw_level drops to dev_normal_run_wake_up_time. It is a default - * behavior. This wake up time is the bottom line to avoid underrun. - * Normally, the audio thread does not wake up at that time because the - * streams should wake it up before then. - */ - if (*hw_level > odev->min_cb_level && dev_playback_frames(odev)) - return *hw_level - odev->min_cb_level; - - wakeup_frames = cras_time_to_frames( - &dev_normal_run_wake_up_time, odev->format->frame_rate); - if (level > wakeup_frames) - return level - wakeup_frames; - else - return level; - } - - /* - * When this device has no stream, schedule audio thread to wake up when - * hw_level drops to dev_no_stream_wake_up_time so audio thread can - * fill zeros to it. We also need to consider min_cb_level in order to avoid - * busyloop when device buffer size is smaller than wake up time. - */ - wakeup_frames = cras_time_to_frames(&dev_no_stream_wake_up_time, - odev->format->frame_rate); - if (level > MIN(odev->min_cb_level, wakeup_frames)) - return level - MIN(odev->min_cb_level, wakeup_frames); - else - return 0; -} - -unsigned int cras_iodev_frames_to_play_in_sleep(struct cras_iodev *odev, - unsigned int *hw_level, - struct timespec *hw_tstamp) -{ - /* Use odev's own implementation, if not supported then fall back - * to default behavior below. */ - if (odev->frames_to_play_in_sleep) - return odev->frames_to_play_in_sleep(odev, hw_level, hw_tstamp); - else - return cras_iodev_default_frames_to_play_in_sleep( - odev, hw_level, hw_tstamp); -} - -int cras_iodev_default_no_stream_playback(struct cras_iodev *odev, int enable) -{ - if (enable) - return default_no_stream_playback(odev); - return 0; -} - -int cras_iodev_prepare_output_before_write_samples(struct cras_iodev *odev) -{ - int may_enter_normal_run; - enum CRAS_IODEV_STATE state; - - if (odev->direction != CRAS_STREAM_OUTPUT) - return -EINVAL; - - state = cras_iodev_state(odev); - - may_enter_normal_run = (state == CRAS_IODEV_STATE_OPEN || - state == CRAS_IODEV_STATE_NO_STREAM_RUN); - - if (may_enter_normal_run && dev_playback_frames(odev)) - return cras_iodev_output_event_sample_ready(odev); - - /* no_stream ops is called every cycle in no_stream state. */ - if (state == CRAS_IODEV_STATE_NO_STREAM_RUN) - return odev->no_stream(odev, 1); - - return 0; -} - -unsigned int cras_iodev_get_num_underruns(const struct cras_iodev *iodev) -{ - return iodev->num_underruns; -} - -unsigned int cras_iodev_get_num_severe_underruns(const struct cras_iodev *iodev) -{ - if (iodev->get_num_severe_underruns) - return iodev->get_num_severe_underruns(iodev); - return 0; -} - -int cras_iodev_reset_request(struct cras_iodev *iodev) -{ - /* Ignore requests if there is a pending request. - * This function sends the request from audio thread to main - * thread when audio thread finds a device is in a bad state - * e.g. severe underrun. Before main thread receives the - * request and resets device, audio thread might try to send - * multiple requests because it finds device is still in bad - * state. We should ignore requests in this cause. Otherwise, - * main thread will reset device multiple times. - * The flag is cleared in cras_iodev_open. - * */ - if (iodev->reset_request_pending) - return 0; - iodev->reset_request_pending = 1; - return cras_device_monitor_reset_device(iodev->info.idx); -} - -static void ramp_down_mute_callback(void *data) -{ - struct cras_iodev *odev = (struct cras_iodev *)data; - cras_device_monitor_set_device_mute_state(odev->info.idx); -} - -/* Used in audio thread. Check the docstrings of CRAS_IODEV_RAMP_REQUEST. */ -int cras_iodev_start_ramp(struct cras_iodev *odev, - enum CRAS_IODEV_RAMP_REQUEST request) -{ - cras_ramp_cb cb = NULL; - void *cb_data = NULL; - int rc; - float from, to, duration_secs; - - /* Ignores request if device is closed. */ - if (!cras_iodev_is_open(odev)) - return 0; - - switch (request) { - case CRAS_IODEV_RAMP_REQUEST_UP_UNMUTE: - from = 0.0; - to = 1.0; - duration_secs = RAMP_UNMUTE_DURATION_SECS; - break; - case CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK: - from = 0.0; - to = 1.0; - duration_secs = RAMP_NEW_STREAM_DURATION_SECS; - break; - /* Unmute -> mute. Callback to set mute state should be called after - * ramping is done. */ - case CRAS_IODEV_RAMP_REQUEST_DOWN_MUTE: - from = 1.0; - to = 0.0; - duration_secs = RAMP_MUTE_DURATION_SECS; - cb = ramp_down_mute_callback; - cb_data = (void *)odev; - break; - case CRAS_IODEV_RAMP_REQUEST_RESUME_MUTE: - from = 0; - to = 0; - duration_secs = RAMP_RESUME_MUTE_DURATION_SECS; - odev->initial_ramp_request = - CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK; - break; - case CRAS_IODEV_RAMP_REQUEST_SWITCH_MUTE: - from = 0; - to = 0; - duration_secs = RAMP_SWITCH_MUTE_DURATION_SECS; - odev->initial_ramp_request = - CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK; - break; - default: - return -EINVAL; - } - - /* Starts ramping. */ - rc = cras_mute_ramp_start(odev->ramp, from, to, - duration_secs * odev->format->frame_rate, cb, - cb_data); - - if (rc) - return rc; - - /* Mute -> unmute case, unmute state should be set after ramping is - * started so device can start playing with samples close to 0. */ - if (request == CRAS_IODEV_RAMP_REQUEST_UP_UNMUTE) - cras_device_monitor_set_device_mute_state(odev->info.idx); - - return 0; -} - -int cras_iodev_start_volume_ramp(struct cras_iodev *odev, - unsigned int old_volume, - unsigned int new_volume) -{ - float old_scaler, new_scaler; - float from, to; - - if (old_volume == new_volume) - return 0; - if (!cras_iodev_is_open(odev)) - return 0; - if (!odev->format) - return -EINVAL; - if (odev->active_node && odev->active_node->softvol_scalers) { - old_scaler = odev->active_node->softvol_scalers[old_volume]; - new_scaler = odev->active_node->softvol_scalers[new_volume]; - } else { - old_scaler = softvol_get_scaler(old_volume); - new_scaler = softvol_get_scaler(new_volume); - } - if (new_scaler == 0.0) { - return -EINVAL; - } - /* We will soon set odev's volume to new_volume from old_volume. - * Because we're using softvol, we were previously scaling our volume by - * old_scaler. If we want to avoid a jump in volume, we need to start - * our ramp so that (from * new_scaler) = old_scaler. */ - from = old_scaler / new_scaler; - to = 1.0; - - return cras_volume_ramp_start(odev->ramp, from, to, - RAMP_VOLUME_CHANGE_DURATION_SECS * - odev->format->frame_rate, - NULL, NULL); -} - -int cras_iodev_set_mute(struct cras_iodev *iodev) -{ - if (!cras_iodev_is_open(iodev)) - return 0; - - if (iodev->set_mute) - iodev->set_mute(iodev); - return 0; -} - -void cras_iodev_update_highest_hw_level(struct cras_iodev *iodev, - unsigned int hw_level) -{ - /* - * If the hw_level is unreasonably high and reach to the device's - * buffer size, regard it as a device overrun. - * In the normal status, the hw_level for should be between 1 to 2 - * largest_cb_level for an output device and 0 to 1 largest_cb_level - * for an input device. Therefore, larger than 3 can be considered - * unreasonable. - */ - if (hw_level == iodev->buffer_size && - iodev->largest_cb_level * 3 < iodev->buffer_size) { - ATLOG(atlog, AUDIO_THREAD_DEV_OVERRUN, iodev->info.idx, - hw_level, 0); - /* Only log the event when the first time it happens. */ - if (iodev->highest_hw_level != hw_level) - cras_audio_thread_event_dev_overrun(); - } - iodev->highest_hw_level = MAX(iodev->highest_hw_level, hw_level); -} - -/* - * Makes an input device drop the given number of frames. - * Args: - * iodev - The device. - * frames - How many frames will be dropped in a device. - * Returns: - * The number of frames have been dropped. Negative error code on failure. - */ -static int cras_iodev_drop_frames(struct cras_iodev *iodev, unsigned int frames) -{ - struct timespec hw_tstamp; - int i, rc; - unsigned int target_frames, dropped_frames = 0; - - if (iodev->direction != CRAS_STREAM_INPUT) - return -EINVAL; - - rc = cras_iodev_frames_queued(iodev, &hw_tstamp); - if (rc < 0) - return rc; - - target_frames = MIN(frames, rc); - - /* - * Loop reading the buffer, at most twice. This is to cover when - * circular buffer is at the end and returns partial of the target - * frames. - */ - for (i = 0; (dropped_frames < target_frames) && (i < 2); i++) { - frames = target_frames - dropped_frames; - rc = iodev->get_buffer(iodev, &iodev->input_data->area, - &frames); - if (rc < 0) - return rc; - - rc = iodev->put_buffer(iodev, frames); - if (rc < 0) - return rc; - dropped_frames += frames; - /* - * Tell rate estimator that some frames have been dropped to - * avoid calculating the wrong rate. - */ - rate_estimator_add_frames(iodev->rate_est, -frames); - } - - ATLOG(atlog, AUDIO_THREAD_DEV_DROP_FRAMES, iodev->info.idx, - dropped_frames, 0); - - return frames; -} - -int cras_iodev_drop_frames_by_time(struct cras_iodev *iodev, struct timespec ts) -{ - int frames_to_set; - double est_rate; - int rc; - - est_rate = iodev->format->frame_rate * - cras_iodev_get_est_rate_ratio(iodev); - frames_to_set = cras_time_to_frames(&ts, est_rate); - - rc = cras_iodev_drop_frames(iodev, frames_to_set); - - return rc; -} - -bool cras_iodev_support_noise_cancellation(const struct cras_iodev *iodev) -{ - if (iodev->direction != CRAS_STREAM_INPUT) - return false; - - if (iodev->support_noise_cancellation) - return !!iodev->support_noise_cancellation(iodev); - return false; -} |