summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCheng-Yi Chiang <cychiang@chromium.org>2016-12-13 16:33:53 +0800
committerchrome-bot <chrome-bot@chromium.org>2016-12-30 03:54:25 -0800
commit64350200f69f730bbb1eb33a648c8dd6c840da0b (patch)
treef278dba9b96b6bb63be96f878036efbb44a1ec88
parentcbb23746b63f0d482736cc246bda54b08ff2ab33 (diff)
downloadadhd-64350200f69f730bbb1eb33a648c8dd6c840da0b.tar.gz
CRAS: Ramping up/down for new stream and mute/unmute switching
Let alsa_io hold a cras_ramp member to control ramping. The start/update of cras_ramp is done in audio thread. The reset of cras_ramp is done in main thread when closing device. Ramping is used in these places: 1. When the sample from new stream is ready, ramping up. 2. When system mute state is changed from unmute to mute, ramp samples down. Set a callback in cras_ramp so the callback can set mute state on device after ramping is done. This process let device plays samples that are close to zero before it is turned to mute state. 3. When system mute state is changed from mute to unmute, starts ramping up samples and switch mute control to unmute state. This process let device plays samples that are close to zero after it is turned to unmute state. For example, when user mutes the system, the flow of 2 is: a. Mute state in cras_system_state changed. b. iodev_list asks audio thread to start ramping down. c. audio_thread gets the message and starts ramping down active devices. d. audio_thread updates ramped sample in cras_iodev_put_output_buffer in each output cycle. e. Ramping is done. audio_thread executes callback in cras_ramp to use cras_device_monitor_set_device_mute_state to ask main thread to set mute state on device. f. Main thread receives message and calls cras_iodev_set_mute on device to actually change device mute state using set_mute ops. BUG=chromium:669662 TEST=make check TEST=on samus, play sine tone in youtube, press mute and volume up repeatedly, observe the ramping up and down without pop noise. TEST=On samus, play a sine tone in youtube, change volume to somewhere close to 0 and press "down down down up" quickly. Observe there is pop noise at unmute. This is to be solved in the next patch. Change-Id: I71659edfa0a15d9aafa9db2e8b933c19ef48793f Reviewed-on: https://chromium-review.googlesource.com/415012 Commit-Ready: Cheng-Yi Chiang <cychiang@chromium.org> Tested-by: Cheng-Yi Chiang <cychiang@chromium.org> Reviewed-by: Hsinyu Chao <hychao@chromium.org>
-rw-r--r--cras/src/server/audio_thread.c57
-rw-r--r--cras/src/server/audio_thread.h17
-rw-r--r--cras/src/server/cras_alsa_io.c5
-rw-r--r--cras/src/server/cras_iodev.c113
-rw-r--r--cras/src/server/cras_iodev.h47
-rw-r--r--cras/src/server/cras_iodev_list.c25
-rw-r--r--cras/src/tests/alsa_io_unittest.cc4
-rw-r--r--cras/src/tests/audio_thread_unittest.cc48
-rw-r--r--cras/src/tests/iodev_list_unittest.cc111
-rw-r--r--cras/src/tests/iodev_unittest.cc465
10 files changed, 871 insertions, 21 deletions
diff --git a/cras/src/server/audio_thread.c b/cras/src/server/audio_thread.c
index ebb632b4..a08492b4 100644
--- a/cras/src/server/audio_thread.c
+++ b/cras/src/server/audio_thread.c
@@ -43,6 +43,7 @@ enum AUDIO_THREAD_COMMAND {
AUDIO_THREAD_DUMP_THREAD_INFO,
AUDIO_THREAD_DRAIN_STREAM,
AUDIO_THREAD_CONFIG_GLOBAL_REMIX,
+ AUDIO_THREAD_DEV_START_RAMP,
};
struct audio_thread_msg {
@@ -72,6 +73,12 @@ struct audio_thread_dump_debug_info_msg {
struct audio_debug_info *info;
};
+struct audio_thread_dev_start_ramp_msg {
+ struct audio_thread_msg header;
+ struct cras_iodev *dev;
+ enum CRAS_IODEV_RAMP_REQUEST request;
+};
+
/* Audio thread logging. */
struct audio_thread_event_log *atlog;
/* Global fmt converter used to remix output channels. */
@@ -387,6 +394,20 @@ static int thread_rm_open_dev(struct audio_thread *thread,
return 0;
}
+/* Handles messages from the main thread to start ramping on a device. */
+static int thread_dev_start_ramp(struct audio_thread *thread,
+ struct cras_iodev *iodev,
+ enum CRAS_IODEV_RAMP_REQUEST request)
+{
+ /* Do nothing if device wasn't already in the active dev list. */
+ struct open_dev *adev = find_adev(
+ thread->open_devs[iodev->direction], iodev);
+ if (!adev)
+ return -EINVAL;
+ return cras_iodev_start_ramp(iodev, request);
+}
+
+
/* Return non-zero if the stream is attached to any device. */
static int thread_find_stream(struct audio_thread *thread,
struct cras_rstream *rstream)
@@ -909,6 +930,13 @@ static int handle_playback_thread_message(struct audio_thread *thread)
return write(thread->to_main_fds[1], &rsp, sizeof(rsp));
}
+ case AUDIO_THREAD_DEV_START_RAMP: {
+ struct audio_thread_dev_start_ramp_msg *rmsg;
+
+ rmsg = (struct audio_thread_dev_start_ramp_msg*)msg;
+ ret = thread_dev_start_ramp(thread, rmsg->dev, rmsg->request);
+ break;
+ }
default:
ret = -EINVAL;
break;
@@ -1637,6 +1665,19 @@ static void init_config_global_remix_msg(
msg->header.length = sizeof(*msg);
}
+static void init_device_start_ramp_msg(
+ struct audio_thread_dev_start_ramp_msg *msg,
+ enum AUDIO_THREAD_COMMAND id,
+ struct cras_iodev *dev,
+ enum CRAS_IODEV_RAMP_REQUEST request)
+{
+ memset(msg, 0, sizeof(*msg));
+ msg->header.id = id;
+ msg->header.length = sizeof(*msg);
+ msg->dev = dev;
+ msg->request = request;
+}
+
/* Exported Interface */
int audio_thread_add_stream(struct audio_thread *thread,
@@ -1807,6 +1848,22 @@ int audio_thread_rm_open_dev(struct audio_thread *thread,
return audio_thread_post_message(thread, &msg.header);
}
+int audio_thread_dev_start_ramp(struct audio_thread *thread,
+ struct cras_iodev *dev,
+ enum CRAS_IODEV_RAMP_REQUEST request)
+{
+ struct audio_thread_dev_start_ramp_msg msg;
+
+ assert(thread && dev);
+
+ if (!thread->started)
+ return -EINVAL;
+
+ init_device_start_ramp_msg(&msg, AUDIO_THREAD_DEV_START_RAMP,
+ dev, request);
+ return audio_thread_post_message(thread, &msg.header);
+}
+
int audio_thread_start(struct audio_thread *thread)
{
int rc;
diff --git a/cras/src/server/audio_thread.h b/cras/src/server/audio_thread.h
index 1c2808cf..ebfc8a96 100644
--- a/cras/src/server/audio_thread.h
+++ b/cras/src/server/audio_thread.h
@@ -9,6 +9,7 @@
#include <pthread.h>
#include <stdint.h>
+#include "cras_iodev.h"
#include "cras_types.h"
struct buffer_share;
@@ -172,4 +173,20 @@ int audio_thread_config_global_remix(struct audio_thread *thread,
/* Gets the global remix converter. */
struct cras_fmt_conv *audio_thread_get_global_remix_converter();
+
+/* Start ramping on a device.
+ *
+ * Ramping is started/updated in audio thread. This function lets the main
+ * thread request that the audio thread start ramping.
+ *
+ * Args:
+ * thread - a pointer to the audio thread.
+ * dev - the device to start ramping.
+ * request - Check the docstrings of CRAS_IODEV_RAMP_REQUEST.
+ * Returns:
+ * 0 on success, negative if error.
+ */
+int audio_thread_dev_start_ramp(struct audio_thread *thread,
+ struct cras_iodev *dev,
+ enum CRAS_IODEV_RAMP_REQUEST request);
#endif /* AUDIO_THREAD_H_ */
diff --git a/cras/src/server/cras_alsa_io.c b/cras/src/server/cras_alsa_io.c
index c71e6836..47e37bc1 100644
--- a/cras/src/server/cras_alsa_io.c
+++ b/cras/src/server/cras_alsa_io.c
@@ -26,6 +26,7 @@
#include "cras_iodev.h"
#include "cras_iodev_list.h"
#include "cras_messages.h"
+#include "cras_ramp.h"
#include "cras_rclient.h"
#include "cras_shm.h"
#include "cras_system_state.h"
@@ -1701,6 +1702,10 @@ struct cras_iodev *alsa_iodev_create(size_t card_index,
if (card_type == ALSA_CARD_TYPE_USB)
iodev->min_buffer_level = USB_EXTRA_BUFFER_FRAMES;
+ iodev->ramp = cras_ramp_create();
+ if (iodev->ramp == NULL)
+ goto cleanup_iodev;
+
aio->mixer = mixer;
aio->ucm = ucm;
if (ucm) {
diff --git a/cras/src/server/cras_iodev.c b/cras/src/server/cras_iodev.c
index 95ae5643..fce5d9a0 100644
--- a/cras/src/server/cras_iodev.c
+++ b/cras/src/server/cras_iodev.c
@@ -21,6 +21,7 @@
#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"
@@ -29,6 +30,8 @@
#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. */
};
@@ -182,8 +185,16 @@ static int cras_iodev_no_stream_playback_transition(struct cras_iodev *odev,
* cras_iodev_output_event_sample_ready change device state from S1 or S3 into
* S2.
*/
-int cras_iodev_output_event_sample_ready(struct cras_iodev *odev)
+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) {
+ if (odev->ramp)
+ 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
@@ -208,6 +219,8 @@ int cras_iodev_output_event_sample_ready(struct cras_iodev *odev)
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)
{
size_t system_volume;
@@ -536,6 +549,8 @@ 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)
@@ -832,6 +847,8 @@ int cras_iodev_close(struct cras_iodev *iodev)
if (rc)
return rc;
iodev->state = CRAS_IODEV_STATE_CLOSE;
+ if (iodev->ramp)
+ cras_ramp_reset(iodev->ramp);
return 0;
}
@@ -847,13 +864,24 @@ int cras_iodev_put_output_buffer(struct cras_iodev *iodev, uint8_t *frames,
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;
+ float software_volume_scaler;
+ int software_volume_needed = cras_iodev_software_volume_needed(iodev);
+ int is_ramping = 0;
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);
+ if (ramp_action.type == CRAS_RAMP_ACTION_PARTIAL)
+ is_ramping = 1;
+ }
- if (output_should_mute(iodev)) {
+ /* Mute samples if adjusted volume is 0 or system is muted, plus
+ * that this device is not ramping. */
+ if (output_should_mute(iodev) && !is_ramping) {
const unsigned int frame_bytes = cras_get_format_bytes(fmt);
cras_mix_mute_buffer(frames, frame_bytes, nframes);
} else {
@@ -863,13 +891,35 @@ int cras_iodev_put_output_buffer(struct cras_iodev *iodev, uint8_t *frames,
iodev->post_dsp_hook(frames, nframes, fmt,
iodev->post_dsp_hook_cb_data);
- if (cras_iodev_software_volume_needed(iodev)) {
- unsigned int nsamples = nframes * fmt->num_channels;
- float scaler =
+ /* Compute scaler for software volume if needed. */
+ if (software_volume_needed) {
+ software_volume_scaler =
cras_iodev_get_software_volume_scaler(iodev);
+ }
+
+ if (iodev->ramp &&
+ 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, scaler);
+ nsamples, software_volume_scaler);
}
}
@@ -1149,6 +1199,57 @@ int cras_iodev_reset_request(struct cras_iodev* iodev)
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))
diff --git a/cras/src/server/cras_iodev.h b/cras/src/server/cras_iodev.h
index 7ce0ed97..78b7d66d 100644
--- a/cras/src/server/cras_iodev.h
+++ b/cras/src/server/cras_iodev.h
@@ -19,6 +19,7 @@
#include "cras_messages.h"
struct buffer_share;
+struct cras_ramp;
struct cras_rstream;
struct cras_audio_area;
struct cras_audio_format;
@@ -164,6 +165,8 @@ struct cras_ionode {
* pre_dsp_hook_cb_data - Callback data that will be passing to pre_dsp_hook.
* post_dsp_hook_cb_data - Callback data that will be passing to post_dsp_hook.
* reset_request_pending - The flag for pending reset request.
+ * ramp - The cras_ramp struct to control ramping up/down at mute/unmute and
+ * start of playback.
*/
struct cras_iodev {
void (*set_volume)(struct cras_iodev *iodev);
@@ -224,10 +227,43 @@ struct cras_iodev {
void *pre_dsp_hook_cb_data;
void *post_dsp_hook_cb_data;
int reset_request_pending;
+ struct cras_ramp* ramp;
struct cras_iodev *prev, *next;
};
/*
+ * Ramp request used in cras_iodev_start_ramp.
+ *
+ * - CRAS_IODEV_RAMP_REQUEST_UP_UNMUTE: Mute->unmute.
+ * Change device to unmute state after ramping is stared,
+ * that is, (a) in the plot.
+ *
+ * ____
+ * .... /
+ * _____/
+ * (a)
+ *
+ * - CRAS_IODEV_RAMP_REQUEST_DOWN_MUTE: Unmute->mute.
+ * Change device to mute state after ramping is done, that is,
+ * (b) in the plot.
+ *
+ * _____
+ * \....
+ * \____
+ * (b)
+ *
+ * - CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK: Ramping is requested because
+ * first sample of new stream is ready, there is no need to change mute/unmute
+ * state.
+ */
+
+enum CRAS_IODEV_RAMP_REQUEST {
+ CRAS_IODEV_RAMP_REQUEST_UP_UNMUTE = 0,
+ CRAS_IODEV_RAMP_REQUEST_DOWN_MUTE = 1,
+ CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK = 2,
+};
+
+/*
* Utility functions to be used by iodev implementations.
*/
@@ -633,6 +669,17 @@ int cras_iodev_reset_request(struct cras_iodev* iodev);
*/
int cras_iodev_output_underrun(struct cras_iodev *odev);
+/* Start ramping samples up/down on a device.
+ * Args:
+ * iodev[in] - The device.
+ * request[in] - The request type. Check the docstrings of
+ * CRAS_IODEV_RAMP_REQUEST.
+ * Returns:
+ * 0 on success. Negative error code on failure.
+ */
+int cras_iodev_start_ramp(struct cras_iodev *odev,
+ enum CRAS_IODEV_RAMP_REQUEST request);
+
/* Set iodev to mute/unmute state.
* Args:
* iodev[in] - The device.
diff --git a/cras/src/server/cras_iodev_list.c b/cras/src/server/cras_iodev_list.c
index 7378a7a6..ecbb8b15 100644
--- a/cras/src/server/cras_iodev_list.c
+++ b/cras/src/server/cras_iodev_list.c
@@ -292,10 +292,31 @@ static void sys_mute_change(void *context, int muted, int user_muted,
int mute_locked)
{
struct cras_iodev *dev;
+ int should_mute = muted || user_muted;
DL_FOREACH(devs[CRAS_STREAM_OUTPUT].iodevs, dev) {
- if (dev->set_mute && cras_iodev_is_open(dev))
- dev->set_mute(dev);
+ if (cras_iodev_list_dev_is_enabled(dev) && dev->ramp) {
+ /* Start ramping in audio thread and set mute/unmute
+ * state on device.
+ *
+ * 1. Mute -> Unmute: Set device unmute state after
+ * ramping is started.
+ * 2. Unmute -> Mute: Set device mute state after
+ * ramping is done.
+ *
+ * The above transition will be handled by cras_iodev_ramp_start.
+ */
+ audio_thread_dev_start_ramp(
+ audio_thread,
+ dev,
+ (should_mute ?
+ CRAS_IODEV_RAMP_REQUEST_DOWN_MUTE :
+ CRAS_IODEV_RAMP_REQUEST_UP_UNMUTE));
+
+ } else {
+ /* For device without ramp, just set its mute state. */
+ cras_iodev_set_mute(dev);
+ }
}
}
diff --git a/cras/src/tests/alsa_io_unittest.cc b/cras/src/tests/alsa_io_unittest.cc
index 51762b70..b953b40b 100644
--- a/cras/src/tests/alsa_io_unittest.cc
+++ b/cras/src/tests/alsa_io_unittest.cc
@@ -2648,4 +2648,8 @@ int cras_iodev_dsp_set_swap_mode_for_node(struct cras_iodev *iodev,
return 0;
}
+struct cras_ramp* cras_ramp_create() {
+ return (struct cras_ramp*)0x1;
+}
+
}
diff --git a/cras/src/tests/audio_thread_unittest.cc b/cras/src/tests/audio_thread_unittest.cc
index 00c758a1..f7e5e2ef 100644
--- a/cras/src/tests/audio_thread_unittest.cc
+++ b/cras/src/tests/audio_thread_unittest.cc
@@ -34,6 +34,10 @@ static int cras_iodev_prepare_output_before_write_samples_ret;
static int cras_iodev_reset_request_called;
static struct cras_iodev *cras_iodev_reset_request_iodev;
static int cras_iodev_output_underrun_called;
+static int cras_device_monitor_reset_device_called;
+static struct cras_iodev *cras_device_monitor_reset_device_iodev;
+static struct cras_iodev *cras_iodev_start_ramp_odev;
+static enum CRAS_IODEV_RAMP_REQUEST cras_iodev_start_ramp_request;
void ResetGlobalStubData() {
cras_rstream_dev_offset_called = 0;
@@ -62,6 +66,10 @@ void ResetGlobalStubData() {
cras_iodev_reset_request_called = 0;
cras_iodev_reset_request_iodev = NULL;
cras_iodev_output_underrun_called = 0;
+ cras_device_monitor_reset_device_called = 0;
+ cras_device_monitor_reset_device_iodev = NULL;
+ cras_iodev_start_ramp_odev = NULL;
+ cras_iodev_start_ramp_request = CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK;
}
// Test streams and devices manipulation.
@@ -210,6 +218,38 @@ TEST_F(StreamDeviceSuite, AddRemoveOpenOutputDevice) {
EXPECT_EQ(NULL, adev);
}
+TEST_F(StreamDeviceSuite, StartRamp) {
+ struct cras_iodev iodev;
+ struct open_dev *adev;
+ int rc;
+ enum CRAS_IODEV_RAMP_REQUEST req;
+
+ SetupDevice(&iodev, CRAS_STREAM_OUTPUT);
+
+ // Check the newly added device is open.
+ thread_add_open_dev(thread_, &iodev);
+ adev = thread_->open_devs[CRAS_STREAM_OUTPUT];
+ EXPECT_EQ(adev->dev, &iodev);
+
+ // Ramp up for unmute.
+ req = CRAS_IODEV_RAMP_REQUEST_UP_UNMUTE;
+ rc = thread_dev_start_ramp(thread_, &iodev, req);
+
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(&iodev, cras_iodev_start_ramp_odev);
+ EXPECT_EQ(req, cras_iodev_start_ramp_request);
+
+ // Ramp down for mute.
+ ResetStubData();
+ req = CRAS_IODEV_RAMP_REQUEST_DOWN_MUTE;
+
+ rc = thread_dev_start_ramp(thread_, &iodev, req);
+
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(&iodev, cras_iodev_start_ramp_odev);
+ EXPECT_EQ(req, cras_iodev_start_ramp_request);
+}
+
TEST_F(StreamDeviceSuite, AddRemoveOpenInputDevice) {
struct cras_iodev iodev;
struct open_dev *adev;
@@ -959,6 +999,14 @@ unsigned int cras_iodev_get_num_severe_underruns(const struct cras_iodev *iodev)
return 0;
}
+int cras_iodev_start_ramp(struct cras_iodev *odev,
+ enum CRAS_IODEV_RAMP_REQUEST request)
+{
+ cras_iodev_start_ramp_odev = odev;
+ cras_iodev_start_ramp_request = request;
+ return 0;
+}
+
} // extern "C"
int main(int argc, char **argv) {
diff --git a/cras/src/tests/iodev_list_unittest.cc b/cras/src/tests/iodev_list_unittest.cc
index 94e81c43..61e1655d 100644
--- a/cras/src/tests/iodev_list_unittest.cc
+++ b/cras/src/tests/iodev_list_unittest.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <algorithm>
#include <stdio.h>
#include <gtest/gtest.h>
@@ -10,6 +11,7 @@ extern "C" {
#include "cras_iodev.h"
#include "cras_iodev_list.h"
#include "cras_observer_ops.h"
+#include "cras_ramp.h"
#include "cras_rstream.h"
#include "cras_system_state.h"
#include "cras_tm.h"
@@ -68,12 +70,22 @@ static size_t cras_observer_notify_node_left_right_swapped_called;
static size_t cras_observer_notify_input_node_gain_called;
static int cras_iodev_open_called;
static int cras_iodev_open_ret[8];
+static int set_mute_called;
+static std::vector<struct cras_iodev*> set_mute_dev_vector;
+static struct cras_iodev *audio_thread_dev_start_ramp_dev;
+static int audio_thread_dev_start_ramp_called;
+static enum CRAS_IODEV_RAMP_REQUEST audio_thread_dev_start_ramp_req ;
void dummy_update_active_node(struct cras_iodev *iodev,
unsigned node_idx,
unsigned dev_enabled) {
}
+int device_in_vector(std::vector<struct cras_iodev*> v, struct cras_iodev *dev)
+{
+ return std::find(v.begin(), v.end(), dev) != v.end();
+}
+
class IoDevTestSuite : public testing::Test {
protected:
virtual void SetUp() {
@@ -102,7 +114,6 @@ class IoDevTestSuite : public testing::Test {
memset(&node3, 0, sizeof(node3));
d1_.set_volume = NULL;
- d1_.set_mute = NULL;
d1_.set_capture_gain = NULL;
d1_.set_capture_mute = NULL;
d1_.update_supported_formats = NULL;
@@ -116,7 +127,6 @@ class IoDevTestSuite : public testing::Test {
d1_.supported_rates = sample_rates_;
d1_.supported_channel_counts = channel_counts_;
d2_.set_volume = NULL;
- d2_.set_mute = NULL;
d2_.set_capture_gain = NULL;
d2_.set_capture_mute = NULL;
d2_.update_supported_formats = NULL;
@@ -130,7 +140,6 @@ class IoDevTestSuite : public testing::Test {
d2_.supported_rates = sample_rates_;
d2_.supported_channel_counts = channel_counts_;
d3_.set_volume = NULL;
- d3_.set_mute = NULL;
d3_.set_capture_gain = NULL;
d3_.set_capture_mute = NULL;
d3_.update_supported_formats = NULL;
@@ -145,7 +154,6 @@ class IoDevTestSuite : public testing::Test {
d3_.supported_channel_counts = channel_counts_;
loopback_input.set_volume = NULL;
- loopback_input.set_mute = NULL;
loopback_input.set_capture_gain = NULL;
loopback_input.set_capture_mute = NULL;
loopback_input.update_supported_formats = NULL;
@@ -179,16 +187,18 @@ class IoDevTestSuite : public testing::Test {
cras_observer_notify_input_node_gain_called = 0;
cras_iodev_open_called = 0;
memset(cras_iodev_open_ret, 0, sizeof(cras_iodev_open_ret));
+ set_mute_called = 0;
+ set_mute_dev_vector.clear();
+ audio_thread_dev_start_ramp_dev = NULL;
+ audio_thread_dev_start_ramp_called = 0;
+ audio_thread_dev_start_ramp_req =
+ CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK;
}
static void set_volume_1(struct cras_iodev* iodev) {
set_volume_1_called_++;
}
- static void set_mute_1(struct cras_iodev* iodev) {
- set_mute_1_called_++;
- }
-
static void set_capture_gain_1(struct cras_iodev* iodev) {
set_capture_gain_1_called_++;
}
@@ -212,14 +222,12 @@ class IoDevTestSuite : public testing::Test {
size_t sample_rates_[3];
size_t channel_counts_[2];
static int set_volume_1_called_;
- static int set_mute_1_called_;
static int set_capture_gain_1_called_;
static int set_capture_mute_1_called_;
struct cras_ionode node1, node2, node3;
};
int IoDevTestSuite::set_volume_1_called_;
-int IoDevTestSuite::set_mute_1_called_;
int IoDevTestSuite::set_capture_gain_1_called_;
int IoDevTestSuite::set_capture_mute_1_called_;
@@ -587,6 +595,73 @@ TEST_F(IoDevTestSuite, AddRemoveOutput) {
EXPECT_EQ(0, cras_observer_notify_active_node_called);
}
+// Test output_mute_changed callback.
+TEST_F(IoDevTestSuite, OutputMuteChangedToMute) {
+ cras_iodev_list_init();
+
+ // d1_ and d3_ have ramp while d2_ does not have ramp.
+ d1_.ramp = reinterpret_cast<cras_ramp*>(0x123);
+ d2_.ramp = NULL;
+ d3_.ramp = reinterpret_cast<cras_ramp*>(0x124);
+
+ cras_iodev_list_add_output(&d1_);
+ cras_iodev_list_add_output(&d2_);
+ cras_iodev_list_add_output(&d3_);
+
+ // d1_ and d2_ are enabled.
+ cras_iodev_list_enable_dev(&d1_);
+ cras_iodev_list_enable_dev(&d2_);
+
+ observer_ops->output_mute_changed(NULL, 0, 1, 0);
+
+ // d1_ should set mute state through audio_thread_dev_start_ramp.
+ EXPECT_EQ(&d1_, audio_thread_dev_start_ramp_dev);
+ EXPECT_EQ(1, audio_thread_dev_start_ramp_called);
+ EXPECT_EQ(CRAS_IODEV_RAMP_REQUEST_DOWN_MUTE, audio_thread_dev_start_ramp_req);
+
+ // d2_ should set mute state right away.
+ // d3_ should set mute state right away without calling ramp
+ // because it is not enabled.
+ EXPECT_EQ(2, set_mute_called);
+ EXPECT_EQ(2, set_mute_dev_vector.size());
+ ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d2_));
+ ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d3_));
+}
+
+// Test output_mute_changed callback.
+TEST_F(IoDevTestSuite, OutputMuteChangedToUnmute) {
+ cras_iodev_list_init();
+
+ // d1_ and d3_ have ramp while d2_ does not have ramp.
+ d1_.ramp = reinterpret_cast<cras_ramp*>(0x123);
+ d2_.ramp = NULL;
+ d3_.ramp = reinterpret_cast<cras_ramp*>(0x124);
+
+ cras_iodev_list_add_output(&d1_);
+ cras_iodev_list_add_output(&d2_);
+ cras_iodev_list_add_output(&d3_);
+
+ // d1_ and d2_ are enabled.
+ cras_iodev_list_enable_dev(&d1_);
+ cras_iodev_list_enable_dev(&d2_);
+
+ observer_ops->output_mute_changed(NULL, 0, 0, 0);
+
+ // d1_ should set mute state through audio_thread_dev_start_ramp.
+ EXPECT_EQ(&d1_, audio_thread_dev_start_ramp_dev);
+ EXPECT_EQ(1, audio_thread_dev_start_ramp_called);
+ EXPECT_EQ(CRAS_IODEV_RAMP_REQUEST_UP_UNMUTE,
+ audio_thread_dev_start_ramp_req);
+
+ // d2_ should set mute state right away.
+ // d3_ should set mute state right away without calling ramp
+ // because it is not enabled.
+ EXPECT_EQ(2, set_mute_called);
+ EXPECT_EQ(2, set_mute_dev_vector.size());
+ ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d2_));
+ ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d3_));
+}
+
static void device_enabled_cb(struct cras_iodev *dev, int enabled,
void *cb_data)
{
@@ -1156,6 +1231,12 @@ int cras_iodev_set_format(struct cras_iodev *iodev,
return 0;
}
+int cras_iodev_set_mute(struct cras_iodev* iodev) {
+ set_mute_called++;
+ set_mute_dev_vector.push_back(iodev);
+ return 0;
+}
+
struct stream_list *stream_list_create(stream_callback *add_cb,
stream_callback *rm_cb,
stream_create_func *create_cb,
@@ -1260,6 +1341,16 @@ void cras_observer_notify_input_node_gain(cras_node_id_t node_id,
cras_observer_notify_input_node_gain_called++;
}
+int audio_thread_dev_start_ramp(struct audio_thread *thread,
+ struct cras_iodev *dev,
+ enum CRAS_IODEV_RAMP_REQUEST request)
+{
+ audio_thread_dev_start_ramp_called++;
+ audio_thread_dev_start_ramp_dev = dev;
+ audio_thread_dev_start_ramp_req = request;
+ return 0;
+}
+
// From librt.
int clock_gettime(clockid_t clk_id, struct timespec *tp) {
tp->tv_sec = clock_gettime_retspec.tv_sec;
diff --git a/cras/src/tests/iodev_unittest.cc b/cras/src/tests/iodev_unittest.cc
index f52eb069..ae10c823 100644
--- a/cras/src/tests/iodev_unittest.cc
+++ b/cras/src/tests/iodev_unittest.cc
@@ -7,6 +7,7 @@
extern "C" {
#include "cras_iodev.h"
+#include "cras_ramp.h"
#include "cras_rstream.h"
#include "dev_stream.h"
#include "utlist.h"
@@ -19,6 +20,8 @@ float softvol_scalers[101];
#define BUFFER_SIZE 8192
+#define RAMP_DURATION_SECS 0.5
+
static int cras_iodev_list_disable_dev_called;
static int select_node_called;
static enum CRAS_STREAM_DIRECTION select_node_direction;
@@ -57,8 +60,10 @@ static unsigned int rate_estimator_add_frames_called;
static int cras_system_get_mute_return;
static snd_pcm_format_t cras_scale_buffer_fmt;
static float cras_scale_buffer_scaler;
+static int cras_scale_buffer_called;
static unsigned int pre_dsp_hook_called;
static const uint8_t *pre_dsp_hook_frames;
+
static void *pre_dsp_hook_cb_data;
static unsigned int post_dsp_hook_called;
static const uint8_t *post_dsp_hook_frames;
@@ -80,7 +85,22 @@ static int get_num_underruns_ret;
static int device_monitor_reset_device_called;
static int output_underrun_called;
static int set_mute_called;
-
+static int cras_ramp_start_is_up;
+static int cras_ramp_start_duration_frames;
+static int cras_ramp_start_is_called;
+static int cras_ramp_reset_is_called;
+static struct cras_ramp_action cras_ramp_get_current_action_ret;
+static int cras_ramp_update_ramped_frames_num_frames;
+static cras_ramp_cb cras_ramp_start_cb;
+static void* cras_ramp_start_cb_data;
+static int cras_device_monitor_set_device_mute_state_called;
+static struct cras_iodev* cras_device_monitor_set_device_mute_state_dev;
+static snd_pcm_format_t cras_scale_buffer_increment_fmt;
+static uint8_t *cras_scale_buffer_increment_buff;
+static unsigned int cras_scale_buffer_increment_frame;
+static float cras_scale_buffer_increment_scaler;
+static float cras_scale_buffer_increment_increment;
+static int cras_scale_buffer_increment_channel;
// Iodev callback
int update_channel_layout(struct cras_iodev *iodev) {
@@ -134,8 +154,7 @@ void ResetStubData() {
pre_dsp_hook_called = 0;
pre_dsp_hook_frames = NULL;
post_dsp_hook_called = 0;
- post_dsp_hook_frames = NULL;
- iodev_buffer_size = 0;
+ post_dsp_hook_frames = NULL; iodev_buffer_size = 0;
cras_system_get_capture_gain_ret_value = 0;
// Assume there is some data in audio buffer.
memset(audio_buffer, 0xff, sizeof(audio_buffer));
@@ -156,6 +175,23 @@ void ResetStubData() {
device_monitor_reset_device_called = 0;
output_underrun_called = 0;
set_mute_called = 0;
+ cras_ramp_start_is_up = 0;
+ cras_ramp_start_duration_frames = 0;
+ cras_ramp_start_cb = NULL;
+ cras_ramp_start_cb_data = NULL;
+ cras_ramp_start_is_called = 0;
+ cras_ramp_reset_is_called = 0;
+ cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_NONE;
+ cras_ramp_update_ramped_frames_num_frames = 0;
+ cras_device_monitor_set_device_mute_state_called = 0;
+ cras_device_monitor_set_device_mute_state_dev = NULL;
+ cras_scale_buffer_called = 0;
+ cras_scale_buffer_increment_fmt = SND_PCM_FORMAT_UNKNOWN;
+ cras_scale_buffer_increment_buff = NULL;
+ cras_scale_buffer_increment_frame = 0;
+ cras_scale_buffer_increment_scaler = 0;
+ cras_scale_buffer_increment_increment = 0;
+ cras_scale_buffer_increment_channel = 0;
}
namespace {
@@ -587,6 +623,96 @@ TEST(IoDevPutOutputBuffer, NodeVolumeZeroShouldMute) {
EXPECT_EQ(20, rate_estimator_add_frames_num_frames);
}
+TEST(IoDevPutOutputBuffer, SystemMutedWithRamp) {
+ struct cras_audio_format fmt;
+ struct cras_iodev iodev;
+ uint8_t *frames = reinterpret_cast<uint8_t*>(0x44);
+ int rc;
+
+ ResetStubData();
+ memset(&iodev, 0, sizeof(iodev));
+ cras_system_get_mute_return = 1;
+
+ fmt.format = SND_PCM_FORMAT_S16_LE;
+ fmt.frame_rate = 48000;
+ fmt.num_channels = 2;
+ iodev.format = &fmt;
+ iodev.put_buffer = put_buffer;
+
+ // Assume device has ramp member.
+ iodev.ramp = reinterpret_cast<struct cras_ramp*>(0x1);
+
+ // Assume ramping is done.
+ cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_NONE;
+
+ rc = cras_iodev_put_output_buffer(&iodev, frames, 20);
+ // Output should be muted.
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(20, cras_mix_mute_count);
+ EXPECT_EQ(20, put_buffer_nframes);
+ EXPECT_EQ(20, rate_estimator_add_frames_num_frames);
+
+ // Test for the case where ramping is not done yet.
+ ResetStubData();
+ cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_PARTIAL;
+ rc = cras_iodev_put_output_buffer(&iodev, frames, 20);
+
+ // Output should not be muted.
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_mix_mute_count);
+ // Ramped frames should be increased by 20.
+ EXPECT_EQ(20, cras_ramp_update_ramped_frames_num_frames);
+ EXPECT_EQ(20, put_buffer_nframes);
+ EXPECT_EQ(20, rate_estimator_add_frames_num_frames);
+}
+
+TEST(IoDevPutOutputBuffer, NodeVolumeZeroShouldMuteWithRamp) {
+ struct cras_audio_format fmt;
+ struct cras_iodev iodev;
+ struct cras_ionode ionode;
+ uint8_t *frames = reinterpret_cast<uint8_t*>(0x44);
+ int rc;
+
+ ResetStubData();
+ memset(&iodev, 0, sizeof(iodev));
+ memset(&ionode, 0, sizeof(ionode));
+
+ iodev.nodes = &ionode;
+ iodev.active_node = &ionode;
+ iodev.active_node->dev = &iodev;
+ iodev.active_node->volume = 0;
+
+ fmt.format = SND_PCM_FORMAT_S16_LE;
+ fmt.frame_rate = 48000;
+ fmt.num_channels = 2;
+ iodev.format = &fmt;
+ iodev.put_buffer = put_buffer;
+
+ // Assume device has ramp member.
+ iodev.ramp = reinterpret_cast<struct cras_ramp*>(0x1);
+
+ // Assume ramping is done.
+ cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_NONE;
+
+ rc = cras_iodev_put_output_buffer(&iodev, frames, 20);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(20, cras_mix_mute_count);
+ EXPECT_EQ(20, put_buffer_nframes);
+ EXPECT_EQ(20, rate_estimator_add_frames_num_frames);
+
+ // Test for the case where ramping is not done yet.
+ ResetStubData();
+ cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_PARTIAL;
+ rc = cras_iodev_put_output_buffer(&iodev, frames, 20);
+
+ // Output should not be muted.
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_mix_mute_count);
+ // Ramped frames should be increased by 20.
+ EXPECT_EQ(20, cras_ramp_update_ramped_frames_num_frames);
+ EXPECT_EQ(20, put_buffer_nframes);
+ EXPECT_EQ(20, rate_estimator_add_frames_num_frames);
+}
TEST(IoDevPutOutputBuffer, NoDSP) {
struct cras_audio_format fmt;
struct cras_iodev iodev;
@@ -677,6 +803,132 @@ TEST(IoDevPutOutputBuffer, SoftVol) {
EXPECT_EQ(SND_PCM_FORMAT_S16_LE, cras_scale_buffer_fmt);
}
+TEST(IoDevPutOutputBuffer, SoftVolWithRamp) {
+ struct cras_audio_format fmt;
+ struct cras_iodev iodev;
+ uint8_t *frames = reinterpret_cast<uint8_t*>(0x44);
+ int rc;
+ int n_frames = 53;
+ float ramp_scaler = 0.2;
+ float increment = 0.001;
+ int volume = 13;
+ float volume_scaler = 0.435;
+
+ ResetStubData();
+ memset(&iodev, 0, sizeof(iodev));
+ iodev.software_volume_needed = 1;
+
+ fmt.format = SND_PCM_FORMAT_S16_LE;
+ fmt.frame_rate = 48000;
+ fmt.num_channels = 2;
+ iodev.format = &fmt;
+ iodev.put_buffer = put_buffer;
+ // Assume device has ramp member.
+ iodev.ramp = reinterpret_cast<struct cras_ramp*>(0x1);
+
+ // Assume ramping is done.
+ cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_NONE;
+
+ cras_system_get_volume_return = volume;
+ softvol_scalers[volume] = volume_scaler;
+
+ rc = cras_iodev_put_output_buffer(&iodev, frames, n_frames);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_mix_mute_count);
+ EXPECT_EQ(n_frames, put_buffer_nframes);
+ EXPECT_EQ(n_frames, rate_estimator_add_frames_num_frames);
+ EXPECT_EQ(softvol_scalers[volume], cras_scale_buffer_scaler);
+ EXPECT_EQ(SND_PCM_FORMAT_S16_LE, cras_scale_buffer_fmt);
+
+ ResetStubData();
+ // Assume ramping is not done.
+ cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_PARTIAL;
+ cras_ramp_get_current_action_ret.scaler = ramp_scaler;
+ cras_ramp_get_current_action_ret.increment = increment;
+
+ cras_system_get_volume_return = volume;
+ softvol_scalers[volume] = volume_scaler;
+
+ rc = cras_iodev_put_output_buffer(&iodev, frames, n_frames);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_mix_mute_count);
+ // cras_scale_buffer is not called.
+ EXPECT_EQ(0, cras_scale_buffer_called);
+
+ // Verify the arguments passed to cras_scale_buffer_increment.
+ EXPECT_EQ(fmt.format, cras_scale_buffer_increment_fmt);
+ EXPECT_EQ(frames, cras_scale_buffer_increment_buff);
+ EXPECT_EQ(n_frames, cras_scale_buffer_increment_frame);
+ // Initial scaler will be product of software volume scaler and
+ // ramp scaler.
+ EXPECT_FLOAT_EQ(softvol_scalers[volume] * ramp_scaler,
+ cras_scale_buffer_increment_scaler);
+ // Increment scaler will be product of software volume scaler and
+ // ramp increment.
+ EXPECT_FLOAT_EQ(softvol_scalers[volume] * increment,
+ cras_scale_buffer_increment_increment);
+ EXPECT_EQ(fmt.num_channels, cras_scale_buffer_increment_channel);
+
+ EXPECT_EQ(n_frames, put_buffer_nframes);
+ EXPECT_EQ(n_frames, rate_estimator_add_frames_num_frames);
+}
+
+TEST(IoDevPutOutputBuffer, NoSoftVolWithRamp) {
+ struct cras_audio_format fmt;
+ struct cras_iodev iodev;
+ uint8_t *frames = reinterpret_cast<uint8_t*>(0x44);
+ int rc;
+ int n_frames = 53;
+ float ramp_scaler = 0.2;
+ float increment = 0.001;
+
+ ResetStubData();
+ memset(&iodev, 0, sizeof(iodev));
+ iodev.software_volume_needed = 0;
+
+ fmt.format = SND_PCM_FORMAT_S16_LE;
+ fmt.frame_rate = 48000;
+ fmt.num_channels = 2;
+ iodev.format = &fmt;
+ iodev.put_buffer = put_buffer;
+ // Assume device has ramp member.
+ iodev.ramp = reinterpret_cast<struct cras_ramp*>(0x1);
+
+ // Assume ramping is done.
+ cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_NONE;
+
+ rc = cras_iodev_put_output_buffer(&iodev, frames, n_frames);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_mix_mute_count);
+ // cras_scale_buffer is not called.
+ EXPECT_EQ(0, cras_scale_buffer_called);
+ EXPECT_EQ(n_frames, put_buffer_nframes);
+ EXPECT_EQ(n_frames, rate_estimator_add_frames_num_frames);
+
+ ResetStubData();
+ // Assume ramping is not done.
+ cras_ramp_get_current_action_ret.type = CRAS_RAMP_ACTION_PARTIAL;
+ cras_ramp_get_current_action_ret.scaler = ramp_scaler;
+ cras_ramp_get_current_action_ret.increment = increment;
+
+ rc = cras_iodev_put_output_buffer(&iodev, frames, n_frames);
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_mix_mute_count);
+ // cras_scale_buffer is not called.
+ EXPECT_EQ(0, cras_scale_buffer_called);
+
+ // Verify the arguments passed to cras_scale_buffer_increment.
+ EXPECT_EQ(fmt.format, cras_scale_buffer_increment_fmt);
+ EXPECT_EQ(frames, cras_scale_buffer_increment_buff);
+ EXPECT_EQ(n_frames, cras_scale_buffer_increment_frame);
+ EXPECT_FLOAT_EQ(ramp_scaler, cras_scale_buffer_increment_scaler);
+ EXPECT_FLOAT_EQ(increment, cras_scale_buffer_increment_increment);
+ EXPECT_EQ(fmt.num_channels, cras_scale_buffer_increment_channel);
+
+ EXPECT_EQ(n_frames, put_buffer_nframes);
+ EXPECT_EQ(n_frames, rate_estimator_add_frames_num_frames);
+}
+
TEST(IoDevPutOutputBuffer, Scale32Bit) {
struct cras_audio_format fmt;
struct cras_iodev iodev;
@@ -1227,6 +1479,7 @@ TEST(IoDev, PrepareOutputBeforeWriteSamples) {
fmt.frame_rate = 48000;
fmt.num_channels = 2;
iodev.ext_format = &fmt;
+ iodev.format = &fmt;
iodev.min_cb_level = min_cb_level;
iodev.get_buffer = get_buffer;
iodev.put_buffer = put_buffer;
@@ -1312,6 +1565,161 @@ TEST(IoDev, PrepareOutputBeforeWriteSamples) {
EXPECT_EQ(0, no_stream_called);
ResetStubData();
+
+ // Test for device with ramp. Device should start ramping
+ // when sample is ready.
+
+ // Assume device has ramp member.
+ iodev.ramp = reinterpret_cast<struct cras_ramp*>(0x1);
+
+ // Case 4: Assume device with ramp is started and is in no stream state.
+ iodev.state = CRAS_IODEV_STATE_NO_STREAM_RUN;
+ // Assume sample is ready.
+ dev_stream_playback_frames_ret = 100;
+
+ rc = cras_iodev_prepare_output_before_write_samples(&iodev);
+
+ // Device should start ramping up without setting mute callback.
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, cras_ramp_start_is_called);
+ EXPECT_EQ(1, cras_ramp_start_is_up);
+ EXPECT_EQ(fmt.frame_rate * RAMP_DURATION_SECS,
+ cras_ramp_start_duration_frames);
+ EXPECT_EQ(NULL, cras_ramp_start_cb);
+ EXPECT_EQ(NULL, cras_ramp_start_cb_data);
+
+ ResetStubData();
+
+ // Case 5: Assume device with ramp is in open state.
+ iodev.state = CRAS_IODEV_STATE_OPEN;
+ // Assume sample is ready.
+ dev_stream_playback_frames_ret = 100;
+
+ rc = cras_iodev_prepare_output_before_write_samples(&iodev);
+
+ // Device should start ramping up without setting mute callback.
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, cras_ramp_start_is_called);
+ EXPECT_EQ(1, cras_ramp_start_is_up);
+ EXPECT_EQ(fmt.frame_rate * RAMP_DURATION_SECS,
+ cras_ramp_start_duration_frames);
+ EXPECT_EQ(NULL, cras_ramp_start_cb);
+ EXPECT_EQ(NULL, cras_ramp_start_cb_data);
+}
+
+TEST(IoDev, StartRampUp) {
+ struct cras_iodev iodev;
+ int rc;
+ struct cras_audio_format fmt;
+ enum CRAS_IODEV_RAMP_REQUEST req;
+ memset(&iodev, 0, sizeof(iodev));
+
+ // Format will be used in cras_iodev_start_ramp to determine ramp duration.
+ fmt.format = SND_PCM_FORMAT_S16_LE;
+ fmt.frame_rate = 48000;
+ fmt.num_channels = 2;
+ iodev.format = &fmt;
+
+ // Assume device has ramp member.
+ iodev.ramp = reinterpret_cast<struct cras_ramp*>(0x1);
+
+ // Case 1: Device is not opened yet.
+ ResetStubData();
+ iodev.state = CRAS_IODEV_STATE_CLOSE;
+ req = CRAS_IODEV_RAMP_REQUEST_UP_UNMUTE;
+
+ rc = cras_iodev_start_ramp(&iodev, req);
+
+ // Ramp request is ignored.
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_ramp_start_is_called);
+
+ // Case 2: Ramp up without mute.
+ ResetStubData();
+ iodev.state = CRAS_IODEV_STATE_OPEN;
+ req = CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK;
+
+ rc = cras_iodev_start_ramp(&iodev, req);
+
+ // Device should start ramping up without setting mute callback.
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, cras_ramp_start_is_called);
+ EXPECT_EQ(1, cras_ramp_start_is_up);
+ EXPECT_EQ(fmt.frame_rate * RAMP_DURATION_SECS,
+ cras_ramp_start_duration_frames);
+ EXPECT_EQ(NULL, cras_ramp_start_cb);
+ EXPECT_EQ(NULL, cras_ramp_start_cb_data);
+
+ // Case 3: Ramp up for unmute.
+ ResetStubData();
+ iodev.state = CRAS_IODEV_STATE_OPEN;
+ req = CRAS_IODEV_RAMP_REQUEST_UP_UNMUTE;
+
+ rc = cras_iodev_start_ramp(&iodev, req);
+
+ // Device should start ramping up.
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, cras_ramp_start_is_called);
+ EXPECT_EQ(1, cras_ramp_start_is_up);
+ EXPECT_EQ(fmt.frame_rate * RAMP_DURATION_SECS,
+ cras_ramp_start_duration_frames);
+ // Callback for unmute is not used.
+ EXPECT_EQ(NULL, cras_ramp_start_cb);
+ // Device mute state is set after ramping starts.
+ EXPECT_EQ(1, cras_device_monitor_set_device_mute_state_called);
+ EXPECT_EQ(&iodev, cras_device_monitor_set_device_mute_state_dev);
+}
+
+TEST(IoDev, StartRampDown) {
+ struct cras_iodev iodev;
+ int rc;
+ struct cras_audio_format fmt;
+ enum CRAS_IODEV_RAMP_REQUEST req;
+ memset(&iodev, 0, sizeof(iodev));
+
+ // Format will be used in cras_iodev_start_ramp to determine ramp duration.
+ fmt.format = SND_PCM_FORMAT_S16_LE;
+ fmt.frame_rate = 48000;
+ fmt.num_channels = 2;
+ iodev.format = &fmt;
+
+ // Assume device has ramp member.
+ iodev.ramp = reinterpret_cast<struct cras_ramp*>(0x1);
+
+ // Case 1: Device is not opened yet.
+ ResetStubData();
+ iodev.state = CRAS_IODEV_STATE_CLOSE;
+ req = CRAS_IODEV_RAMP_REQUEST_DOWN_MUTE;
+
+ rc = cras_iodev_start_ramp(&iodev, req);
+
+ // Ramp request is ignored.
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(0, cras_ramp_start_is_called);
+
+ // Case 2: Ramp down for mute.
+ ResetStubData();
+ iodev.state = CRAS_IODEV_STATE_OPEN;
+ req = CRAS_IODEV_RAMP_REQUEST_DOWN_MUTE;
+
+ rc = cras_iodev_start_ramp(&iodev, req);
+
+ // Device should start ramping down with mute callback.
+ EXPECT_EQ(0, rc);
+ EXPECT_EQ(1, cras_ramp_start_is_called);
+ EXPECT_EQ(0, cras_ramp_start_is_up);
+ EXPECT_EQ(fmt.frame_rate * RAMP_DURATION_SECS,
+ cras_ramp_start_duration_frames);
+
+ // Device mute state is not set yet. It should wait for ramp to finish.
+ EXPECT_EQ(0, cras_device_monitor_set_device_mute_state_called);
+ EXPECT_EQ(NULL, cras_device_monitor_set_device_mute_state_dev);
+
+ // Assume the callback is set, and it is later called after ramp is done.
+ // It should trigger cras_device_monitor_set_device_mute_state.
+ cras_ramp_start_cb(cras_ramp_start_cb_data);
+ EXPECT_EQ(1, cras_device_monitor_set_device_mute_state_called);
+ EXPECT_EQ(&iodev, cras_device_monitor_set_device_mute_state_dev);
}
TEST(IoDev, OutputDeviceShouldWake) {
@@ -1723,10 +2131,23 @@ int cras_system_get_capture_mute() {
void cras_scale_buffer(snd_pcm_format_t fmt, uint8_t *buffer,
unsigned int count, float scaler) {
+ cras_scale_buffer_called++;
cras_scale_buffer_fmt = fmt;
cras_scale_buffer_scaler = scaler;
}
+void cras_scale_buffer_increment(snd_pcm_format_t fmt, uint8_t *buff,
+ unsigned int frame, float scaler,
+ float increment, int channel)
+{
+ cras_scale_buffer_increment_fmt = fmt;
+ cras_scale_buffer_increment_buff = buff;
+ cras_scale_buffer_increment_frame = frame;
+ cras_scale_buffer_increment_scaler = scaler;
+ cras_scale_buffer_increment_increment = increment;
+ cras_scale_buffer_increment_channel = channel;
+}
+
size_t cras_mix_mute_buffer(uint8_t *dst,
size_t frame_bytes,
size_t count) {
@@ -1782,6 +2203,44 @@ int cras_device_monitor_reset_device(struct cras_iodev *iodev) {
return 0;
}
+void cras_ramp_destroy(struct cras_ramp* ramp) {
+ return;
+}
+
+int cras_ramp_start(struct cras_ramp *ramp, int is_up, int duration_frames,
+ cras_ramp_cb cb, void *cb_data)
+{
+ cras_ramp_start_is_called++;
+ cras_ramp_start_is_up = is_up;
+ cras_ramp_start_duration_frames = duration_frames;
+ cras_ramp_start_cb = cb;
+ cras_ramp_start_cb_data = cb_data;
+ return 0;
+}
+
+int cras_ramp_reset(struct cras_ramp *ramp) {
+ cras_ramp_reset_is_called++;
+ return 0;
+}
+
+struct cras_ramp_action cras_ramp_get_current_action(
+ const struct cras_ramp *ramp) {
+ return cras_ramp_get_current_action_ret;
+}
+
+int cras_ramp_update_ramped_frames(
+ struct cras_ramp *ramp, int num_frames) {
+ cras_ramp_update_ramped_frames_num_frames = num_frames;
+ return 0;
+}
+
+int cras_device_monitor_set_device_mute_state(struct cras_iodev *iodev)
+{
+ cras_device_monitor_set_device_mute_state_called++;
+ cras_device_monitor_set_device_mute_state_dev = iodev;
+ return 0;
+}
+
} // extern "C"
} // namespace