/* 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_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 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 layout to an iodev. */ static void set_default_channel_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; 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; /* * Output devices like internal speakers and headphones are 2-channel * and do not need to update channel layout. * For HDMI and USB devices that might have more than 2 channels, update * channel layout only if more than 2 channel is requested. */ if (iodev->direction == CRAS_STREAM_OUTPUT && iodev->format->num_channels <= 2) { set_default_channel_layout(iodev); return; } if (iodev->update_channel_layout == NULL) return; rc = iodev->update_channel_layout(iodev); if (rc < 0) { set_default_channel_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; float 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: up = 1; duration_secs = RAMP_UNMUTE_DURATION_SECS; break; case CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK: up = 1; 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: up = 0; duration_secs = RAMP_MUTE_DURATION_SECS; cb = ramp_mute_callback; cb_data = (void*)odev; break; default: return -EINVAL; } /* Starts ramping. */ rc = cras_ramp_start( odev->ramp, up, 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); 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; }