/**************************************************************************** * 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 #include #include #include #include #include #include #include /**************************************************************************** * 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; }