893387b2c5
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
325 lines
8.8 KiB
C
325 lines
8.8 KiB
C
/****************************************************************************
|
|
* apps/audioutils/fmsynth/test/fmsynth_alsa_test.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 <stdio.h>
|
|
#include <stdint.h>
|
|
#include <termios.h>
|
|
#include <fcntl.h>
|
|
|
|
#include <alsa/asoundlib.h>
|
|
|
|
#include <audioutils/fmsynth_eg.h>
|
|
#include <audioutils/fmsynth_op.h>
|
|
#include <audioutils/fmsynth.h>
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#define FS (48000)
|
|
|
|
#define CHANNEL_NUM (2)
|
|
#define RESAMPLING_ALSA (1)
|
|
#define LATENCY_ALSA (10000)
|
|
|
|
#define SAMPLE_NUM (FS / 20)
|
|
#define BUFF_LENGTH (SAMPLE_NUM * CHANNEL_NUM)
|
|
|
|
#define CODE_C_FREQ (261.625565f)
|
|
#define CODE_D_FREQ (293.6647674f)
|
|
#define CODE_E_FREQ (329.6275561f)
|
|
#define CODE_F_FREQ (349.2282305f)
|
|
#define CODE_G_FREQ (391.9954347f)
|
|
#define CODE_A_FREQ (440.f)
|
|
#define CODE_B_FREQ (493.8833009f)
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static int16_t samples[BUFF_LENGTH];
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* name: init_alsa
|
|
****************************************************************************/
|
|
|
|
static snd_pcm_t *init_alsa(int fs)
|
|
{
|
|
int ret;
|
|
snd_pcm_t *hndl = NULL;
|
|
|
|
ret = snd_pcm_open(&hndl, "default", SND_PCM_STREAM_PLAYBACK, 0);
|
|
if (ret < 0)
|
|
{
|
|
printf("sdn_pcm_open error\n");
|
|
return NULL;
|
|
}
|
|
|
|
ret = snd_pcm_set_params(hndl,
|
|
SND_PCM_FORMAT_S16,
|
|
SND_PCM_ACCESS_RW_INTERLEAVED,
|
|
CHANNEL_NUM, fs, RESAMPLING_ALSA, LATENCY_ALSA);
|
|
if (ret != 0)
|
|
{
|
|
printf("sdn_pcm_set_params error\n");
|
|
snd_pcm_close(hndl);
|
|
return NULL;
|
|
}
|
|
|
|
return hndl;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* name: set_nonblocking
|
|
****************************************************************************/
|
|
|
|
static int set_nonblocking(struct termios *saved)
|
|
{
|
|
struct termios settings;
|
|
|
|
tcgetattr(0, saved);
|
|
settings = *saved;
|
|
|
|
settings.c_lflag &= ~(ECHO | ICANON);
|
|
settings.c_cc[VTIME] = 0;
|
|
settings.c_cc[VMIN] = 1;
|
|
tcsetattr(0, TCSANOW, &settings);
|
|
fcntl(0, F_SETFL, O_NONBLOCK);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* name: store_setting
|
|
****************************************************************************/
|
|
|
|
static void store_setting(struct termios *saved)
|
|
{
|
|
tcsetattr(0, TCSANOW, saved);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* name: set_levels
|
|
****************************************************************************/
|
|
|
|
static fmsynth_eglevels_t *set_levels(fmsynth_eglevels_t *level,
|
|
float atk_lvl, int atk_peri,
|
|
float decbrk_lvl, int decbrk_peri,
|
|
float dec_lvl, int dec_peri,
|
|
float sus_lvl, int sus_peri,
|
|
float rel_lvl, int rel_peri)
|
|
{
|
|
level->attack.level = atk_lvl;
|
|
level->attack.period_ms = atk_peri;
|
|
level->decaybrk.level = decbrk_lvl;
|
|
level->decaybrk.period_ms = decbrk_peri;
|
|
level->decay.level = dec_lvl;
|
|
level->decay.period_ms = dec_peri;
|
|
level->sustain.level = sus_lvl;
|
|
level->sustain.period_ms = sus_peri;
|
|
level->release.level = rel_lvl;
|
|
level->release.period_ms = rel_peri;
|
|
|
|
return level;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* name: main
|
|
****************************************************************************/
|
|
|
|
int main(void)
|
|
{
|
|
int running;
|
|
int dump_count = 0;
|
|
int dump_enable = 0;
|
|
|
|
fmsynth_eglevels_t levels;
|
|
fmsynth_sound_t *snd1;
|
|
fmsynth_op_t *envop;
|
|
fmsynth_op_t *fbop;
|
|
snd_pcm_t *hndl = NULL;
|
|
|
|
struct termios save_param;
|
|
|
|
hndl = init_alsa(FS);
|
|
if (!hndl)
|
|
{
|
|
printf("Init alsa error\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Initialize FM synthesizer */
|
|
|
|
fmsynth_initialize(FS);
|
|
|
|
/* Operator setup */
|
|
|
|
envop = fmsynthop_create();
|
|
fbop = fmsynthop_create();
|
|
|
|
set_levels(&levels, 0.6f, 100, 0.3f, 300, 0.1f, 500, 0.f, 0, 0.f, 70);
|
|
|
|
fmsynthop_set_envelope(envop, &levels);
|
|
fmsynthop_select_opfunc(envop, FMSYNTH_OPFUNC_SIN);
|
|
|
|
fmsynthop_set_envelope(fbop, &levels);
|
|
fmsynthop_select_opfunc(fbop, FMSYNTH_OPFUNC_SIN);
|
|
fmsynthop_bind_feedback(fbop, fbop, 0.6f);
|
|
|
|
fmsynthop_parallel_subop(envop, fbop);
|
|
|
|
/* Sound setup */
|
|
|
|
snd1 = fmsynthsnd_create();
|
|
fmsynthsnd_set_operator(snd1, envop);
|
|
fmsynthsnd_set_soundfreq(snd1, CODE_C_FREQ);
|
|
|
|
set_nonblocking(&save_param);
|
|
running = 1;
|
|
while (running)
|
|
{
|
|
switch (getchar())
|
|
{
|
|
case 'c':
|
|
fmsynthsnd_set_soundfreq(snd1, CODE_C_FREQ);
|
|
if (dump_enable)
|
|
{
|
|
dump_count = FS;
|
|
dump_enable = 0;
|
|
printf("DUMP: ");
|
|
}
|
|
|
|
printf("Do\n");
|
|
break;
|
|
case 'd':
|
|
fmsynthsnd_set_soundfreq(snd1, CODE_D_FREQ);
|
|
if (dump_enable)
|
|
{
|
|
dump_count = FS;
|
|
dump_enable = 0;
|
|
printf("DUMP: ");
|
|
}
|
|
|
|
printf("Le\n");
|
|
break;
|
|
case 'e':
|
|
fmsynthsnd_set_soundfreq(snd1, CODE_E_FREQ);
|
|
if (dump_enable)
|
|
{
|
|
dump_count = FS;
|
|
dump_enable = 0;
|
|
printf("DUMP: ");
|
|
}
|
|
|
|
printf("Mi\n");
|
|
break;
|
|
case 'f':
|
|
fmsynthsnd_set_soundfreq(snd1, CODE_F_FREQ);
|
|
if (dump_enable)
|
|
{
|
|
dump_count = FS;
|
|
dump_enable = 0;
|
|
printf("DUMP: ");
|
|
}
|
|
|
|
printf("Fha\n");
|
|
break;
|
|
case 'g':
|
|
fmsynthsnd_set_soundfreq(snd1, CODE_G_FREQ);
|
|
if (dump_enable)
|
|
{
|
|
dump_count = FS;
|
|
dump_enable = 0;
|
|
printf("DUMP: ");
|
|
}
|
|
|
|
printf("So\n");
|
|
break;
|
|
case 'a':
|
|
fmsynthsnd_set_soundfreq(snd1, CODE_A_FREQ);
|
|
if (dump_enable)
|
|
{
|
|
dump_count = FS;
|
|
dump_enable = 0;
|
|
printf("DUMP: ");
|
|
}
|
|
|
|
printf("Ra\n");
|
|
break;
|
|
case 'b':
|
|
fmsynthsnd_set_soundfreq(snd1, CODE_B_FREQ);
|
|
if (dump_enable)
|
|
{
|
|
dump_count = FS;
|
|
dump_enable = 0;
|
|
printf("DUMP: ");
|
|
}
|
|
|
|
printf("Shi\n");
|
|
break;
|
|
case 'z':
|
|
dump_enable = 1;
|
|
printf("Dump next code\n");
|
|
break;
|
|
case 'q':
|
|
running = 0;
|
|
break;
|
|
}
|
|
|
|
fmsynth_rendering(snd1, samples, BUFF_LENGTH, CHANNEL_NUM, NULL, 0);
|
|
|
|
if (dump_count)
|
|
{
|
|
for (int i = 0; i < BUFF_LENGTH; i += 2)
|
|
{
|
|
printf("%d\n", samples[i]);
|
|
}
|
|
|
|
dump_count -= SAMPLE_NUM;
|
|
}
|
|
|
|
snd_pcm_writei(hndl, samples, SAMPLE_NUM);
|
|
}
|
|
|
|
snd_pcm_drain(hndl);
|
|
snd_pcm_close(hndl);
|
|
|
|
fmsynthop_delete(envop);
|
|
fmsynthop_delete(fbop);
|
|
|
|
fmsynthsnd_delete(snd1);
|
|
|
|
store_setting(&save_param);
|
|
|
|
return 0;
|
|
}
|