From 4d86c69a22092e803340f4c22c1a5dee4c592843 Mon Sep 17 00:00:00 2001 From: Takayoshi Koizumi Date: Tue, 16 Aug 2022 09:03:39 +0000 Subject: [PATCH] examples/fmsynth: Add examples for FM synthesizer lib Add examples to show how to use fmsynth library. There are 2 samples. One is a music keyboard, and the other is music player decording MML. --- examples/fmsynth/Kconfig | 42 ++ examples/fmsynth/Make.defs | 23 ++ examples/fmsynth/Makefile | 44 ++ examples/fmsynth/keyboard_main.c | 449 ++++++++++++++++++++ examples/fmsynth/mmlplayer_main.c | 563 ++++++++++++++++++++++++++ examples/fmsynth/mmlplayer_score.h | 58 +++ examples/fmsynth/music_scale.c | 97 +++++ examples/fmsynth/music_scale.h | 62 +++ examples/fmsynth/operator_algorithm.c | 188 +++++++++ examples/fmsynth/operator_algorithm.h | 39 ++ 10 files changed, 1565 insertions(+) create mode 100644 examples/fmsynth/Kconfig create mode 100644 examples/fmsynth/Make.defs create mode 100644 examples/fmsynth/Makefile create mode 100644 examples/fmsynth/keyboard_main.c create mode 100644 examples/fmsynth/mmlplayer_main.c create mode 100644 examples/fmsynth/mmlplayer_score.h create mode 100644 examples/fmsynth/music_scale.c create mode 100644 examples/fmsynth/music_scale.h create mode 100644 examples/fmsynth/operator_algorithm.c create mode 100644 examples/fmsynth/operator_algorithm.h diff --git a/examples/fmsynth/Kconfig b/examples/fmsynth/Kconfig new file mode 100644 index 000000000..6038fb033 --- /dev/null +++ b/examples/fmsynth/Kconfig @@ -0,0 +1,42 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_FMSYNTH + tristate "FM Synthesizer examples" + default n + ---help--- + Examples for fmsynth library. + +if EXAMPLES_FMSYNTH + +config EXAMPLES_FMSYNTH_KEYBOARD_PROGNAME + string "Simple music keyboard Program name" + default "keyboard" + ---help--- + Command name of this "Simple music keyboard". + +config EXAMPLES_FMSYNTH_KEYBOARD_PRIORITY + int "Simple music keyboard task priority" + default 100 + +config EXAMPLES_FMSYNTH_KEYBOARD_STACKSIZE + int "Simple music keyboard stack size" + default 2048 + +config EXAMPLES_FMSYNTH_MMLPLAYER_PROGNAME + string "MML Player Program name" + default "mmlplayer" + ---help--- + Command name of this "MML Player". + +config EXAMPLES_FMSYNTH_MMLPLAYER_PRIORITY + int "MML Player task priority" + default 100 + +config EXAMPLES_FMSYNTH_MMLPLAYER_STACKSIZE + int "MML Player stack size" + default 2048 + +endif diff --git a/examples/fmsynth/Make.defs b/examples/fmsynth/Make.defs new file mode 100644 index 000000000..25be117b1 --- /dev/null +++ b/examples/fmsynth/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/examples/fmsynth/Make.defs +# +# 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. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_FMSYNTH),) +CONFIGURED_APPS += $(APPDIR)/examples/fmsynth +endif diff --git a/examples/fmsynth/Makefile b/examples/fmsynth/Makefile new file mode 100644 index 000000000..d89a7df17 --- /dev/null +++ b/examples/fmsynth/Makefile @@ -0,0 +1,44 @@ +############################################################################ +# apps/examples/fmsynth/Makefile +# +# 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. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# For fmsynth utilities + +ASRCS = +CSRCS = music_scale.c operator_algorithm.c + +# For fmsynth_keyboard + +PROGNAME = $(CONFIG_EXAMPLES_FMSYNTH_KEYBOARD_PROGNAME) +PRIORITY = $(CONFIG_EXAMPLES_FMSYNTH_KEYBOARD_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_FMSYNTH_KEYBOARD_STACKSIZE) +MAINSRC = keyboard_main.c + +# For fmsynth_mmlplayer + +PROGNAME += $(CONFIG_EXAMPLES_FMSYNTH_MMLPLAYER_PROGNAME) +PRIORITY += $(CONFIG_EXAMPLES_FMSYNTH_MMLPLAYER_PRIORITY) +STACKSIZE += $(CONFIG_EXAMPLES_FMSYNTH_MMLPLAYER_STACKSIZE) +MAINSRC += mmlplayer_main.c + +MODULE = $(CONFIG_EXAMPLES_FMSYNTH) + +include $(APPDIR)/Application.mk diff --git a/examples/fmsynth/keyboard_main.c b/examples/fmsynth/keyboard_main.c new file mode 100644 index 000000000..d3f0c9bc4 --- /dev/null +++ b/examples/fmsynth/keyboard_main.c @@ -0,0 +1,449 @@ +/**************************************************************************** + * apps/examples/fmsynth/keyboard_main.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 + +#include +#include +#include + +#include "operator_algorithm.h" +#include "music_scale.h" + +/**************************************************************************** + * Pre-processor + ****************************************************************************/ + +#define APP_FS (48000) +#define APP_BPS (16) +#define APP_CHNUM (2) + +#define APP_DEFAULT_VOL (400) + +/**************************************************************************** + * Private Data Type + ****************************************************************************/ + +struct app_options +{ + int volume; + int mode; +}; + +struct kbd_s +{ + struct nxaudio_s nxaudio; + + FAR fmsynth_sound_t *sound; + FAR fmsynth_op_t *carrier; + + volatile int request_scale; +}; + +struct key_convert_s +{ + int key; + char key_str; + FAR const char *dispstr; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +extern int board_external_amp_mute_control(bool en); +static void app_dequeue_cb(unsigned long arg, + FAR struct ap_buffer_s *apb); +static void app_complete_cb(unsigned long arg); +static void app_user_cb(unsigned long arg, + FAR struct audio_msg_s *msg, FAR bool *running); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct kbd_s g_kbd; + +static struct nxaudio_callbacks_s cbs = +{ + app_dequeue_cb, + app_complete_cb, + app_user_cb +}; + +static struct key_convert_s key_convert[] = +{ + { OCTAVE(4, MUSIC_SCALE_C), 'a', "O4C" }, + { OCTAVE(4, MUSIC_SCALE_CS), 'w', "O4C+" }, + { OCTAVE(4, MUSIC_SCALE_D), 's', "O4D" }, + { OCTAVE(4, MUSIC_SCALE_DS), 'e', "O4D+" }, + { OCTAVE(4, MUSIC_SCALE_E), 'd', "O4E" }, + { OCTAVE(4, MUSIC_SCALE_F), 'f', "O4F" }, + { OCTAVE(4, MUSIC_SCALE_FS), 't', "O4F+" }, + { OCTAVE(4, MUSIC_SCALE_G), 'g', "O4G" }, + { OCTAVE(4, MUSIC_SCALE_GS), 'y', "O4G+" }, + { OCTAVE(4, MUSIC_SCALE_A), 'h', "O4A" }, + { OCTAVE(4, MUSIC_SCALE_AS), 'u', "O4A+" }, + { OCTAVE(4, MUSIC_SCALE_B), 'j', "O4B" }, + { OCTAVE(5, MUSIC_SCALE_C), 'k', "O5C" }, + { OCTAVE(5, MUSIC_SCALE_CS), 'o', "O5C+" }, + { OCTAVE(5, MUSIC_SCALE_D), 'l', "O5D" }, + { OCTAVE(5, MUSIC_SCALE_DS), 'p', "O5D+" }, + { OCTAVE(5, MUSIC_SCALE_E), ';', "O5E" }, +}; + +#define MAX_KEYCONVERT (sizeof(key_convert)/sizeof(key_convert[0])) + +/**************************************************************************** + * Private functions + ****************************************************************************/ + +/**************************************************************************** + * name: convert_key2idx + ****************************************************************************/ + +static int convert_key2idx(char key) +{ + int i; + + for (i = 0; i < MAX_KEYCONVERT; i++) + { + if (key == key_convert[i].key_str) + { + return i; + } + } + + return -1; +} + +/**************************************************************************** + * name: tick_callback + ****************************************************************************/ + +static void tick_callback(unsigned long arg) +{ + FAR struct kbd_s *kbd = (FAR struct kbd_s *)(uintptr_t)arg; + int scale = kbd->request_scale; + + if (scale != -1) + { + fmsynthsnd_set_soundfreq(kbd->sound, + musical_scale[scale]); + kbd->request_scale = -1; + } +} + +/**************************************************************************** + * name: app_dequeue_cb + ****************************************************************************/ + +static void app_dequeue_cb(unsigned long arg, + FAR struct ap_buffer_s *apb) +{ + FAR struct kbd_s *kbd = (FAR struct kbd_s *)(uintptr_t)arg; + + apb->curbyte = 0; + apb->flags = 0; + + if (kbd->request_scale != -1) + { + apb->nbytes = fmsynth_rendering(kbd->sound, + (FAR int16_t *)apb->samp, + apb->nmaxbytes / sizeof(int16_t), + kbd->nxaudio.chnum, + tick_callback, (unsigned long)kbd); + } + else + { + apb->nbytes = fmsynth_rendering(kbd->sound, + (FAR int16_t *)apb->samp, + apb->nmaxbytes / sizeof(int16_t), + kbd->nxaudio.chnum, + NULL, 0); + } + + nxaudio_enqbuffer(&kbd->nxaudio, apb); +} + +/**************************************************************************** + * name: app_complete_cb + ****************************************************************************/ + +static void app_complete_cb(unsigned long arg) +{ + /* Do nothing.. */ + + printf("Audio loop is Done\n"); +} + +/**************************************************************************** + * name: app_user_cb + ****************************************************************************/ + +static void app_user_cb(unsigned long arg, + FAR struct audio_msg_s *msg, FAR bool *running) +{ + /* Do nothing.. */ +} + +/**************************************************************************** + * name: audio_loop_thread + ****************************************************************************/ + +static FAR void *audio_loop_thread(pthread_addr_t arg) +{ + FAR struct kbd_s *kbd = (FAR struct kbd_s *)arg; + + nxaudio_start(&kbd->nxaudio); + nxaudio_msgloop(&kbd->nxaudio, &cbs, (unsigned long)kbd); + + return NULL; +} + +/**************************************************************************** + * name: create_audio_thread + ****************************************************************************/ + +static pthread_t create_audio_thread(FAR struct kbd_s *kbd) +{ + pthread_t pid; + pthread_attr_t tattr; + struct sched_param sparam; + + pthread_attr_init(&tattr); + sparam.sched_priority = sched_get_priority_max(SCHED_FIFO) - 9; + pthread_attr_setschedparam(&tattr, &sparam); + pthread_attr_setstacksize(&tattr, 4096); + + pthread_create(&pid, &tattr, audio_loop_thread, + (pthread_addr_t)kbd); + pthread_setname_np(pid, "musickeyboard_thread"); + + return pid; +} + +/**************************************************************************** + * name: init_fmmusi_soundsc + ****************************************************************************/ + +static int init_keyboard_sound(FAR struct kbd_s *kbd, int fs, int mode) +{ + int ret = ERROR; + + fmsynth_initialize(fs); + + kbd->sound = fmsynthsnd_create(); + if (kbd->sound) + { + kbd->carrier = mode == 0 ? fmsynthutil_algorithm0() : + mode == 1 ? fmsynthutil_algorithm1() : + mode == 2 ? fmsynthutil_algorithm2() : + NULL; + if (!kbd->carrier) + { + fmsynthsnd_delete(kbd->sound); + return ret; + } + + fmsynthsnd_set_operator(kbd->sound, kbd->carrier); + kbd->request_scale = -1; + ret = OK; + } + + return ret; +} + +/**************************************************************************** + * name: fin_keyboard + ****************************************************************************/ + +static void fin_keyboard(FAR struct kbd_s *kbd) +{ + fin_nxaudio(&kbd->nxaudio); + fmsynthutil_delete_ops(kbd->carrier); + fmsynthsnd_delete(kbd->sound); +} + +/**************************************************************************** + * name: print_help + ****************************************************************************/ + +static void print_help(FAR char *name) +{ + printf("nsh> %s ([-v (volume from 0 to 100)]) ([-m (mode 0, 1 or 2)])\n", + name); +} + +/**************************************************************************** + * name: configure_option + ****************************************************************************/ + +static int configure_option(FAR struct app_options *option, + int argc, FAR char **argv) +{ + int opt; + + option->volume = APP_DEFAULT_VOL; + option->mode = 0; + while ((opt = getopt(argc, argv, "hv:m:")) != ERROR) + { + switch (opt) + { + case 'v': + option->volume = atoi(optarg) * 10; + if (option->volume < 0 || option->volume > 1000) + { + option->volume = 400; + } + break; + + case 'm': + option->mode = atoi(optarg); + if (option->mode < 0 || option->mode > 3) + { + option->mode = 0; + } + break; + + default: + return ERROR; + } + } + + return OK; +} + +/**************************************************************************** + * name: print_keyusage() + ****************************************************************************/ + +static void print_keyusage(void) +{ + int i; + + printf("press any key below to make a sound.\n"); + for (i = 0; i < MAX_KEYCONVERT; i++) + { + printf(" [KEY]: %c, [CODE]: %s\n", + key_convert[i].key_str, + key_convert[i].dispstr); + } + printf("\n"); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * name: main + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + int i; + int ret; + char key; + bool running = true; + pthread_t pid; + struct app_options appopt; + int key_idx; + + if (configure_option(&appopt, argc, argv) != OK) + { + print_help(argv[0]); + return -1; + } + + ret = init_nxaudio(&g_kbd.nxaudio, APP_FS, APP_BPS, APP_CHNUM); + if (ret < 0) + { + printf("init_nxaoud() returned with error!!\n"); + return -1; + } + + nxaudio_setvolume(&g_kbd.nxaudio, appopt.volume); + + ret = init_keyboard_sound(&g_kbd, APP_FS, appopt.mode); + if (ret != OK) + { + fin_nxaudio(&g_kbd.nxaudio); + printf("init_keyboard_sound() error!!\n"); + return -1; + } + + /* Render audio samples in audio buffers */ + + for (i = 0; i < g_kbd.nxaudio.abufnum; i++) + { + app_dequeue_cb((unsigned long)&g_kbd, + g_kbd.nxaudio.abufs[i]); + } + + pid = create_audio_thread(&g_kbd); + + printf("Start %s\n", argv[0]); + print_keyusage(); + + while (running) + { + key = (char)getchar(); + if (key != EOF) + { + switch (key) + { + case 'q': + running = false; + break; + + default: + key_idx = convert_key2idx(key); + if (key_idx >= 0) + { + g_kbd.request_scale = key_convert[key_idx].key; + printf("%s \n", key_convert[key_idx].dispstr); + fflush(stdout); + } + break; + } + } + } + + board_external_amp_mute_control(true); + + nxaudio_stop(&g_kbd.nxaudio); + pthread_join(pid, NULL); + + fin_keyboard(&g_kbd); + + return ret; +} diff --git a/examples/fmsynth/mmlplayer_main.c b/examples/fmsynth/mmlplayer_main.c new file mode 100644 index 000000000..edab3cb9a --- /dev/null +++ b/examples/fmsynth/mmlplayer_main.c @@ -0,0 +1,563 @@ +/**************************************************************************** + * apps/examples/fmsynth/mmlplayer_main.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 + +#include +#include +#include +#include + +#include "operator_algorithm.h" +#include "music_scale.h" +#include "mmlplayer_score.h" + +/**************************************************************************** + * Pre-processor + ****************************************************************************/ + +#define APP_FS (48000) +#define APP_BPS (16) +#define APP_CHNUM (2) +#define CARRIER_LEVEL (25.f / 100.f) + +#define APP_DEFAULT_VOL (1000) + +/**************************************************************************** + * Private Data Type + ****************************************************************************/ + +struct app_options +{ + int volume; + int mode; +}; + +struct mmlplayer_s +{ + struct nxaudio_s nxaudio; + + /* Right hand sound */ + + FAR fmsynth_sound_t *rsound[2]; /* Need 2 sounds for CHORD */ + FAR fmsynth_op_t *rop[2]; /* Need 2 sounds for CHORD */ + + int rtick; + FAR char *rscore; + struct music_macro_lang_s rmml; + + /* Left hand sound */ + + FAR fmsynth_sound_t *lsound; + FAR fmsynth_op_t *lop; + int ltick; + FAR char *lscore; + struct music_macro_lang_s lmml; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void app_dequeue_cb(unsigned long arg, + FAR struct ap_buffer_s *apb); +static void app_complete_cb(unsigned long arg); +static void app_user_cb(unsigned long arg, + FAR struct audio_msg_s *msg, FAR bool *running); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct mmlplayer_s g_mmlplayer; + +static struct nxaudio_callbacks_s cbs = +{ + app_dequeue_cb, + app_complete_cb, + app_user_cb +}; + +/**************************************************************************** + * Private functions + ****************************************************************************/ + +/**************************************************************************** + * name: print_note + ****************************************************************************/ + +static void print_note(bool LR, int index, int length) +{ + printf("%c: O%d%c : %d\n", LR ? 'R' : 'L', + index / 12, "CcDdEFfGgAaB"[index % 12], length); +} + +/**************************************************************************** + * name: print_chord + ****************************************************************************/ + +static void print_chord(bool LR, int index1, int index2, int length) +{ + printf("%c: [O%d%c, O%d%c] : %d\n", LR ? 'R' : 'L', + index1 / 12, "CcDdEFfGgAaB"[index1 % 12], + index2 / 12, "CcDdEFfGgAaB"[index2 % 12], + length); +} + +/**************************************************************************** + * name: update_righthand_note + ****************************************************************************/ + +static void update_righthand_note(FAR struct mmlplayer_s *fmmsc) +{ + int mml_ret; + struct mml_result_s mml_result; + + fmmsc->rtick = 0; + do + { + mml_ret = parse_mml(&fmmsc->rmml, &fmmsc->rscore, &mml_result); + switch (mml_ret) + { + case MML_TYPE_NOTE: + fmmsc->rtick = mml_result.length; + fmsynthsnd_set_soundfreq(fmmsc->rsound[0], + musical_scale[mml_result.note_idx[0]]); + fmsynthsnd_set_volume(fmmsc->rsound[0], CARRIER_LEVEL); + fmsynthsnd_set_volume(fmmsc->rsound[1], 0.f); + print_note(1, mml_result.note_idx[0], mml_result.length); + break; + + case MML_TYPE_CHORD: + fmmsc->rtick = mml_result.length; + fmsynthsnd_set_soundfreq(fmmsc->rsound[0], + musical_scale[mml_result.note_idx[0]]); + fmsynthsnd_set_soundfreq(fmmsc->rsound[1], + musical_scale[mml_result.note_idx[1]]); + fmsynthsnd_set_volume(fmmsc->rsound[0], CARRIER_LEVEL); + fmsynthsnd_set_volume(fmmsc->rsound[1], CARRIER_LEVEL); + print_chord(1, mml_result.note_idx[0], mml_result.note_idx[1], + mml_result.length); + break; + + case MML_TYPE_REST: + fmmsc->rtick = mml_result.length; + fmsynthsnd_set_volume(fmmsc->rsound[0], 0.f); + fmsynthsnd_set_volume(fmmsc->rsound[1], 0.f); + printf("R: Rest : %d\n", mml_result.length); + break; + + default: + + /* Do nothing */ + + break; + } + } + while (!fmmsc->rtick && mml_ret != MML_TYPE_EOF); +} + +/**************************************************************************** + * name: update_lefthand_note + ****************************************************************************/ + +static void update_lefthand_note(FAR struct mmlplayer_s *fmmsc) +{ + int mml_ret; + struct mml_result_s mml_result; + + fmmsc->ltick = 0; + do + { + mml_ret = parse_mml(&fmmsc->lmml, &fmmsc->lscore, &mml_result); + switch (mml_ret) + { + case MML_TYPE_NOTE: + fmmsc->ltick = mml_result.length; + fmsynthsnd_set_soundfreq(fmmsc->lsound, + musical_scale[mml_result.note_idx[0]]); + fmsynthsnd_set_volume(fmmsc->lsound, CARRIER_LEVEL); + print_note(0, mml_result.note_idx[0], mml_result.length); + break; + + case MML_TYPE_REST: + fmmsc->ltick = mml_result.length; + fmsynthsnd_set_volume(fmmsc->lsound, 0.f); + printf("L: Rest : %d\n", mml_result.length); + break; + + default: + + /* Do nothing */ + + break; + } + } + while (!fmmsc->ltick && mml_ret != MML_TYPE_EOF); +} + +/**************************************************************************** + * name: tick_callback + ****************************************************************************/ + +static void tick_callback(unsigned long arg) +{ + FAR struct mmlplayer_s *fmmsc = (FAR struct mmlplayer_s *)(uintptr_t)arg; + + fmmsc->rtick--; + fmmsc->ltick--; + + if (fmmsc->rtick <= 0) + { + update_righthand_note(fmmsc); + } + + if (fmmsc->ltick <= 0) + { + update_lefthand_note(fmmsc); + } +} + +/**************************************************************************** + * name: app_dequeue_cb + ****************************************************************************/ + +static void app_dequeue_cb(unsigned long arg, + FAR struct ap_buffer_s *apb) +{ + FAR struct mmlplayer_s *mmlplayer = (struct mmlplayer_s *)(uintptr_t)arg; + + apb->curbyte = 0; + apb->flags = 0; + apb->nbytes = fmsynth_rendering(mmlplayer->lsound, + (FAR int16_t *)apb->samp, + apb->nmaxbytes / sizeof(int16_t), + mmlplayer->nxaudio.chnum, + tick_callback, + (unsigned long)(uintptr_t)mmlplayer); + nxaudio_enqbuffer(&mmlplayer->nxaudio, apb); +} + +/**************************************************************************** + * name: app_complete_cb + ****************************************************************************/ + +static void app_complete_cb(unsigned long arg) +{ + /* Do nothing.. */ + + printf("Audio loop is Done\n"); +} + +/**************************************************************************** + * name: app_user_cb + ****************************************************************************/ + +static void app_user_cb(unsigned long arg, + FAR struct audio_msg_s *msg, FAR bool *running) +{ + /* Do nothing.. */ +} + +/**************************************************************************** + * name: autio_loop_thread + ****************************************************************************/ + +static FAR void *audio_loop_thread(pthread_addr_t arg) +{ + struct mmlplayer_s *mmlplayer = (FAR struct mmlplayer_s *)arg; + + nxaudio_start(&mmlplayer->nxaudio); + nxaudio_msgloop(&mmlplayer->nxaudio, &cbs, + (unsigned long)(uintptr_t)mmlplayer); + + return NULL; +} + +/**************************************************************************** + * name: create_audio_thread + ****************************************************************************/ + +static pthread_t create_audio_thread(FAR struct mmlplayer_s *mmlplayer) +{ + pthread_t pid; + pthread_attr_t tattr; + struct sched_param sparam; + + pthread_attr_init(&tattr); + sparam.sched_priority = sched_get_priority_max(SCHED_FIFO) - 9; + pthread_attr_setschedparam(&tattr, &sparam); + pthread_attr_setstacksize(&tattr, 4096); + + pthread_create(&pid, &tattr, audio_loop_thread, + (pthread_addr_t)mmlplayer); + pthread_setname_np(pid, "mmlplayer_thread"); + + return pid; +} + +/**************************************************************************** + * name: delete_sounds + ****************************************************************************/ + +static void delete_sounds(FAR struct mmlplayer_s *mmlplayer) +{ + if (mmlplayer->rop[0]) + { + fmsynthutil_delete_ops(mmlplayer->rop[0]); + } + + if (mmlplayer->rop[1]) + { + fmsynthutil_delete_ops(mmlplayer->rop[1]); + } + + if (mmlplayer->lop) + { + fmsynthutil_delete_ops(mmlplayer->lop); + } + + if (mmlplayer->rsound[0]) + { + fmsynthsnd_delete(mmlplayer->rsound[0]); + } + + if (mmlplayer->rsound[1]) + { + fmsynthsnd_delete(mmlplayer->rsound[1]); + } + + if (mmlplayer->lsound) + { + fmsynthsnd_delete(mmlplayer->lsound); + } +} + +/**************************************************************************** + * name: init_fmmusi_soundsc + ****************************************************************************/ + +static int init_mmlplayer_sound(FAR struct mmlplayer_s *mmlplayer, int fs, + int mode) +{ + CODE fmsynth_op_t *(*opfunc)(void); + + opfunc = mode == 0 ? fmsynthutil_algorithm0 : + mode == 1 ? fmsynthutil_algorithm1 : + mode == 2 ? fmsynthutil_algorithm2 : + NULL; + + fmsynth_initialize(fs); + + mmlplayer->rop[0] = NULL; + mmlplayer->rop[1] = NULL; + mmlplayer->lop = NULL; + + mmlplayer->rsound[0] = fmsynthsnd_create(); + mmlplayer->rsound[1] = fmsynthsnd_create(); + mmlplayer->lsound = fmsynthsnd_create(); + + if (mmlplayer->rsound[0] && mmlplayer->rsound[1] && mmlplayer->lsound) + { + mmlplayer->rop[0] = opfunc(); + mmlplayer->rop[1] = opfunc(); + mmlplayer->lop = opfunc(); + + if (mmlplayer->rop[0] && mmlplayer->rop[1] && mmlplayer->lop) + { + fmsynthsnd_set_operator(mmlplayer->rsound[0], mmlplayer->rop[0]); + fmsynthsnd_set_operator(mmlplayer->rsound[1], mmlplayer->rop[1]); + fmsynthsnd_set_operator(mmlplayer->lsound, mmlplayer->lop); + fmsynthsnd_set_operator(mmlplayer->lsound, mmlplayer->lop); + + fmsynthsnd_add_subsound(mmlplayer->lsound, mmlplayer->rsound[0]); + fmsynthsnd_add_subsound(mmlplayer->lsound, mmlplayer->rsound[1]); + } + else + { + delete_sounds(mmlplayer); + return ERROR; + } + } + else + { + delete_sounds(mmlplayer); + return ERROR; + } + + mmlplayer->rtick = 0; + mmlplayer->ltick = 0; + + init_mml(&mmlplayer->rmml, fs, 120, 4, 4); + init_mml(&mmlplayer->lmml, fs, 120, 4, 3); + + mmlplayer->rscore = (FAR char *)floh_walzer_right; + mmlplayer->lscore = (FAR char *)floh_walzer_left; + + return OK; +} + +/**************************************************************************** + * name: fin_mmlplayer + ****************************************************************************/ + +static void fin_mmlplayer(FAR struct mmlplayer_s *mmlplayer) +{ + fin_nxaudio(&mmlplayer->nxaudio); + delete_sounds(mmlplayer); +} + +/**************************************************************************** + * name: print_help + ****************************************************************************/ + +static void print_help(FAR char *name) +{ + printf("nsh> %s ([-v (volume)]) ([-m (mode)])\n", name); +} + +/**************************************************************************** + * name: configure_option + ****************************************************************************/ + +static int configure_option(FAR struct app_options *option, + int argc, char **argv) +{ + int opt; + + option->volume = APP_DEFAULT_VOL; + option->mode = 0; + while ((opt = getopt(argc, argv, "hv:m:")) != ERROR) + { + switch (opt) + { + case 'v': + option->volume = atoi(optarg) * 10; + if (option->volume < 0 || option->volume > 1000) + { + option->volume = 400; + } + + break; + + case 'm': + option->mode = atoi(optarg); + if (option->mode < 0 || option->mode >= 3) + { + option->mode = 0; + } + + break; + + case 'h': + return ERROR; + break; + + default: + return ERROR; + break; + } + } + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * name: main + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + int i; + int ret; + char key; + bool running = true; + pthread_t pid; + struct app_options appopt; + + printf("Start %s\n", argv[0]); + + if (configure_option(&appopt, argc, argv) != OK) + { + print_help(argv[0]); + return -1; + } + + ret = init_nxaudio(&g_mmlplayer.nxaudio, APP_FS, APP_BPS, APP_CHNUM); + if (ret < 0) + { + printf("init_nxaoud() returned with error!!\n"); + return -1; + } + + nxaudio_setvolume(&g_mmlplayer.nxaudio, appopt.volume); + + ret = init_mmlplayer_sound(&g_mmlplayer, APP_FS, appopt.mode); + if (ret != OK) + { + printf("init_mmlplayer_sound() returned error.\n"); + fin_nxaudio(&g_mmlplayer.nxaudio); + return -1; + } + + for (i = 0; i < g_mmlplayer.nxaudio.abufnum; i++) + { + app_dequeue_cb((unsigned long)&g_mmlplayer, + g_mmlplayer.nxaudio.abufs[i]); + } + + pid = create_audio_thread(&g_mmlplayer); + + while (running) + { + key = (char)getchar(); + if (key != EOF) + { + switch (key) + { + case 'q': + running = false; + break; + } + } + } + + nxaudio_stop(&g_mmlplayer.nxaudio); + pthread_join(pid, NULL); + + fin_mmlplayer(&g_mmlplayer); + + return ret; +} diff --git a/examples/fmsynth/mmlplayer_score.h b/examples/fmsynth/mmlplayer_score.h new file mode 100644 index 000000000..f9e757846 --- /dev/null +++ b/examples/fmsynth/mmlplayer_score.h @@ -0,0 +1,58 @@ +/**************************************************************************** + * apps/examples/fmsynth/mmlplayer_score.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __APPS_EXAMPLES_FMSYNTH_MMLPLAYER_SCORE_H +#define __APPS_EXAMPLES_FMSYNTH_MMLPLAYER_SCORE_H + +static const char *floh_walzer_right = + "T120 L4 O4" + "R8 {D#C#}8" + "R8{[F#][F#]}{D#C#}8 R8{[F#][F#]}{D#C#}8" + "R8[F#]8R8[F#]8 R8{[F][F]} {D#C#}8" + "R8{[F][F]}{D#C#}8 R8{[F][F]}{D#C#}8 " + "R8[F]8R8[F]8 R8{[F#][F#]} {D#C#}8" + "R8{[F#][F#]}{D#C#}8 R8{[F#][F#]}{D#C#}8" + "R8[F#]8R8[F#]8 R8{[F][F]} {D#C#}8" + "R8{[F][F]}{D#C#}8 R8{[F][F]}{D#C#}8 " + "R8[F]8R8[F]8 R8{[F#][F#]} {D#C#}8" + "R8[F#]8R8[F#]8 R8[F#]8R8[F#]8 " + "R2 R8{[F][F]} {D#C#}8" + "R8[F]8R8[F]8 R8[F]8R8[F]8 " + "R2 R8{[F#][F#]} {D#C#}8" + "R8{[F#][F#]}{D#C#}8 R8{[F#][F#]}{D#C#}8" + "R8[F#]8R8[F#]8 R8{[F][F]} {D#C#}8" + "R8{[F][F]}{D#C#}8 R8{[F][F]}{D#C#}8 " + "R8[F]8R8[F]8 R8{[F#][F#]} {D#C#}8" + "[F#]8{CC}8{C#C} R8[F]8[F#]8R8"; + +static const char *floh_walzer_left = + "T120 L4 O3" + "R4" + "F#.R8 F#.R8 F#D# C#.R8" + "C#.R8 C#.R8 C#D# F#.R8" + ">A#.R8 A#.R8 A#>C# D#.R8" + "D#.R8 D#.R8 D#C# + +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * name: fmsynthutil_algorithm0 + ****************************************************************************/ + +FAR fmsynth_op_t *fmsynthutil_algorithm0(void) +{ + /* [Simple sin operator] + * + * +--------------+ + * | OP (carrier) | ----> Audio out + * +--------------+ + */ + + FAR fmsynth_op_t *carrier; + fmsynth_eglevels_t level; + + /* Attack level and period time : 1.0, 40ms + * Decay Break level and period time : 0.3, 200ms + * Decay level and period time : 0.1, 100ms + * Sustain level and period time : 0.1, 100ms + * Release level : 0.0, 0ms + * + */ + + level.attack.level = 1.0f; + level.attack.period_ms = 40; + level.decaybrk.level = 0.3f; + level.decaybrk.period_ms = 200; + level.decay.level = 0.1f; + level.decay.period_ms = 100; + level.sustain.level = 0.1f; + level.sustain.period_ms = 100; + level.release.level = 0.f; + level.release.period_ms = 0; + + carrier = fmsynthop_create(); + if (carrier) + { + fmsynthop_set_envelope(carrier, &level); + fmsynthop_select_opfunc(carrier, FMSYNTH_OPFUNC_SIN); + } + + return carrier; +} + +/**************************************************************************** + * name: fmsynthutil_algorithm1 + ****************************************************************************/ + +FAR fmsynth_op_t *fmsynthutil_algorithm1(void) +{ + /* feed back + * +--------------------+ + * | | + * | +--------------+ | + * +->| OP (carrier) | -+--> Audio out + * +--------------+ + */ + + FAR fmsynth_op_t *carrier; + fmsynth_eglevels_t level; + + level.attack.level = 1.0f; + level.attack.period_ms = 40; + level.decaybrk.level = 0.3f; + level.decaybrk.period_ms = 200; + level.decay.level = 0.1f; + level.decay.period_ms = 100; + level.sustain.level = 0.1f; + level.sustain.period_ms = 100; + level.release.level = 0.f; + level.release.period_ms = 0; + + carrier = fmsynthop_create(); + if (carrier) + { + fmsynthop_set_envelope(carrier, &level); + fmsynthop_select_opfunc(carrier, FMSYNTH_OPFUNC_SIN); + fmsynthop_bind_feedback(carrier, carrier, 0.6f); + } + + return carrier; +} + +/**************************************************************************** + * name: fmsynthutil_algorithm2 + ****************************************************************************/ + +FAR fmsynth_op_t *fmsynthutil_algorithm2(void) +{ + /* feed back + * +--------------------+ + * | | + * | +--------------+ | +--------------+ + * +->| OP (subop) | -+-> | OP (carrier) | ----> Audio out + * +--------------+ +--------------+ + */ + + FAR fmsynth_op_t *carrier; + FAR fmsynth_op_t *subop; + fmsynth_eglevels_t level; + + level.attack.level = 1.0f; + level.attack.period_ms = 40; + level.decaybrk.level = 0.3f; + level.decaybrk.period_ms = 200; + level.decay.level = 0.1f; + level.decay.period_ms = 100; + level.sustain.level = 0.f; + level.sustain.period_ms = 0; + level.release.level = 0.f; + level.release.period_ms = 0; + + carrier = fmsynthop_create(); + if (carrier) + { + subop = fmsynthop_create(); + if (!subop) + { + fmsynthop_delete(carrier); + return NULL; + } + + fmsynthop_set_envelope(carrier, &level); + fmsynthop_select_opfunc(carrier, FMSYNTH_OPFUNC_SIN); + + fmsynthop_set_soundfreqrate(subop, 3.7f); + fmsynthop_select_opfunc(subop, FMSYNTH_OPFUNC_SIN); + + fmsynthop_cascade_subop(carrier, subop); + } + + return carrier; +} + +/**************************************************************************** + * name: fmsynthutil_delete_ops + ****************************************************************************/ + +void FAR fmsynthutil_delete_ops(FAR fmsynth_op_t *op) +{ + FAR fmsynth_op_t *tmp; + + while (op != NULL) + { + tmp = op->parallelop; + + if (op->cascadeop) + { + fmsynthutil_delete_ops(op->cascadeop); + } + + fmsynthop_delete(op); + op = tmp; + } +} diff --git a/examples/fmsynth/operator_algorithm.h b/examples/fmsynth/operator_algorithm.h new file mode 100644 index 000000000..94594d027 --- /dev/null +++ b/examples/fmsynth/operator_algorithm.h @@ -0,0 +1,39 @@ +/**************************************************************************** + * apps/examples/fmsynth/operator_algorithm.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __APPS_EXAMPLES_FMSYNTH_OPERATOR_ALGORITHM_H +#define __APPS_EXAMPLES_FMSYNTH_OPERATOR_ALGORITHM_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +FAR fmsynth_op_t *fmsynthutil_algorithm0(void); +FAR fmsynth_op_t *fmsynthutil_algorithm1(void); +FAR fmsynth_op_t *fmsynthutil_algorithm2(void); +void FAR fmsynthutil_delete_ops(FAR fmsynth_op_t *op); + +#endif /* __APPS_EXAMPLES_FMSYNTH_OPERATOR_ALGORITHM_H */