audioconvert: add support of volume ramp scale parameter

- Add support for Linear and Cubic volume scales
- Also start treating a "zero" value of volume ramp parameters as invalid
This commit is contained in:
Ashok Sidipotu 2023-03-28 10:38:41 +05:30 committed by Wim Taymans
parent 7b91d506f1
commit 8bfafaeca2
3 changed files with 78 additions and 47 deletions

View file

@ -261,6 +261,11 @@ enum spa_audio_channel {
SPA_AUDIO_CHANNEL_START_Custom = 0x10000, SPA_AUDIO_CHANNEL_START_Custom = 0x10000,
}; };
enum spa_audio_volume_ramp_scale {
SPA_AUDIO_VOLUME_RAMP_LINEAR,
SPA_AUDIO_VOLUME_RAMP_CUBIC,
};
/** Extra audio flags */ /** Extra audio flags */
#define SPA_AUDIO_FLAG_NONE (0) /*< no valid flag */ #define SPA_AUDIO_FLAG_NONE (0) /*< no valid flag */
#define SPA_AUDIO_FLAG_UNPOSITIONED (1 << 0) /*< the position array explicitly #define SPA_AUDIO_FLAG_UNPOSITIONED (1 << 0) /*< the position array explicitly

View file

@ -59,15 +59,11 @@ enum spa_prop {
SPA_PROP_START_Audio = 0x10000, /**< audio related properties */ SPA_PROP_START_Audio = 0x10000, /**< audio related properties */
SPA_PROP_waveType, SPA_PROP_waveType,
SPA_PROP_frequency, SPA_PROP_frequency,
SPA_PROP_volume, /**< a volume (Float), 0.0 silence, 1.0 normal */ SPA_PROP_volume, /**< a volume (Float), 0.0 silence, 1.0 normal */
SPA_PROP_volumeRampSamples, /**< Samples to ramp the volume over SPA_PROP_volumeRampSamples, /**< Samples to ramp the volume over */
*-1 is invalid, 0 ramps over instantly*/ SPA_PROP_volumeRampStepSamples, /**< Step or incremental Samples to ramp the volume over */
SPA_PROP_volumeRampStepSamples, /**< Step or incremental Samples to ramp the volume over SPA_PROP_volumeRampTime, /**< Time in millisec to ramp the volume over */
*-1 is invalid, 0 ramps over instantly */ SPA_PROP_volumeRampStepTime, /**< Step or incremental Time in nano seconds to ramp the */
SPA_PROP_volumeRampTime, /**< Time in millisec to ramp the volume over
*-1 is invalid, 0 ramps over instantly*/
SPA_PROP_volumeRampStepTime, /**< Step or incremental Time in nano seconds to ramp the
*volume over -1 is invalid, 0 ramps over instantly */
SPA_PROP_volumeRampScale, /**< the scale or graph to used to ramp the volume */ SPA_PROP_volumeRampScale, /**< the scale or graph to used to ramp the volume */
SPA_PROP_mute, /**< mute (Bool) */ SPA_PROP_mute, /**< mute (Bool) */
SPA_PROP_patternType, SPA_PROP_patternType,

View file

@ -46,8 +46,6 @@ static struct spa_log_topic *log_topic = &SPA_LOG_TOPIC(0, "spa.audioconvert");
#define DEFAULT_MUTE false #define DEFAULT_MUTE false
#define DEFAULT_VOLUME VOLUME_NORM #define DEFAULT_VOLUME VOLUME_NORM
#define INVALID -1
struct volumes { struct volumes {
bool mute; bool mute;
uint32_t n_volumes; uint32_t n_volumes;
@ -71,10 +69,11 @@ struct props {
struct volumes channel; struct volumes channel;
struct volumes soft; struct volumes soft;
struct volumes monitor; struct volumes monitor;
int volume_ramp_samples; unsigned int volume_ramp_samples;
int volume_ramp_step_samples; unsigned int volume_ramp_step_samples;
int volume_ramp_time; unsigned int volume_ramp_time;
int volume_ramp_step_time; unsigned int volume_ramp_step_time;
enum spa_audio_volume_ramp_scale scale;
unsigned int have_soft_volume:1; unsigned int have_soft_volume:1;
unsigned int mix_disabled:1; unsigned int mix_disabled:1;
unsigned int resample_disabled:1; unsigned int resample_disabled:1;
@ -871,9 +870,9 @@ static int parse_prop_params(struct impl *this, struct spa_pod *params)
static unsigned int get_ramp_samples(struct impl *this) static unsigned int get_ramp_samples(struct impl *this)
{ {
struct props p = this->props; struct props p = this->props;
if (p.volume_ramp_samples != INVALID) if (p.volume_ramp_samples)
return p.volume_ramp_samples; return p.volume_ramp_samples;
else if (p.volume_ramp_time != INVALID) { else if (p.volume_ramp_time) {
struct dir *d = &this->dir[SPA_DIRECTION_OUTPUT]; struct dir *d = &this->dir[SPA_DIRECTION_OUTPUT];
unsigned int sample_rate = d->format.info.raw.rate; unsigned int sample_rate = d->format.info.raw.rate;
unsigned int samples = (p.volume_ramp_time * sample_rate) / 1000; unsigned int samples = (p.volume_ramp_time * sample_rate) / 1000;
@ -886,9 +885,9 @@ static unsigned int get_ramp_samples(struct impl *this)
static unsigned int get_ramp_step_samples(struct impl *this) static unsigned int get_ramp_step_samples(struct impl *this)
{ {
struct props p = this->props; struct props p = this->props;
if (p.volume_ramp_step_samples != INVALID) if (p.volume_ramp_step_samples)
return p.volume_ramp_step_samples; return p.volume_ramp_step_samples;
else if (p.volume_ramp_step_time != INVALID) { else if (p.volume_ramp_step_time) {
struct dir *d = &this->dir[SPA_DIRECTION_OUTPUT]; struct dir *d = &this->dir[SPA_DIRECTION_OUTPUT];
int sample_rate = d->format.info.raw.rate; int sample_rate = d->format.info.raw.rate;
/* convert the step time which is in nano seconds to seconds */ /* convert the step time which is in nano seconds to seconds */
@ -899,6 +898,17 @@ static unsigned int get_ramp_step_samples(struct impl *this)
return 0; return 0;
} }
static double get_volume_at_scale(struct impl *this, double value)
{
struct props p = this->props;
if (p.scale == SPA_AUDIO_VOLUME_RAMP_LINEAR)
return value;
else if (p.scale == SPA_AUDIO_VOLUME_RAMP_CUBIC)
return (value * value * value);
return 0.0;
}
static struct spa_pod *generate_vol_ramp_up_sequence(struct impl *this, const struct props *p) static struct spa_pod *generate_vol_ramp_up_sequence(struct impl *this, const struct props *p)
{ {
uint8_t buffer[64 * 1024 * 10]; uint8_t buffer[64 * 1024 * 10];
@ -912,15 +922,16 @@ static struct spa_pod *generate_vol_ramp_up_sequence(struct impl *this, const st
spa_pod_builder_init(&b, buffer, sizeof(buffer)); spa_pod_builder_init(&b, buffer, sizeof(buffer));
spa_pod_builder_push_sequence(&b, &f[0], 0); spa_pod_builder_push_sequence(&b, &f[0], 0);
spa_log_info(this->log, "generating volume ramp up sequence from %f to %f with a step value %f", spa_log_info(this->log, "generating ramp up sequence from %f to %f with a step value %f at scale %d",
p->prev_volume,p->volume, volume_step); p->prev_volume,p->volume, volume_step, p->scale);
do { do {
// spa_log_info(this->log, "volume level %f offset %d", volume_accum, volume_offs); // spa_log_info(this->log, "volume level %f offset %d", get_volume_at_scale(this, volume_accum), volume_offs);
spa_pod_builder_control(&b, volume_offs, SPA_CONTROL_Properties); spa_pod_builder_control(&b, volume_offs, SPA_CONTROL_Properties);
spa_pod_builder_add_object(&b, spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_Props, 0, SPA_TYPE_OBJECT_Props, 0,
SPA_PROP_volume, SPA_POD_Float(volume_accum)); SPA_PROP_volume,
SPA_POD_Float(get_volume_at_scale(this, volume_accum)));
volume_accum += volume_step; volume_accum += volume_step;
volume_offs += ramp_step_samples; volume_offs += ramp_step_samples;
} while (volume_accum < p->volume); } while (volume_accum < p->volume);
@ -941,14 +952,16 @@ static struct spa_pod *generate_vol_ramp_down_sequence(struct impl *this, const
spa_pod_builder_init(&b, buffer, sizeof(buffer)); spa_pod_builder_init(&b, buffer, sizeof(buffer));
spa_pod_builder_push_sequence(&b, &f[0], 0); spa_pod_builder_push_sequence(&b, &f[0], 0);
spa_log_info(this->log, "generating volume ramp down sequence from %f to %f with a step value %f", spa_log_info(this->log, "generating ramp down sequence from %f to %f with a step value %f at scale %d",
p->prev_volume, p->volume, volume_step); p->prev_volume, p->volume, volume_step, p->scale);
do { do {
// spa_log_info(this->log, "volume level %f offset %d", volume_accum, volume_offs); // spa_log_info(this->log, "volume level %f offset %d", get_volume_at_scale(this, volume_accum), volume_offs);
spa_pod_builder_control(&b, volume_offs, SPA_CONTROL_Properties); spa_pod_builder_control(&b, volume_offs, SPA_CONTROL_Properties);
spa_pod_builder_add_object(&b, spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_Props, 0, SPA_TYPE_OBJECT_Props, 0,
SPA_PROP_volume, SPA_POD_Float(volume_accum)); SPA_PROP_volume,
SPA_POD_Float(get_volume_at_scale(this, volume_accum)));
volume_accum -= volume_step; volume_accum -= volume_step;
volume_offs += ramp_step_samples; volume_offs += ramp_step_samples;
} while (volume_accum > p->volume); } while (volume_accum > p->volume);
@ -974,15 +987,16 @@ static int apply_props(struct impl *this, const struct spa_pod *param)
int changed = 0; int changed = 0;
uint32_t n; uint32_t n;
int32_t value; int32_t value;
uint32_t id;
SPA_POD_OBJECT_FOREACH(obj, prop) { SPA_POD_OBJECT_FOREACH(obj, prop) {
switch (prop->key) { switch (prop->key) {
case SPA_PROP_volume: case SPA_PROP_volume:
p->prev_volume = p->volume; p->prev_volume = p->volume;
spa_log_debug(this->log, "previous volume %f", p->prev_volume); spa_log_debug(this->log, "%p previous volume %f", this, p->prev_volume);
if (spa_pod_get_float(&prop->value, &p->volume) == 0) { if (spa_pod_get_float(&prop->value, &p->volume) == 0) {
spa_log_debug(this->log, "current volume %f", p->volume); spa_log_debug(this->log, "%p current volume %f", this, p->volume);
if (p->volume_ramp_enabled && !this->vol_ramp_sequence) { if (p->volume_ramp_enabled && !this->vol_ramp_sequence) {
this->vol_ramp_sequence = (struct spa_pod_sequence *) generate_vol_ramp_sequence (this, p); this->vol_ramp_sequence = (struct spa_pod_sequence *) generate_vol_ramp_sequence (this, p);
if (!this->vol_ramp_sequence) { if (!this->vol_ramp_sequence) {
@ -1004,55 +1018,71 @@ static int apply_props(struct impl *this, const struct spa_pod *param)
case SPA_PROP_volumeRampSamples: case SPA_PROP_volumeRampSamples:
if (p->volume_ramp_enabled && this->vol_ramp_sequence) { if (p->volume_ramp_enabled && this->vol_ramp_sequence) {
spa_log_error(this->log,"volume ramp sequence is being applied try again"); spa_log_error(this->log, "%p volume ramp sequence is being "
"applied try again", this);
return EAGAIN; return EAGAIN;
} }
if (spa_pod_get_int(&prop->value, &value) == 0 && value != INVALID) { if (spa_pod_get_int(&prop->value, &value) == 0 && value) {
p->volume_ramp_samples = value; p->volume_ramp_samples = value;
p->volume_ramp_time = INVALID; spa_log_info(this->log, "%p volume ramp samples %d", this,
spa_log_info(this->log, "volume ramp samples %d", p->volume_ramp_samples); p->volume_ramp_samples);
p->volume_ramp_enabled = true; p->volume_ramp_enabled = true;
} }
break; break;
case SPA_PROP_volumeRampStepSamples: case SPA_PROP_volumeRampStepSamples:
if (p->volume_ramp_enabled && this->vol_ramp_sequence) { if (p->volume_ramp_enabled && this->vol_ramp_sequence) {
spa_log_error(this->log, "volume ramp sequence is being applied try again"); spa_log_error(this->log, "%p volume ramp sequence is being "
"applied try again", this);
return EAGAIN; return EAGAIN;
} }
if (spa_pod_get_int(&prop->value, &value) == 0 && value != INVALID) { if (spa_pod_get_int(&prop->value, &value) == 0 && value) {
p->volume_ramp_step_samples = value; p->volume_ramp_step_samples = value;
p->volume_ramp_step_time = INVALID; spa_log_info(this->log, "%p volume ramp step samples is %d",
spa_log_info(this->log, "volume ramp step samples is %d", this, p->volume_ramp_step_samples);
p->volume_ramp_step_samples);
p->volume_ramp_enabled = true; p->volume_ramp_enabled = true;
} }
break; break;
case SPA_PROP_volumeRampTime: case SPA_PROP_volumeRampTime:
if (p->volume_ramp_enabled && this->vol_ramp_sequence) { if (p->volume_ramp_enabled && this->vol_ramp_sequence) {
spa_log_error(this->log, "volume ramp sequence is being applied try again"); spa_log_error(this->log, "%p volume ramp sequence is being "
"applied try again", this);
return EAGAIN; return EAGAIN;
} }
if (spa_pod_get_int(&prop->value, &value) == 0 && value != INVALID) { if (spa_pod_get_int(&prop->value, &value) == 0 && value) {
p->volume_ramp_time = value; p->volume_ramp_time = value;
p->volume_ramp_samples = INVALID; spa_log_info(this->log, "%p volume ramp time %d", this,
spa_log_info(this->log, "volume ramp time %d", p->volume_ramp_time); p->volume_ramp_time);
p->volume_ramp_enabled = true; p->volume_ramp_enabled = true;
} }
break; break;
case SPA_PROP_volumeRampStepTime: case SPA_PROP_volumeRampStepTime:
if (p->volume_ramp_enabled && this->vol_ramp_sequence) { if (p->volume_ramp_enabled && this->vol_ramp_sequence) {
spa_log_error(this->log, "volume ramp sequence is being applied try again"); spa_log_error(this->log, "%p volume ramp sequence is being "
"applied try again", this);
return EAGAIN; return EAGAIN;
} }
if (spa_pod_get_int(&prop->value, &value) == 0 && value != INVALID) { if (spa_pod_get_int(&prop->value, &value) == 0 && value) {
p->volume_ramp_step_time = value; p->volume_ramp_step_time = value;
spa_log_info(this->log, "volume ramp step time %d", spa_log_info(this->log, "%p volume ramp time %d", this,
p->volume_ramp_step_time); p->volume_ramp_step_samples);
p->volume_ramp_step_samples = INVALID; p->volume_ramp_enabled = true;
}
break;
case SPA_PROP_volumeRampScale:
if (p->volume_ramp_enabled && this->vol_ramp_sequence) {
spa_log_error(this->log, "%p volume ramp sequence is being "
"applied try again", this);
return EAGAIN;
}
if (spa_pod_get_id(&prop->value, &id) == 0) {
p->scale = id;
spa_log_info(this->log, "%p volume ramp scale %d", this,
p->scale);
p->volume_ramp_enabled = true; p->volume_ramp_enabled = true;
} }
break; break;