| /* 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 <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_device_monitor.h" |
| #include "cras_dsp.h" |
| #include "cras_dsp_pipeline.h" |
| #include "cras_fmt_conv.h" |
| #include "cras_iodev.h" |
| #include "cras_iodev_list.h" |
| #include "cras_mix.h" |
| #include "cras_ramp.h" |
| #include "cras_rstream.h" |
| #include "cras_system_state.h" |
| #include "cras_util.h" |
| #include "dev_stream.h" |
| #include "utlist.h" |
| #include "rate_estimator.h" |
| #include "softvol_curve.h" |
| |
| static const float RAMP_DURATION_SEC = 0.5; |
| |
| static const struct timespec rate_estimation_window_sz = { |
| 20, 0 /* 20 sec. */ |
| }; |
| static const double rate_estimation_smooth_factor = 0.9f; |
| |
| 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; |
| |
| 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; |
| |
| /* 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 dummy 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, |
| CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK); |
| } |
| |
| 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]; |
| } |
| |
| /* Set default channel count and layout to an iodev. |
| * iodev->format->num_channels is from get_best_channel_count. |
| */ |
| static void set_default_channel_count_layout(struct cras_iodev *iodev) |
| { |
| int8_t default_layout[CRAS_CH_MAX]; |
| size_t i; |
| |
| for (i = 0; i < CRAS_CH_MAX; i++) |
| default_layout[i] = i < iodev->format->num_channels ? i : -1; |
| |
| iodev->ext_format->num_channels = iodev->format->num_channels; |
| cras_audio_format_set_channel_layout(iodev->format, default_layout); |
| cras_audio_format_set_channel_layout(iodev->ext_format, default_layout); |
| } |
| |
| /* Applies the DSP to the samples for the iodev if applicable. */ |
| static void apply_dsp(struct cras_iodev *iodev, uint8_t *buf, size_t frames) |
| { |
| struct cras_dsp_context *ctx; |
| struct pipeline *pipeline; |
| |
| ctx = iodev->dsp_context; |
| if (!ctx) |
| return; |
| |
| pipeline = cras_dsp_get_pipeline(ctx); |
| if (!pipeline) |
| return; |
| |
| cras_dsp_pipeline_apply(pipeline, |
| buf, |
| frames); |
| |
| cras_dsp_put_pipeline(ctx); |
| } |
| |
| 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); |
| iodev->ext_format->num_channels = |
| cras_dsp_num_input_channels(ctx); |
| } else { |
| iodev->format->num_channels = |
| cras_dsp_num_input_channels(ctx); |
| iodev->ext_format->num_channels = |
| cras_dsp_num_output_channels(ctx); |
| } |
| |
| cras_dsp_put_pipeline(ctx); |
| } |
| |
| /* Updates channel layout based on the number of channels set by a |
| * client stream. When successful we need to update the new channel |
| * layout to ext_format, otherwise we should set a default value |
| * to both format and ext_format. |
| */ |
| 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) { |
| set_default_channel_count_layout(iodev); |
| } else { |
| cras_audio_format_set_channel_layout( |
| iodev->ext_format, |
| iodev->format->channel_layout); |
| } |
| } |
| |
| 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; |
| |
| /* 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)); |
| iodev->ext_format = malloc(sizeof(struct cras_audio_format)); |
| if (!iodev->format || !iodev->ext_format) |
| return -ENOMEM; |
| *iodev->format = *fmt; |
| *iodev->ext_format = *fmt; |
| |
| if (iodev->update_supported_formats) { |
| rc = iodev->update_supported_formats(iodev); |
| if (rc) { |
| syslog(LOG_ERR, "Failed to update formats"); |
| goto error; |
| } |
| } |
| |
| /* 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; |
| iodev->ext_format->frame_rate = actual_rate; |
| |
| cras_iodev_alloc_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; |
| iodev->ext_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; |
| iodev->ext_format->num_channels = actual_num_channels; |
| 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); |
| free(iodev->ext_format); |
| iodev->format = NULL; |
| iodev->ext_format = NULL; |
| return rc; |
| } |
| |
| 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); |
| free(iodev->ext_format); |
| iodev->format = NULL; |
| iodev->ext_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); |
| cras_iodev_update_dsp(iodev); |
| } |
| |
| 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 */ |
| static void plug_node(struct cras_ionode *node, int plugged) |
| { |
| if (node->plugged == plugged) |
| return; |
| node->plugged = plugged; |
| if (plugged) { |
| gettimeofday(&node->plugged_time, NULL); |
| } else if (node == node->dev->active_node) { |
| cras_iodev_list_disable_dev(node->dev); |
| } |
| cras_iodev_list_notify_nodes_changed(); |
| } |
| |
| static void set_node_volume(struct cras_ionode *node, int value) |
| { |
| struct cras_iodev *dev = node->dev; |
| unsigned int volume; |
| |
| if (dev->direction != CRAS_STREAM_OUTPUT) |
| return; |
| |
| volume = (unsigned int)MIN(value, 100); |
| node->volume = volume; |
| if (dev->set_volume) |
| dev->set_volume(dev); |
| |
| cras_iodev_list_notify_node_volume(node); |
| } |
| |
| static void set_node_capture_gain(struct cras_ionode *node, int value) |
| { |
| struct cras_iodev *dev = node->dev; |
| |
| if (dev->direction != CRAS_STREAM_INPUT) |
| return; |
| |
| node->capture_gain = (long)value; |
| if (dev->set_capture_gain) |
| dev->set_capture_gain(dev); |
| |
| cras_iodev_list_notify_node_capture_gain(node); |
| } |
| |
| static void set_node_left_right_swapped(struct cras_ionode *node, int value) |
| { |
| struct cras_iodev *dev = node->dev; |
| int rc; |
| |
| if (!dev->set_swap_mode_for_node) |
| return; |
| rc = dev->set_swap_mode_for_node(dev, node, value); |
| if (rc) { |
| syslog(LOG_ERR, |
| "Failed to set swap mode on node %s to %d; error %d", |
| node->name, value, rc); |
| return; |
| } |
| node->left_right_swapped = value; |
| cras_iodev_list_notify_node_left_right_swapped(node); |
| return; |
| } |
| |
| int cras_iodev_set_node_attr(struct cras_ionode *ionode, |
| enum ionode_attr attr, int value) |
| { |
| switch (attr) { |
| case IONODE_ATTR_PLUGGED: |
| plug_node(ionode, value); |
| break; |
| case IONODE_ATTR_VOLUME: |
| set_node_volume(ionode, value); |
| break; |
| case IONODE_ATTR_CAPTURE_GAIN: |
| set_node_capture_gain(ionode, value); |
| break; |
| case IONODE_ATTR_SWAP_LEFT_RIGHT: |
| set_node_left_right_swapped(ionode, value); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| 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); |
| } |
| |
| 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) { |
| float scaler = 1.0f; |
| if (cras_iodev_software_volume_needed(iodev)) { |
| long gain = cras_iodev_adjust_active_node_gain( |
| iodev, cras_system_get_capture_gain()); |
| scaler = convert_softvol_scaler_from_dB(gain); |
| } |
| return scaler; |
| } |
| |
| int cras_iodev_add_stream(struct cras_iodev *iodev, |
| struct dev_stream *stream) |
| { |
| unsigned int cb_threshold = dev_stream_cb_threshold(stream); |
| DL_APPEND(iodev->streams, stream); |
| |
| if (!iodev->buf_state) |
| iodev->buf_state = buffer_share_create(iodev->buffer_size); |
| 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); |
| return 0; |
| } |
| |
| 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; |
| unsigned int old_min_cb_level = iodev->min_cb_level; |
| |
| 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; |
| } |
| 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 (!iodev->streams) { |
| buffer_share_destroy(iodev->buf_state); |
| iodev->buf_state = NULL; |
| iodev->min_cb_level = old_min_cb_level; |
| /* 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); |
| } |
| 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) { |
| 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) |
| { |
| int rc; |
| |
| rc = iodev->open_dev(iodev); |
| if (rc < 0) |
| return rc; |
| |
| /* 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->reset_request_pending = 0; |
| iodev->state = CRAS_IODEV_STATE_OPEN; |
| |
| 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; |
| } else { |
| /* 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; |
| } |
| |
| 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) |
| { |
| int rc; |
| if (!cras_iodev_is_open(iodev)) |
| return 0; |
| |
| rc = iodev->close_dev(iodev); |
| if (rc) |
| return rc; |
| iodev->state = CRAS_IODEV_STATE_CLOSE; |
| if (iodev->ramp) |
| cras_ramp_reset(iodev->ramp); |
| return 0; |
| } |
| |
| int cras_iodev_put_input_buffer(struct cras_iodev *iodev, unsigned int nframes) |
| { |
| rate_estimator_add_frames(iodev->rate_est, -nframes); |
| return iodev->put_buffer(iodev, nframes); |
| } |
| |
| int cras_iodev_put_output_buffer(struct cras_iodev *iodev, uint8_t *frames, |
| unsigned int nframes) |
| { |
| const struct cras_audio_format *fmt = iodev->format; |
| struct cras_fmt_conv * remix_converter = |
| audio_thread_get_global_remix_converter(); |
| struct cras_ramp_action ramp_action = { |
| .type = CRAS_RAMP_ACTION_NONE, |
| .scaler = 0.0f, |
| .increment = 0.0f, |
| }; |
| float software_volume_scaler; |
| int software_volume_needed = cras_iodev_software_volume_needed(iodev); |
| |
| if (iodev->pre_dsp_hook) |
| iodev->pre_dsp_hook(frames, nframes, iodev->ext_format, |
| iodev->pre_dsp_hook_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); |
| } else { |
| apply_dsp(iodev, frames, nframes); |
| |
| if (iodev->post_dsp_hook) |
| iodev->post_dsp_hook(frames, nframes, fmt, |
| iodev->post_dsp_hook_cb_data); |
| |
| /* 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; |
| |
| if (software_volume_needed) { |
| starting_scaler *= software_volume_scaler; |
| increment *= software_volume_scaler; |
| } |
| |
| cras_scale_buffer_increment( |
| fmt->format, frames, nframes, |
| starting_scaler, increment, |
| fmt->num_channels); |
| cras_ramp_update_ramped_frames(iodev->ramp, nframes); |
| } else if (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); |
| rate_estimator_add_frames(iodev->rate_est, nframes); |
| return iodev->put_buffer(iodev, nframes); |
| } |
| |
| int cras_iodev_get_input_buffer(struct cras_iodev *iodev, |
| struct cras_audio_area **area, |
| unsigned *frames) |
| { |
| const struct cras_audio_format *fmt = iodev->format; |
| const unsigned int frame_bytes = cras_get_format_bytes(fmt); |
| uint8_t *hw_buffer; |
| int rc; |
| unsigned frame_requested = *frames; |
| |
| rc = iodev->get_buffer(iodev, 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; |
| } |
| |
| /* TODO(dgreid) - This assumes interleaved audio. */ |
| hw_buffer = (*area)->channels[0].buf; |
| |
| if (cras_system_get_capture_mute()) |
| cras_mix_mute_buffer(hw_buffer, frame_bytes, *frames); |
| else |
| apply_dsp(iodev, hw_buffer, *frames); /* TODO-applied 2x */ |
| |
| 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) |
| { |
| 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->ext_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->ext_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 || iodev->direction == CRAS_STREAM_INPUT) |
| 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; |
| } |
| |
| void cras_iodev_register_pre_dsp_hook(struct cras_iodev *iodev, |
| loopback_hook_t loop_cb, |
| void *cb_data) |
| { |
| iodev->pre_dsp_hook = loop_cb; |
| iodev->pre_dsp_hook_cb_data = cb_data; |
| } |
| |
| void cras_iodev_register_post_dsp_hook(struct cras_iodev *iodev, |
| loopback_hook_t loop_cb, |
| void *cb_data) |
| { |
| iodev->post_dsp_hook = loop_cb; |
| iodev->post_dsp_hook_cb_data = cb_data; |
| } |
| |
| 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->ext_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, frames_written * frame_bytes); |
| cras_iodev_put_output_buffer(odev, buf, frames_written); |
| frames -= frames_written; |
| } |
| |
| return 0; |
| } |
| |
| int cras_iodev_output_underrun(struct cras_iodev *odev) { |
| 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->output_should_wake) |
| return odev->output_should_wake(odev); |
| |
| /* 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_frames_to_play_in_sleep(struct cras_iodev *odev, |
| unsigned int *hw_level, |
| struct timespec *hw_tstamp) |
| { |
| int rc; |
| |
| rc = cras_iodev_frames_queued(odev, hw_tstamp); |
| *hw_level = (rc < 0) ? 0 : rc; |
| |
| if (odev->streams) { |
| /* Schedule that audio thread will wake up when |
| * hw_level drops to 0. |
| * This should not cause underrun because audio thread |
| * should be waken up by the reply from client. */ |
| return *hw_level; |
| } |
| /* When this device has no stream, schedule audio thread to wake up |
| * when hw_level drops to min_cb_level so audio thread can fill |
| * zeros to it. */ |
| if (*hw_level > odev->min_cb_level) |
| return *hw_level - odev->min_cb_level; |
| else |
| return 0; |
| } |
| |
| 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) |
| { |
| if (iodev->get_num_underruns) |
| return iodev->get_num_underruns(iodev); |
| return 0; |
| } |
| |
| 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); |
| } |
| |
| static void ramp_mute_callback(void *data) |
| { |
| struct cras_iodev *odev = (struct cras_iodev *)data; |
| cras_device_monitor_set_device_mute_state(odev); |
| } |
| |
| /* 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, up; |
| |
| /* Ignores request if device is closed. */ |
| if (!cras_iodev_is_open(odev)) |
| return 0; |
| |
| switch (request) { |
| case CRAS_IODEV_RAMP_REQUEST_UP_UNMUTE: |
| case CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK: |
| up = 1; |
| break; |
| /* Unmute -> mute. Callback to set mute state should be called after |
| * ramping is done. */ |
| case CRAS_IODEV_RAMP_REQUEST_DOWN_MUTE: |
| up = 0; |
| cb = ramp_mute_callback; |
| cb_data = (void*)odev; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| /* Starts ramping. */ |
| rc = cras_ramp_start( |
| odev->ramp, up, |
| RAMP_DURATION_SEC * 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); |
| |
| return 0; |
| } |
| |
| 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; |
| } |