nuttx-apps/audioutils/fmsynth/fmsynth_eg.c
Takayoshi Koizumi 40c506f3a0 audioutils/fmsynth: Add FM synthesizer library
Add simple FM synthesizer library in audioutils.
2022-08-25 20:29:55 +09:00

198 lines
6.1 KiB
C

/****************************************************************************
* apps/audioutils/fmsynth/fmsynth_eg.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <stdlib.h>
#include <limits.h>
#include <audioutils/fmsynth_eg.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define CONVERT_INITVAL(lv) (int)((lv) * FMSYNTH_MAX_EGLEVEL)
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* name: set_egparams
****************************************************************************/
static int set_egparams(int fs,
FAR fmsynth_egparam_t *param,
FAR struct fmsynth_eglevel_s *target_level,
FAR struct fmsynth_eglevel_s *last_level)
{
param->initval = CONVERT_INITVAL(last_level->level);
param->period = fs * target_level->period_ms / 1000;
param->diff2next = CONVERT_INITVAL(target_level->level)
- CONVERT_INITVAL(last_level->level);
if (param->initval < -FMSYNTH_MAX_EGLEVEL ||
param->initval > FMSYNTH_MAX_EGLEVEL || param->period < 0)
{
return -1;
}
return 0;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* name: fmsyntheg_create
****************************************************************************/
FAR fmsynth_eg_t *fmsyntheg_create(void)
{
int i;
FAR fmsynth_eg_t *ret = (FAR fmsynth_eg_t *)malloc(sizeof(fmsynth_eg_t));
if (ret)
{
ret->state = EGSTATE_RELEASED;
ret->state_counter = 0;
for (i = 0; i < EGSTATE_MAX; i++)
{
ret->state_params[i].initval = 0;
ret->state_params[i].period = 0;
}
ret->state_params[EGSTATE_RELEASED].initval = FMSYNTH_MAX_EGLEVEL;
}
return ret;
}
/****************************************************************************
* name: fmsyntheg_delete
****************************************************************************/
void fmsyntheg_delete(FAR fmsynth_eg_t *eg)
{
if (eg != NULL)
{
free(eg);
}
}
/****************************************************************************
* name: fmsyntheg_set_param
****************************************************************************/
int fmsyntheg_set_param(FAR fmsynth_eg_t *eg,
int fs, FAR fmsynth_eglevels_t *levels)
{
int errcnt = 0;
if (fs <= 0)
{
return ERROR;
}
errcnt += set_egparams(fs, &eg->state_params[EGSTATE_ATTACK],
&levels->attack, &levels->release);
errcnt += set_egparams(fs, &eg->state_params[EGSTATE_DECAYBREAK],
&levels->decaybrk, &levels->attack);
errcnt += set_egparams(fs, &eg->state_params[EGSTATE_DECAY],
&levels->decay, &levels->decaybrk);
errcnt += set_egparams(fs, &eg->state_params[EGSTATE_SUSTAIN],
&levels->sustain, &levels->decay);
errcnt += set_egparams(fs, &eg->state_params[EGSTATE_RELEASE],
&levels->release, &levels->sustain);
eg->state_params[EGSTATE_RELEASED].initval =
CONVERT_INITVAL(levels->release.level);
return errcnt ? ERROR : OK;
}
/****************************************************************************
* name: fmsyntheg_start
****************************************************************************/
void fmsyntheg_start(FAR fmsynth_eg_t *eg)
{
eg->state = EGSTATE_ATTACK;
eg->state_counter = 0;
}
/****************************************************************************
* name: fmsyntheg_stop
****************************************************************************/
void fmsyntheg_stop(FAR fmsynth_eg_t *eg)
{
eg->state = EGSTATE_RELEASED;
eg->state_counter = 0;
}
/****************************************************************************
* name: fmsyntheg_operate
****************************************************************************/
int fmsyntheg_operate(FAR fmsynth_eg_t *eg)
{
int val;
FAR fmsynth_egparam_t *param = &eg->state_params[eg->state];
val = param->initval;
if (eg->state != EGSTATE_RELEASED)
{
if (eg->state_counter >= eg->state_params[eg->state].period)
{
/* Reset the counter */
eg->state_counter = 0;
/* Search next available state */
do
{
eg->state++;
}
while (eg->state < EGSTATE_RELEASED
&& eg->state_params[eg->state].period == 0);
val = eg->state_params[eg->state].initval;
}
else
{
val = val + param->diff2next * eg->state_counter / param->period;
eg->state_counter++;
}
}
return val;
}