nuttx-apps/audioutils/fmsynth/fmsynth_op.c

522 lines
14 KiB
C
Raw Normal View History

/****************************************************************************
* apps/audioutils/fmsynth/fmsynth_op.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 <audioutils/fmsynth_op.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define PHASE_ADJUST(th) \
(((th) < 0 ? (FMSYNTH_PI) - (th) : (th)) % (FMSYNTH_PI * 2))
/****************************************************************************
* Private Data
****************************************************************************/
static const short s_sintbl[] =
{
0xff37, /* Extra data for linear completion */
/* Actual sin table of half PI [256] */
0x0000, 0x00c9, 0x0192, 0x025b,
0x0324, 0x03ed, 0x04b6, 0x057e,
0x0647, 0x0710, 0x07d9, 0x08a1,
0x096a, 0x0a32, 0x0afb, 0x0bc3,
0x0c8b, 0x0d53, 0x0e1b, 0x0ee3,
0x0fab, 0x1072, 0x1139, 0x1200,
0x12c7, 0x138e, 0x1455, 0x151b,
0x15e1, 0x16a7, 0x176d, 0x1833,
0x18f8, 0x19bd, 0x1a82, 0x1b46,
0x1c0b, 0x1ccf, 0x1d93, 0x1e56,
0x1f19, 0x1fdc, 0x209f, 0x2161,
0x2223, 0x22e4, 0x23a6, 0x2467,
0x2527, 0x25e7, 0x26a7, 0x2767,
0x2826, 0x28e5, 0x29a3, 0x2a61,
0x2b1e, 0x2bdb, 0x2c98, 0x2d54,
0x2e10, 0x2ecc, 0x2f86, 0x3041,
0x30fb, 0x31b4, 0x326d, 0x3326,
0x33de, 0x3496, 0x354d, 0x3603,
0x36b9, 0x376f, 0x3824, 0x38d8,
0x398c, 0x3a3f, 0x3af2, 0x3ba4,
0x3c56, 0x3d07, 0x3db7, 0x3e67,
0x3f16, 0x3fc5, 0x4073, 0x4120,
0x41cd, 0x4279, 0x4325, 0x43d0,
0x447a, 0x4523, 0x45cc, 0x4674,
0x471c, 0x47c3, 0x4869, 0x490e,
0x49b3, 0x4a57, 0x4afa, 0x4b9d,
0x4c3f, 0x4ce0, 0x4d80, 0x4e20,
0x4ebf, 0x4f5d, 0x4ffa, 0x5097,
0x5133, 0x51ce, 0x5268, 0x5301,
0x539a, 0x5432, 0x54c9, 0x555f,
0x55f4, 0x5689, 0x571d, 0x57b0,
0x5842, 0x58d3, 0x5963, 0x59f3,
0x5a81, 0x5b0f, 0x5b9c, 0x5c28,
0x5cb3, 0x5d3d, 0x5dc6, 0x5e4f,
0x5ed6, 0x5f5d, 0x5fe2, 0x6067,
0x60eb, 0x616e, 0x61f0, 0x6271,
0x62f1, 0x6370, 0x63ee, 0x646b,
0x64e7, 0x6562, 0x65dd, 0x6656,
0x66ce, 0x6745, 0x67bc, 0x6831,
0x68a5, 0x6919, 0x698b, 0x69fc,
0x6a6c, 0x6adb, 0x6b4a, 0x6bb7,
0x6c23, 0x6c8e, 0x6cf8, 0x6d61,
0x6dc9, 0x6e30, 0x6e95, 0x6efa,
0x6f5e, 0x6fc0, 0x7022, 0x7082,
0x70e1, 0x7140, 0x719d, 0x71f9,
0x7254, 0x72ae, 0x7306, 0x735e,
0x73b5, 0x740a, 0x745e, 0x74b1,
0x7503, 0x7554, 0x75a4, 0x75f3,
0x7640, 0x768d, 0x76d8, 0x7722,
0x776b, 0x77b3, 0x77f9, 0x783f,
0x7883, 0x78c6, 0x7908, 0x7949,
0x7989, 0x79c7, 0x7a04, 0x7a41,
0x7a7c, 0x7ab5, 0x7aee, 0x7b25,
0x7b5c, 0x7b91, 0x7bc4, 0x7bf7,
0x7c29, 0x7c59, 0x7c88, 0x7cb6,
0x7ce2, 0x7d0e, 0x7d38, 0x7d61,
0x7d89, 0x7db0, 0x7dd5, 0x7df9,
0x7e1c, 0x7e3e, 0x7e5e, 0x7e7e,
0x7e9c, 0x7eb9, 0x7ed4, 0x7eef,
0x7f08, 0x7f20, 0x7f37, 0x7f4c,
0x7f61, 0x7f74, 0x7f86, 0x7f96,
0x7fa6, 0x7fb4, 0x7fc1, 0x7fcd,
0x7fd7, 0x7fe0, 0x7fe8, 0x7fef,
0x7ff5, 0x7ff9, 0x7ffc, 0x7ffe,
0x7fff, /* Extra data for linear completion */
};
static int local_fs;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* name: pseudo_sin256
****************************************************************************/
static int pseudo_sin256(int theta)
{
int short_sin;
int rest;
int phase;
int tblidx;
theta = PHASE_ADJUST(theta);
rest = theta & 0x7f;
phase = theta / (FMSYNTH_PI / 2);
tblidx = (theta % (FMSYNTH_PI / 2)) >> 7;
if (phase & 0x01)
{
tblidx = 257 - tblidx;
short_sin = s_sintbl[tblidx];
short_sin = short_sin
+ (((s_sintbl[tblidx - 1] - short_sin) * rest) >> 7);
}
else
{
short_sin = s_sintbl[tblidx + 1];
short_sin = short_sin
+ (((s_sintbl[tblidx + 2] - short_sin) * rest) >> 7);
}
return phase & 0x02 ? -short_sin : short_sin;
}
/****************************************************************************
* name: triangle_wave
****************************************************************************/
static int triangle_wave(int theta)
{
int ret = 0;
int phase;
int offset;
int slope;
theta = PHASE_ADJUST(theta);
phase = theta / (FMSYNTH_PI / 2);
offset = theta % (FMSYNTH_PI / 2);
switch (phase)
{
case 0:
ret = 0;
slope = SHRT_MAX;
break;
case 1:
ret = SHRT_MAX;
slope = -SHRT_MAX;
break;
case 2:
ret = 0;
slope = -SHRT_MAX;
break;
case 3:
ret = -SHRT_MAX;
slope = SHRT_MAX;
break;
default:
ret = 0;
slope = SHRT_MAX;
break;
}
return ret + ((slope * offset) >> 15);
}
/****************************************************************************
* name: sawtooth_wave
****************************************************************************/
static int sawtooth_wave(int theta)
{
theta = PHASE_ADJUST(theta);
return (theta >> 1) - SHRT_MAX;
}
/****************************************************************************
* name: square_wave
****************************************************************************/
static int square_wave(int theta)
{
theta = PHASE_ADJUST(theta);
return theta < FMSYNTH_PI ? SHRT_MAX : -SHRT_MAX;
}
/****************************************************************************
* name: update_parameters
****************************************************************************/
static void update_parameters(FAR fmsynth_op_t *op)
{
if (local_fs != 0)
{
op->delta_phase = 2 * FMSYNTH_PI * op->sound_freq * op->freq_rate
/ (float)local_fs;
}
else
{
op->delta_phase = 0.f;
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* name: fmsynthop_set_samplerate
****************************************************************************/
int fmsynthop_set_samplerate(int fs)
{
if (fs < 0)
{
return ERROR;
}
local_fs = fs;
return OK;
}
/****************************************************************************
* name: fmsynthop_create
****************************************************************************/
FAR fmsynth_op_t *fmsynthop_create(void)
{
FAR fmsynth_op_t *ret;
ret = (FAR fmsynth_op_t *)malloc(sizeof(fmsynth_op_t));
if (ret)
{
ret->eg = fmsyntheg_create();
if (!ret->eg)
{
free(ret);
return NULL;
}
ret->wavegen = NULL;
ret->cascadeop = NULL;
ret->parallelop = NULL;
ret->feedback_ref = NULL;
ret->feedback_val = 0;
ret->feedbackrate = 0;
ret->last_sigval = 0;
ret->freq_rate = 1.f;
ret->sound_freq = 0.f;
ret->delta_phase = 0.f;
ret->current_phase = 0.f;
}
return ret;
}
/****************************************************************************
* name: fmsynthop_delete
****************************************************************************/
void fmsynthop_delete(FAR fmsynth_op_t *op)
{
if (op != NULL)
{
if (op->eg)
{
free(op->eg);
}
free(op);
}
}
/****************************************************************************
* name: fmsynthop_select_opfunc
****************************************************************************/
int fmsynthop_select_opfunc(FAR fmsynth_op_t *op, int type)
{
int ret = ERROR;
if (op != NULL)
{
switch (type)
{
case FMSYNTH_OPFUNC_SIN:
op->wavegen = pseudo_sin256;
ret = OK;
break;
case FMSYNTH_OPFUNC_TRIANGLE:
op->wavegen = triangle_wave;
ret = OK;
break;
case FMSYNTH_OPFUNC_SAWTOOTH:
op->wavegen = sawtooth_wave;
ret = OK;
break;
case FMSYNTH_OPFUNC_SQUARE:
op->wavegen = square_wave;
ret = OK;
break;
}
}
return ret;
}
/****************************************************************************
* name: fmsynthop_set_envelope
****************************************************************************/
int fmsynthop_set_envelope(FAR fmsynth_op_t *op,
FAR fmsynth_eglevels_t *levels)
{
if (local_fs >= 0 && op && levels)
{
return fmsyntheg_set_param(op->eg, local_fs, levels);
}
return ERROR;
}
/****************************************************************************
* name: fmsynthop_cascade_subop
****************************************************************************/
int fmsynthop_cascade_subop(FAR fmsynth_op_t *op,
FAR fmsynth_op_t *subop)
{
FAR fmsynth_op_t *tmp;
if (!op || !subop)
{
return ERROR;
}
for (tmp = op; tmp->cascadeop; tmp = tmp->cascadeop);
tmp->cascadeop = subop;
return OK;
}
/****************************************************************************
* name: fmsynthop_parallel_subop
****************************************************************************/
int fmsynthop_parallel_subop(FAR fmsynth_op_t *op,
FAR fmsynth_op_t *subop)
{
FAR fmsynth_op_t *tmp;
if (!op || !subop)
{
return ERROR;
}
for (tmp = op; tmp->parallelop; tmp = tmp->parallelop);
tmp->parallelop = subop;
return OK;
}
/****************************************************************************
* name: fmsynthop_bind_feedback
****************************************************************************/
int fmsynthop_bind_feedback(FAR fmsynth_op_t *op,
FAR fmsynth_op_t *subop, float ratio)
{
if (!op || !subop)
{
return ERROR;
}
op->feedbackrate = (int)((float)FMSYNTH_MAX_EGLEVEL * ratio);
op->feedback_ref = &subop->last_sigval;
return OK;
}
/****************************************************************************
* name: fmsynthop_update_feedback
****************************************************************************/
int fmsynthop_update_feedback(FAR fmsynth_op_t *op)
{
FAR fmsynth_op_t *tmp;
for (tmp = op->cascadeop; tmp != NULL; tmp = tmp->parallelop)
{
fmsynthop_update_feedback(tmp);
}
if (op->feedback_ref)
{
op->feedback_val = *op->feedback_ref * op->feedbackrate
/ FMSYNTH_MAX_EGLEVEL;
}
return OK;
}
/****************************************************************************
* name: fmsynthop_set_soundfreq
****************************************************************************/
void fmsynthop_set_soundfreq(FAR fmsynth_op_t *op, float freq)
{
FAR fmsynth_op_t *tmp;
op->sound_freq = freq;
update_parameters(op);
for (tmp = op->cascadeop; tmp != NULL; tmp = tmp->parallelop)
{
fmsynthop_set_soundfreq(tmp, freq);
}
}
/****************************************************************************
* name: fmsynthop_set_soundfreqrate
****************************************************************************/
void fmsynthop_set_soundfreqrate(FAR fmsynth_op_t *op, float rate)
{
op->freq_rate = rate;
update_parameters(op);
}
/****************************************************************************
* name: fmsynthop_start
****************************************************************************/
void fmsynthop_start(FAR fmsynth_op_t *op)
{
FAR fmsynth_op_t *tmp;
fmsyntheg_start(op->eg);
for (tmp = op->cascadeop; tmp; tmp = tmp->parallelop)
{
fmsynthop_start(tmp);
}
}
/****************************************************************************
* name: fmsynthop_stop
****************************************************************************/
void fmsynthop_stop(FAR fmsynth_op_t *op)
{
FAR fmsynth_op_t *tmp;
fmsyntheg_stop(op->eg);
for (tmp = op->cascadeop; tmp; tmp = tmp->parallelop)
{
fmsynthop_stop(tmp);
}
}
/****************************************************************************
* name: fmsynthop_operate
****************************************************************************/
int fmsynthop_operate(FAR fmsynth_op_t *op, int phase_time)
{
int phase;
FAR fmsynth_op_t *subop;
op->current_phase = phase_time ? op->current_phase + op->delta_phase : 0.f;
phase = (int)op->current_phase + op->feedback_val;
subop = op->cascadeop;
while (subop)
{
phase += fmsynthop_operate(subop, phase_time);
subop = subop->parallelop;
}
op->last_sigval = fmsyntheg_operate(op->eg) * op->wavegen(phase)
/ FMSYNTH_MAX_EGLEVEL;
return op->last_sigval;
}