Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 95 additions & 59 deletions src/audio/volume/volume.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,35 +39,33 @@
#include <stddef.h>
#include <stdint.h>

/* Shift to use in volume fractional multiplications */
#define Q_MUL_SHIFT Q_SHIFT_BITS_32(VOL_QXY_Y, VOL_QXY_Y, VOL_QXY_Y)

/**
* \brief Synchronize host mmap() volume with real value.
* \param[in,out] cd Volume component private data.
* \param[in] chan Channel number.
* \param[in] num_channels Update channels 0 to num_channels -1.
*/
static void vol_sync_host(struct comp_data *cd, uint32_t chan)
static void vol_sync_host(struct comp_data *cd, unsigned int num_channels)
{
if (!cd->hvol)
int n;

if (!cd->hvol) {
tracev_volume("vol_sync_host() Warning: null hvol, no update");
return;
}

if (chan < SOF_IPC_MAX_CHANNELS) {
cd->hvol[chan].value = cd->volume[chan];
if (num_channels < SOF_IPC_MAX_CHANNELS) {
for (n = 0; n < num_channels; n++)
cd->hvol[n].value = cd->volume[n];
} else {
trace_volume_error("vol_sync_host() error: "
"chan = %u < SOF_IPC_MAX_CHANNELS", chan);
trace_volume_error("vol_sync_host() error: channels count %d"
" exceeds SOF_IPC_MAX_CHANNELS",
num_channels);
}
}

/**
* \brief Update volume with target value.
* \param[in,out] cd Volume component private data.
* \param[in] chan Channel number.
*/
static void vol_update(struct comp_data *cd, uint32_t chan)
{
cd->volume[chan] = cd->tvolume[chan];
vol_sync_host(cd, chan);
}

/**
* \brief Ramps volume changes over time.
* \param[in,out] data Volume base component device.
Expand All @@ -82,10 +80,24 @@ static enum task_state vol_work(void *data)
int again = 0;
int i;

/* No need to ramp in idle state, jump volume to request. */
if (dev->state == COMP_STATE_READY) {
for (i = 0; i < PLATFORM_MAX_CHANNELS; i++)
cd->volume[i] = cd->tvolume[i];
Comment thread
mmaka1 marked this conversation as resolved.
Outdated

vol_sync_host(cd, PLATFORM_MAX_CHANNELS);
return SOF_TASK_STATE_COMPLETED;
}

/* The first is set and cleared to indicate ongoing ramp, the
* latter is set once to enable self launched ramp only once
* in stream start.
*/
cd->vol_ramp_active = true;
cd->ramp_started = true;
Comment thread
singalsu marked this conversation as resolved.
Outdated

/* inc/dec each volume if it's not at target */
for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) {
/* inc/dec each volume if it's not at target for active channels */
for (i = 0; i < cd->channels; i++) {
/* skip if target reached */
if (cd->volume[i] == cd->tvolume[i])
Comment thread
singalsu marked this conversation as resolved.
Outdated
continue;
Expand All @@ -98,8 +110,8 @@ static enum task_state vol_work(void *data)
if (cd->volume[i] < cd->tvolume[i]) {
/* ramp up, check if ramp completed */
if (vol >= cd->tvolume[i] || vol >= cd->vol_max) {
vol_update(cd, i);
cd->ramp_increment[i] = 0;
cd->volume[i] = cd->tvolume[i];
} else {
cd->volume[i] = vol;
again = 1;
Expand All @@ -108,25 +120,26 @@ static enum task_state vol_work(void *data)
/* ramp down */
if (vol <= 0) {
/* cannot ramp down below 0 */
vol_update(cd, i);
cd->ramp_increment[i] = 0;
cd->volume[i] = cd->tvolume[i];
} else {
/* ramp completed ? */
if (vol <= cd->tvolume[i] ||
vol <= cd->vol_min) {
vol_update(cd, i);
cd->ramp_increment[i] = 0;
cd->volume[i] = cd->tvolume[i];
} else {
cd->volume[i] = vol;
again = 1;
}
}
}

/* sync host with new value */
vol_sync_host(cd, i);
}

/* sync host with new value */
vol_sync_host(cd, cd->channels);

/* do we need to continue ramping */
if (again)
return SOF_TASK_STATE_RESCHEDULE;
Expand Down Expand Up @@ -228,6 +241,8 @@ static struct comp_dev *volume_new(struct sof_ipc_comp *comp)
}

cd->vol_ramp_active = false;
cd->channels = 0; /* To be set in prepare() */

trace_volume_with_ids(dev,
"vol->initial_ramp = %d, vol->ramp = %d, "
"vol->min_value = %d, vol->max_value = %d",
Expand Down Expand Up @@ -275,8 +290,12 @@ static int volume_params(struct comp_dev *dev,
* \param[in,out] dev Volume base component device.
* \param[in] chan Channel number.
* \param[in] vol Target volume.
* \param[in] constant_rate_ramp When true do a constant rate
* and variable time length ramp. When false do
* a fixed length and variable rate ramp.
Comment thread
singalsu marked this conversation as resolved.
Outdated
*/
static inline int volume_set_chan(struct comp_dev *dev, int chan, int32_t vol)
static inline int volume_set_chan(struct comp_dev *dev, int chan,
int32_t vol, bool constant_rate_ramp)
{
struct comp_data *cd = comp_get_drvdata(dev);
struct sof_ipc_comp_volume *pga =
Expand All @@ -285,7 +304,6 @@ static inline int volume_set_chan(struct comp_dev *dev, int chan, int32_t vol)
int32_t delta;
int32_t delta_abs;
int32_t inc;
int32_t t_us;

/* Limit received volume gain to MIN..MAX range before applying it.
* MAX is needed for now for the generic C gain arithmetics to prevent
Expand All @@ -311,39 +329,41 @@ static inline int volume_set_chan(struct comp_dev *dev, int chan, int32_t vol)
/* Check ramp type */
switch (pga->ramp) {
case SOF_VOLUME_LINEAR:

/* Assume the ramp length (initial_ramp [ms]) describes
* time of mute to 0 dB ramp. The actual volume scale min/max
* is not known. If initial_ramp is zero (or negative) use
* step size that reaches 0 dB gain in one step (VOL_ZERO_DB).
*/
if (pga->initial_ramp > 0)
inc = MAX(VOL_RAMP_STEP_CONST / pga->initial_ramp, 1);
else
inc = VOL_ZERO_DB;

/* Scale the increment with actual volume range if it was
* passed via IPC.
*/
if (cd->vol_ramp_range)
inc = q_multsr_32x32(inc, cd->vol_ramp_range,
Q_SHIFT_BITS_32(VOL_QXY_Y,
VOL_QXY_Y,
VOL_QXY_Y));

/* Get volume transition delta and absolute value */
delta = cd->tvolume[chan] - cd->volume[chan];
delta_abs = ABS(delta);

/* If the ramp completion would take longer than initial_ramp
* increase the step size to make this ramp complete at
* initial_ramp time. Calculate estimated ramp length [us].
/* The ramp length (initial_ramp [ms]) describes time of mute
* to vol_max unmuting. Normally the volume ramp has a
* constant linear slope defined this way and variable
* completion time. However in streaming start it is feasible
* to apply the entire topology defined ramp time to unmute to
* any used volume. In this case the ramp rate is not constant.
* Note also the legacy mode without known vol_ramp_range where
* the volume transition always uses the topology defined time.
*/
t_us = delta_abs * VOL_RAMP_UPDATE_US / inc;
if (t_us > pga->initial_ramp * 1000) {
inc = (int32_t)((int64_t)inc * delta_abs / VOL_ZERO_DB);
inc = MAX(inc, 1);
if (pga->initial_ramp > 0) {
if (constant_rate_ramp && cd->vol_ramp_range > 0)
inc = q_multsr_32x32(cd->vol_ramp_range,
VOL_RAMP_STEP_CONST,
Q_MUL_SHIFT);
else
inc = q_multsr_32x32(delta_abs,
VOL_RAMP_STEP_CONST,
Q_MUL_SHIFT);

/* Divide and round to nearest. Note that there will
* be some accumulated error in ramp time the longer
* the ramp and the smaller the transition is.
*/
inc = (2 * inc / pga->initial_ramp + 1) >> 1;
} else {
inc = delta_abs;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@singalsu The else part seems to be simplified as well. But yeah, I guess this is on purpose as well. It would seem "inc = delta_abs" is correct.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes the behavior is changed a bit with ramp time (length) set to zero. Earlier version used mute to 0 dB increment as step and with high gains the ramp length could become few milliseconds. It's a small mistake in previous version that I realized only now after your comment.

}

/* Ensure inc is at least one */
inc = MAX(inc, 1);
Comment thread
singalsu marked this conversation as resolved.
Outdated

/* Invert sign for volume down ramp step */
if (delta < 0)
inc = -inc;
Expand Down Expand Up @@ -375,7 +395,7 @@ static inline void volume_set_chan_mute(struct comp_dev *dev, int chan)

if (!cd->muted[chan]) {
cd->mvolume[chan] = cd->tvolume[chan];
volume_set_chan(dev, chan, 0);
volume_set_chan(dev, chan, 0, true);
cd->muted[chan] = true;
}
}
Expand All @@ -391,7 +411,7 @@ static inline void volume_set_chan_unmute(struct comp_dev *dev, int chan)

if (cd->muted[chan]) {
cd->muted[chan] = false;
volume_set_chan(dev, chan, cd->mvolume[chan]);
volume_set_chan(dev, chan, cd->mvolume[chan], true);
}
}

Expand Down Expand Up @@ -438,7 +458,7 @@ static int volume_ctrl_set_cmd(struct comp_dev *dev,
if (cd->muted[ch]) {
cd->mvolume[ch] = val;
} else {
ret = volume_set_chan(dev, ch, val);
ret = volume_set_chan(dev, ch, val, true);
if (ret)
return ret;
}
Expand Down Expand Up @@ -579,6 +599,10 @@ static int volume_copy(struct comp_dev *dev)

tracev_volume_with_ids(dev, "volume_copy()");

if (!cd->ramp_started)
schedule_task(&cd->volwork, VOL_RAMP_UPDATE_US,
VOL_RAMP_UPDATE_US);

/* Get source, sink, number of frames etc. to process. */
ret = comp_get_copy_limits(dev, &c);
if (ret < 0) {
Expand Down Expand Up @@ -652,8 +676,20 @@ static int volume_prepare(struct comp_dev *dev)
goto err;
}

for (i = 0; i < PLATFORM_MAX_CHANNELS; i++)
vol_sync_host(cd, i);
vol_sync_host(cd, PLATFORM_MAX_CHANNELS);

/* Set current volume to min to ensure ramp starts from minimum
* to previous volume request. Copy() checks for ramp started
* and schedules it if it has not yet started as result of
* driver commands. Ramp is not constant rate to ensure it lasts
* for entire topology specified time.
*/
cd->ramp_started = false;
cd->channels = sinkb->channels;
for (i = 0; i < cd->channels; i++) {
cd->volume[i] = cd->vol_min;
volume_set_chan(dev, i, cd->tvolume[i], false);
}

return 0;

Expand Down
9 changes: 6 additions & 3 deletions src/include/sof/audio/volume.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,13 @@ struct sof_ipc_ctrl_value_chan;
/**
* \brief Macro for volume linear gain ramp step computation
* Volume gain ramp step as Q1.16 is computed with equation
* step = VOL_RAMP_STEP_CONST/ SOF_TKN_VOLUME_RAMP_STEP_MS. This
* step = VOL_RAMP_STEP_CONST / SOF_TKN_VOLUME_RAMP_STEP_MS. This
* macro defines as Q1.16 value the constant term
* (1000 / VOL_RAMP_UPDATE) for step calculation.
* VOL_RAMP_UPDATE / 1000.0 for step calculation. The value 1000
* is used to to convert microseconds to milliseconds.
*/
#define VOL_RAMP_STEP_CONST \
Q_CONVERT_FLOAT(1000.0 / VOL_RAMP_UPDATE_US, VOL_QXY_Y)
Q_CONVERT_FLOAT(VOL_RAMP_UPDATE_US / 1000.0, VOL_QXY_Y)

/**
* \brief Volume maximum value.
Expand Down Expand Up @@ -110,8 +111,10 @@ struct comp_data {
int32_t vol_min; /**< minimum volume */
int32_t vol_max; /**< maximum volume */
int32_t vol_ramp_range; /**< max ramp transition */
unsigned int channels; /**< current channels count */
bool muted[SOF_IPC_MAX_CHANNELS]; /**< set if channel is muted */
bool vol_ramp_active; /**< set if volume is ramped */
bool ramp_started; /**< control ramp launch */
/**< volume processing function */
void (*scale_vol)(struct comp_dev *dev, struct comp_buffer *sink,
struct comp_buffer *source, uint32_t frames);
Expand Down
1 change: 1 addition & 0 deletions tools/ctl/eq_iir_highpass_20hz_0db_48khz.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
4607827,0,88,50380800,0,0,0,0,88,2,1,0,0,0,0,0,0,1,1,0,0,0,0,3225193546,2143508228,537150400,3220666496,537150400,0,32690,
1 change: 1 addition & 0 deletions tools/ctl/eq_iir_highpass_30hz_0db_48khz.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
4607827,0,88,50380800,0,0,0,0,88,2,1,0,0,0,0,0,0,1,1,0,0,0,0,3227172081,2141520527,536653443,3221660410,536653443,0,32690,
1 change: 1 addition & 0 deletions tools/ctl/eq_iir_highpass_40hz_0db_48khz.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
4607827,0,88,50380800,0,0,0,0,88,2,1,0,0,0,0,0,0,1,1,0,0,0,0,3229146956,2139532835,536156946,3222653403,536156946,0,32690,
1 change: 1 addition & 0 deletions tools/ctl/eq_iir_highpass_50hz_0db_48khz.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
4607827,0,88,50380800,0,0,0,0,88,2,1,0,0,0,0,0,0,1,1,0,0,0,0,3231118179,2137545158,535660909,3223645479,535660909,0,32690,
18 changes: 18 additions & 0 deletions tools/topology/m4/eq_iir_coef_highpass_20hz_0db_16khz.m4
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# 20 Hz second order high-pass, gain 0 dB, created with example_iir_eq.m 27-Nov-2019
CONTROLBYTES_PRIV(EQIIR_HP20HZ0dB16K_priv,
` bytes "0x53,0x4f,0x46,0x00,0x00,0x00,0x00,0x00,'
` 0x58,0x00,0x00,0x00,0x00,0xc0,0x00,0x03,'
` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00,'
` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,'
` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x3c,0xf9,0xb4,0xc0,'
` 0x79,0x05,0x4a,0x7f,0x92,0xf9,0xe5,0x1f,'
` 0xdc,0x0c,0x34,0xc0,0x92,0xf9,0xe5,0x1f,'
` 0x00,0x00,0x00,0x00,0xb2,0x7f,0x00,0x00"'
)
18 changes: 18 additions & 0 deletions tools/topology/m4/eq_iir_coef_highpass_20hz_0db_48khz.m4
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# 20 Hz second order high-pass, gain 0 dB, created with example_iir_eq.m 27-Nov-2019
CONTROLBYTES_PRIV(EQIIR_HP20HZ0dB48K_priv,
` bytes "0x53,0x4f,0x46,0x00,0x00,0x00,0x00,0x00,'
` 0x58,0x00,0x00,0x00,0x00,0xc0,0x00,0x03,'
` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00,'
` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,'
` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x4a,0x8c,0x3c,0xc0,'
` 0x04,0x57,0xc3,0x7f,0xc0,0x43,0x04,0x20,'
` 0x80,0x78,0xf7,0xbf,0xc0,0x43,0x04,0x20,'
` 0x00,0x00,0x00,0x00,0xb2,0x7f,0x00,0x00"'
)
18 changes: 18 additions & 0 deletions tools/topology/m4/eq_iir_coef_highpass_20hz_20db_16khz.m4
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# 20 Hz second order high-pass, gain 20 dB, created with example_iir_eq.m 27-Nov-2019
CONTROLBYTES_PRIV(EQIIR_HP20HZ20dB16K_priv,
` bytes "0x53,0x4f,0x46,0x00,0x00,0x00,0x00,0x00,'
` 0x58,0x00,0x00,0x00,0x00,0xc0,0x00,0x03,'
` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00,'
` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,'
` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x3c,0xf9,0xb4,0xc0,'
` 0x79,0x05,0x4a,0x7f,0x92,0xf9,0xe5,0x1f,'
` 0xdc,0x0c,0x34,0xc0,0x92,0xf9,0xe5,0x1f,'
` 0x00,0x00,0x00,0x00,0xb2,0x7f,0x00,0x00"'
)
18 changes: 18 additions & 0 deletions tools/topology/m4/eq_iir_coef_highpass_20hz_20db_48khz.m4
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# 20 Hz second order high-pass, gain 20 dB, created with example_iir_eq.m 27-Nov-2019
CONTROLBYTES_PRIV(EQIIR_HP20HZ20dB48K_priv,
` bytes "0x53,0x4f,0x46,0x00,0x00,0x00,0x00,0x00,'
` 0x58,0x00,0x00,0x00,0x00,0xc0,0x00,0x03,'
` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00,'
` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,'
` 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,'
` 0x00,0x00,0x00,0x00,0x4a,0x8c,0x3c,0xc0,'
` 0x04,0x57,0xc3,0x7f,0xc0,0x43,0x04,0x20,'
` 0x80,0x78,0xf7,0xbf,0xc0,0x43,0x04,0x20,'
` 0x00,0x00,0x00,0x00,0xb2,0x7f,0x00,0x00"'
)
Loading