blob: f98e5e06e2c00d1a72c2c5498df3c744fa87db76 [file] [log] [blame]
/*
* Copyright (c) 2016, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Intel Corporation nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
* Liam Girdwood <liam.r.girdwood@linux.intel.com>
* Keyon Jie <yang.jie@linux.intel.com>
*/
#include <stdint.h>
#include <stddef.h>
#include <errno.h>
#include <stdbool.h>
#include <sof/sof.h>
#include <sof/lock.h>
#include <sof/list.h>
#include <sof/stream.h>
#include <sof/alloc.h>
#include <sof/work.h>
#include <sof/clock.h>
#include <sof/audio/component.h>
#include <sof/audio/format.h>
#include <sof/audio/pipeline.h>
#include <sof/math/trig.h>
#include <uapi/ipc.h>
#include <uapi/tone.h>
#ifdef MODULE_TEST
#include <stdio.h>
#endif
#define trace_tone(__e) trace_event(TRACE_CLASS_TONE, __e)
#define tracev_tone(__e) tracev_event(TRACE_CLASS_TONE, __e)
#define trace_tone_error(__e) trace_error(TRACE_CLASS_TONE, __e)
/* Convert float frequency in Hz to Q16.16 fractional format */
#define TONE_FREQ(f) Q_CONVERT_FLOAT(f, 16)
/* Convert float gain to Q1.31 fractional format */
#define TONE_GAIN(v) Q_CONVERT_FLOAT(v, 31)
/* Set default tone amplitude and frequency */
#define TONE_AMPLITUDE_DEFAULT TONE_GAIN(0.1) /* -20 dB */
#define TONE_FREQUENCY_DEFAULT TONE_FREQ(997.0)
#define TONE_NUM_FS 13 /* Table size for 8-192 kHz range */
/* 2*pi/Fs lookup tables in Q1.31 for each Fs */
static const int32_t tone_fs_list[TONE_NUM_FS] = {
8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000,
64000, 88200, 96000, 176400, 192000
};
static const int32_t tone_pi2_div_fs[TONE_NUM_FS] = {
1686630, 1223858, 843315, 611929, 562210, 421657, 305965,
281105, 210829, 152982, 140552, 76491, 70276
};
/* tone component private data */
struct tone_state {
int mute;
int32_t a; /* Current amplitude Q1.31 */
int32_t a_target; /* Target amplitude Q1.31 */
int32_t ampl_coef; /* Amplitude multiplier Q2.30 */
int32_t c; /* Coefficient 2*pi/Fs Q1.31 */
int32_t f; /* Frequency Q16.16 */
int32_t freq_coef; /* Frequency multiplier Q2.30 */
int32_t fs; /* Sample rate in Hertz Q32.0 */
int32_t ramp_step; /* Amplitude ramp step Q1.31 */
int32_t w; /* Angle radians Q4.28 */
int32_t w_step; /* Angle step Q4.28 */
uint32_t block_count;
uint32_t repeat_count;
uint32_t repeats; /* Number of repeats for tone (sweep steps) */
uint32_t sample_count;
uint32_t samples_in_block; /* Samples in 125 us block */
uint32_t tone_length; /* Active length in 125 us blocks */
uint32_t tone_period; /* Active + idle time in 125 us blocks */
};
struct comp_data {
uint32_t period_bytes;
uint32_t channels;
uint32_t frame_bytes;
uint32_t rate;
struct tone_state sg[PLATFORM_MAX_CHANNELS];
void (*tone_func)(struct comp_dev *dev, struct comp_buffer *sink,
uint32_t frames);
};
static int32_t tonegen(struct tone_state *sg);
static void tonegen_control(struct tone_state *sg);
static void tonegen_update_f(struct tone_state *sg, int32_t f);
/*
* Tone generator algorithm code
*/
static inline void tone_circ_inc_wrap(int32_t **ptr, int32_t *end, size_t size)
{
if (*ptr >= end)
*ptr = (int32_t *) ((size_t) * ptr - size);
}
static void tone_s32_default(struct comp_dev *dev, struct comp_buffer *sink,
uint32_t frames)
{
struct comp_data *cd = comp_get_drvdata(dev);
int32_t *dest = (int32_t*) sink->w_ptr;
int i;
int n;
int n_wrap_dest;
int n_min;
int nch = cd->channels;
n = frames * nch;
while (n > 0) {
n_wrap_dest = (int32_t *) sink->end_addr - dest;
n_min = (n < n_wrap_dest) ? n : n_wrap_dest;
/* Process until wrap or completed n */
while (n_min > 0) {
n -= nch;
n_min -= nch;
for (i = 0; i < nch; i++) {
tonegen_control(&cd->sg[i]);
*dest = tonegen(&cd->sg[i]);
dest++;
}
}
tone_circ_inc_wrap(&dest, sink->end_addr, sink->size);
}
}
static int32_t tonegen(struct tone_state *sg)
{
int64_t sine;
int64_t w;
/* sg->w is angle in Q4.28 radians format, sin() returns Q1.31 */
/* sg->a is amplitude as Q1.31 */
sine =
q_mults_32x32(sin_fixed(sg->w), sg->a,
Q_SHIFT_BITS_64(31, 31, 31));
/* Next point */
w = (int64_t) sg->w + sg->w_step;
sg->w = (w > PI_MUL2_Q4_28)
? (int32_t) (w - PI_MUL2_Q4_28) : (int32_t) w;
if (sg->mute)
return 0;
else
return(int32_t) sine; /* Q1.31 no saturation need */
}
static void tonegen_control(struct tone_state *sg)
{
int64_t a;
int64_t p;
/* Count samples, 125 us blocks */
sg->sample_count++;
if (sg->sample_count < sg->samples_in_block)
return;
sg->sample_count = 0;
if (sg->block_count < INT32_MAX)
sg->block_count++;
/* Fade-in ramp during tone */
if (sg->block_count < sg->tone_length) {
if (sg->a == 0)
sg->w = 0; /* Reset phase to have less clicky ramp */
if (sg->a > sg->a_target) {
a = (int64_t) sg->a - sg->ramp_step;
if (a < sg->a_target)
a = sg->a_target;
} else {
a = (int64_t) sg->a + sg->ramp_step;
if (a > sg->a_target)
a = sg->a_target;
}
sg->a = (int32_t) a;
}
/* Fade-out ramp after tone*/
if (sg->block_count > sg->tone_length) {
a = (int64_t) sg->a - sg->ramp_step;
if (a < 0)
a = 0;
sg->a = (int32_t) a;
}
/* New repeated tone, update for frequency or amplitude sweep */
if ((sg->block_count > sg->tone_period)
&& (sg->repeat_count + 1 < sg->repeats)) {
sg->block_count = 0;
if (sg->ampl_coef > 0) {
sg->a_target =
sat_int32(q_multsr_32x32(sg->a_target,
sg->ampl_coef, Q_SHIFT_BITS_64(31, 30, 31)));
sg->a = (sg->ramp_step > sg->a_target)
? sg->a_target : sg->ramp_step;
}
if (sg->freq_coef > 0) {
/* f is Q16.16, freq_coef is Q2.30 */
p = q_multsr_32x32(sg->f, sg->freq_coef,
Q_SHIFT_BITS_64(16, 30, 16));
tonegen_update_f(sg, (int32_t) p); /* No saturation */
}
sg->repeat_count++;
}
}
/* Set sine amplitude */
static inline void tonegen_set_a(struct tone_state *sg, int32_t a)
{
sg->a_target = a;
}
/* Repeated number of beeps */
static void tonegen_set_repeats(struct tone_state *sg, uint32_t r)
{
sg->repeats = r;
}
/* The next functions support zero as shortcut for defaults to get
* make a nicer API without need to remember the neutral steady
* non-swept tone settings.
*/
/* Multiplication factor for frequency as Q2.30 for logarithmic change */
static void tonegen_set_freq_mult(struct tone_state *sg, int32_t fm)
{
sg->freq_coef = (fm > 0) ? fm : ONE_Q2_30; /* Set freq mult to 1.0 */
}
/* Multiplication factor for amplitude as Q2.30 for logarithmic change */
static void tonegen_set_ampl_mult(struct tone_state *sg, int32_t am)
{
sg->ampl_coef = (am > 0) ? am : ONE_Q2_30; /* Set ampl mult to 1.0 */
}
/* Tone length in samples, this is the active length of tone */
static void tonegen_set_length(struct tone_state *sg, uint32_t tl)
{
sg->tone_length = (tl > 0) ? tl : INT32_MAX; /* Count rate 125 us */
}
/* Tone period in samples, this is the length including the pause after beep */
static void tonegen_set_period(struct tone_state *sg, uint32_t tp)
{
sg->tone_period = (tp > 0) ? tp : INT32_MAX; /* Count rate 125 us */
}
/* Tone ramp parameters:
* step - Value that is added or subtracted to amplitude. A zero or negative
* number disables the ramp and amplitude is immediately modified to
* final value.
*/
static inline void tonegen_set_linramp(struct tone_state *sg, int32_t step)
{
sg->ramp_step = (step > 0) ? step : INT32_MAX;
}
static inline int32_t tonegen_get_f(struct tone_state *sg)
{
return sg->f;
}
static inline int32_t tonegen_get_a(struct tone_state *sg)
{
return sg->a_target;
}
static inline void tonegen_mute(struct tone_state *sg)
{
sg->mute = 1;
}
static inline void tonegen_unmute(struct tone_state *sg)
{
sg->mute = 0;
}
static void tonegen_update_f(struct tone_state *sg, int32_t f)
{
int64_t w_tmp;
int64_t f_max;
/* Calculate Fs/2, fs is Q32.0, f is Q16.16 */
f_max = Q_SHIFT_LEFT((int64_t) sg->fs, 0, 16 - 1);
f_max = (f_max > INT32_MAX) ? INT32_MAX : f_max;
sg->f = (f > f_max) ? f_max : f;
/* Q16 x Q31 -> Q28 */
w_tmp = q_multsr_32x32(sg->f, sg->c, Q_SHIFT_BITS_64(16, 31, 28));
w_tmp = (w_tmp > PI_Q4_28) ? PI_Q4_28 : w_tmp; /* Limit to pi Q4.28 */
sg->w_step = (int32_t) w_tmp;
#ifdef MODULE_TEST
printf("Fs=%d, f_max=%d, f_new=%.3f\n",
sg->fs, (int32_t) (f_max >> 16), sg->f / 65536.0);
#endif
}
static void tonegen_reset(struct tone_state *sg)
{
sg->mute = 1;
sg->a = 0;
sg->a_target = TONE_AMPLITUDE_DEFAULT;
sg->c = 0;
sg->f = TONE_FREQUENCY_DEFAULT;
sg->w = 0;
sg->w_step = 0;
sg->block_count = 0;
sg->repeat_count = 0;
sg->repeats = 0;
sg->sample_count = 0;
sg->samples_in_block = 0;
/* Continuous tone */
sg->freq_coef = ONE_Q2_30; /* Set freq multiplier to 1.0 */
sg->ampl_coef = ONE_Q2_30; /* Set ampl multiplier to 1.0 */
sg->tone_length = INT32_MAX;
sg->tone_period = INT32_MAX;
sg->ramp_step = ONE_Q1_31; /* Set lin ramp modification to max */
}
static int tonegen_init(struct tone_state *sg, int32_t fs, int32_t f, int32_t a)
{
int idx;
int i;
sg->a_target = a;
sg->a = (sg->ramp_step > sg->a_target) ? sg->a_target : sg->ramp_step;
idx = -1;
sg->mute = 1;
sg->fs = 0;
/* Find index of current sample rate and then get from lookup table the
* corresponding 2*pi/Fs value.
*/
for (i = 0; i < TONE_NUM_FS; i++) {
if (fs == tone_fs_list[i])
idx = i;
}
if (idx < 0) {
sg->w_step = 0;
return -EINVAL;
}
sg->fs = fs;
sg->c = tone_pi2_div_fs[idx]; /* Store 2*pi/Fs */
sg->mute = 0;
tonegen_update_f(sg, f);
/* 125us as Q1.31 is 268435, calculate fs * 125e-6 in Q31.0 */
sg->samples_in_block =
(int32_t) q_multsr_32x32(fs, 268435, Q_SHIFT_BITS_64(0, 31, 0));
return 0;
}
/*
* End of algorithm code. Next the standard component methods.
*/
static struct comp_dev *tone_new(struct sof_ipc_comp *comp)
{
struct comp_dev *dev;
struct sof_ipc_comp_tone *tone;
struct sof_ipc_comp_tone *ipc_tone = (struct sof_ipc_comp_tone *) comp;
struct comp_data *cd;
int i;
trace_tone("new");
dev = rzalloc(RZONE_RUNTIME, SOF_MEM_CAPS_RAM,
COMP_SIZE(struct sof_ipc_comp_tone));
if (dev == NULL)
return NULL;
tone = (struct sof_ipc_comp_tone *) &dev->comp;
memcpy(tone, ipc_tone, sizeof(struct sof_ipc_comp_tone));
cd = rzalloc(RZONE_RUNTIME, SOF_MEM_CAPS_RAM, sizeof(*cd));
if (cd == NULL) {
rfree(dev);
return NULL;
}
comp_set_drvdata(dev, cd);
cd->tone_func = tone_s32_default;
cd->rate = ipc_tone->sample_rate;
/* Reset tone generator and set channels volumes to default */
for (i = 0; i < PLATFORM_MAX_CHANNELS; i++)
tonegen_reset(&cd->sg[i]);
dev->state = COMP_STATE_READY;
return dev;
}
static void tone_free(struct comp_dev *dev)
{
struct tone_data *td = comp_get_drvdata(dev);
trace_tone("fre");
rfree(td);
rfree(dev);
}
/* set component audio stream parameters */
static int tone_params(struct comp_dev *dev)
{
struct comp_data *cd = comp_get_drvdata(dev);
struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev);
trace_tone("par");
/* Tone supports only S32_LE PCM format atm */
if (config->frame_fmt != SOF_IPC_FRAME_S32_LE)
return -EINVAL;
trace_value(config->frame_fmt);
dev->params.frame_fmt = config->frame_fmt;
/* Need to compute this in non-host endpoint */
dev->frame_bytes = comp_frame_bytes(dev);
/* calculate period size based on config */
cd->period_bytes = dev->frames * dev->frame_bytes;
return 0;
}
static int tone_cmd_get_value(struct comp_dev *dev,
struct sof_ipc_ctrl_data *cdata)
{
struct comp_data *cd = comp_get_drvdata(dev);
int j;
trace_tone("mgt");
if (cdata->cmd == SOF_CTRL_CMD_SWITCH) {
for (j = 0; j < cdata->num_elems; j++) {
cdata->chanv[j].channel = j;
cdata->chanv[j].value = !cd->sg[j].mute;
trace_value(j);
trace_value(cd->sg[j].mute);
}
}
return 0;
}
static int tone_cmd_set_value(struct comp_dev *dev, struct sof_ipc_ctrl_data *cdata)
{
struct comp_data *cd = comp_get_drvdata(dev);
int j;
uint32_t ch;
bool val;
if (cdata->cmd == SOF_CTRL_CMD_SWITCH) {
trace_tone("mst");
for (j = 0; j < cdata->num_elems; j++) {
ch = cdata->chanv[j].channel;
val = cdata->chanv[j].value;
tracev_value(ch);
tracev_value(val);
if (ch >= PLATFORM_MAX_CHANNELS) {
trace_tone_error("che");
return -EINVAL;
}
if (val)
tonegen_unmute(&cd->sg[ch]);
else
tonegen_mute(&cd->sg[ch]);
}
} else {
trace_tone_error("ste");
return -EINVAL;
}
return 0;
}
static int tone_cmd_set_data(struct comp_dev *dev,
struct sof_ipc_ctrl_data *cdata)
{
struct comp_data *cd = comp_get_drvdata(dev);
struct sof_ipc_ctrl_value_comp *compv;
int i;
uint32_t ch;
uint32_t val;
trace_tone("tri");
/* Check version from ABI header */
if (cdata->data->comp_abi != SOF_TONE_ABI_VERSION) {
trace_tone_error("abi");
return -EINVAL;
}
switch (cdata->cmd) {
case SOF_CTRL_CMD_ENUM:
trace_tone("ten");
trace_value(cdata->index);
compv = (struct sof_ipc_ctrl_value_comp *) cdata->data->data;
for (i = 0; i < (int) cdata->num_elems; i++) {
ch = compv[i].index;
val = compv[i].svalue;
tracev_value(ch);
tracev_value(val);
switch (cdata->index) {
case SOF_TONE_IDX_FREQUENCY:
trace_tone("tfr");
tonegen_update_f(&cd->sg[ch], val);
break;
case SOF_TONE_IDX_AMPLITUDE:
trace_tone("tam");
tonegen_set_a(&cd->sg[ch], val);
break;
case SOF_TONE_IDX_FREQ_MULT:
trace_tone("tfx");
tonegen_set_freq_mult(&cd->sg[ch], val);
break;
case SOF_TONE_IDX_AMPL_MULT:
trace_tone("tax");
tonegen_set_ampl_mult(&cd->sg[ch], val);
break;
case SOF_TONE_IDX_LENGTH:
trace_tone("tle");
tonegen_set_length(&cd->sg[ch], val);
break;
case SOF_TONE_IDX_PERIOD:
trace_tone("tpe");
tonegen_set_period(&cd->sg[ch], val);
break;
case SOF_TONE_IDX_REPEATS:
trace_tone("trp");
tonegen_set_repeats(&cd->sg[ch], val);
break;
case SOF_TONE_IDX_LIN_RAMP_STEP:
trace_tone("trs");
tonegen_set_linramp(&cd->sg[ch], val);
break;
default:
trace_tone_error("ier");
return -EINVAL;
}
}
break;
default:
trace_tone_error("ec1");
return -EINVAL;
}
return 0;
}
/* used to pass standard and bespoke commands (with data) to component */
static int tone_cmd(struct comp_dev *dev, int cmd, void *data)
{
struct sof_ipc_ctrl_data *cdata = data;
int ret = 0;
trace_tone("cmd");
switch (cmd) {
case COMP_CMD_SET_DATA:
ret = tone_cmd_set_data(dev, cdata);
break;
case COMP_CMD_SET_VALUE:
ret = tone_cmd_set_value(dev, cdata);
break;
case COMP_CMD_GET_VALUE:
ret = tone_cmd_get_value(dev, cdata);
break;
}
return ret;
}
static int tone_trigger(struct comp_dev *dev, int cmd)
{
trace_tone("trg");
return comp_set_state(dev, cmd);
}
/* copy and process stream data from source to sink buffers */
static int tone_copy(struct comp_dev * dev)
{
struct comp_buffer *sink;
struct comp_data *cd = comp_get_drvdata(dev);
tracev_comp("cpy");
/* tone component sink buffer */
sink = list_first_item(&dev->bsink_list, struct comp_buffer,
source_list);
/* Test that sink has enough free frames. Then run once to maintain
* low latency and steady load for tones.
*/
if (sink->free >= cd->period_bytes) {
/* create tone */
cd->tone_func(dev, sink, dev->frames);
/* calc new free and available */
comp_update_buffer_produce(sink, cd->period_bytes);
return dev->frames;
} else {
/* XRUN */
trace_tone_error("xrn");
comp_overrun(dev, sink, cd->period_bytes, sink->free);
return -EIO;
}
}
static int tone_prepare(struct comp_dev * dev)
{
struct comp_data *cd = comp_get_drvdata(dev);
int32_t f;
int32_t a;
int ret;
int i;
trace_tone("TPp");
ret = comp_set_state(dev, COMP_TRIGGER_PREPARE);
if (ret < 0)
return ret;
cd->channels = dev->params.channels;
tracev_value(cd->channels);
tracev_value(cd->rate);
for (i = 0; i < cd->channels; i++) {
f = tonegen_get_f(&cd->sg[i]);
a = tonegen_get_a(&cd->sg[i]);
if (tonegen_init(&cd->sg[i], cd->rate, f, a) < 0) {
comp_set_state(dev, COMP_TRIGGER_RESET);
return -EINVAL;
}
}
return 0;
}
static int tone_reset(struct comp_dev * dev)
{
struct comp_data *cd = comp_get_drvdata(dev);
int i;
trace_tone("TRe");
/* Initialize with the defaults */
for (i = 0; i < PLATFORM_MAX_CHANNELS; i++)
tonegen_reset(&cd->sg[i]);
comp_set_state(dev, COMP_TRIGGER_RESET);
return 0;
}
struct comp_driver comp_tone = {
.type = SOF_COMP_TONE,
.ops = {
.new = tone_new,
.free = tone_free,
.params = tone_params,
.cmd = tone_cmd,
.trigger = tone_trigger,
.copy = tone_copy,
.prepare = tone_prepare,
.reset = tone_reset,
},
};
void sys_comp_tone_init(void)
{
comp_register(&comp_tone);
}