You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ma...@apache.org on 2022/08/25 11:30:00 UTC

[incubator-nuttx-apps] branch master updated (995aef9d9 -> 4d86c69a2)

This is an automated email from the ASF dual-hosted git repository.

masayuki pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx-apps.git


    from 995aef9d9 net/sockopt: move BINDTODEVICE to socket level
     new 40c506f3a audioutils/fmsynth: Add FM synthesizer library
     new 80eb94da8 audioutils/nxaudio: Add audio utility library for nuttx audio
     new 4d86c69a2 examples/fmsynth: Add examples for FM synthesizer lib

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 {examples/isl29023 => audioutils/fmsynth}/Kconfig  |   7 +-
 {builtin => audioutils/fmsynth}/Make.defs          |   6 +-
 {netutils/codecs => audioutils/fmsynth}/Makefile   |   4 +-
 .../bas_vt100.h => audioutils/fmsynth/fmsynth.c    | 283 ++++++-----
 audioutils/fmsynth/fmsynth_eg.c                    | 197 +++++++
 audioutils/fmsynth/fmsynth_op.c                    | 521 +++++++++++++++++++
 audioutils/fmsynth/test/.gitignore                 |   5 +
 audioutils/fmsynth/test/Makefile                   |  24 +
 audioutils/fmsynth/test/fmsynth_alsa_test.c        | 324 ++++++++++++
 .../fmsynth/test/fmsynth_eg_test.c                 | 153 +++---
 audioutils/fmsynth/test/fmsynth_op_test.c          | 143 ++++++
 .../fmsynth/test/fmsynth_test.c                    | 152 +++---
 .../fmsynth/test/opfunc_test.c                     | 155 ++++--
 audioutils/nxaudio/Kconfig                         |  26 +
 {builtin => audioutils/nxaudio}/Make.defs          |   6 +-
 {fsutils/inifile => audioutils/nxaudio}/Makefile   |   5 +-
 audioutils/nxaudio/nxaudio.c                       | 319 ++++++++++++
 examples/fmsynth/Kconfig                           |  42 ++
 {builtin => examples/fmsynth}/Make.defs            |   6 +-
 examples/{foc => fmsynth}/Makefile                 |  37 +-
 examples/fmsynth/keyboard_main.c                   | 449 ++++++++++++++++
 examples/fmsynth/mmlplayer_main.c                  | 563 +++++++++++++++++++++
 examples/fmsynth/mmlplayer_score.h                 |  58 +++
 examples/fmsynth/music_scale.c                     |  97 ++++
 .../dhcpd_daemon.h => fmsynth/music_scale.h}       |  39 +-
 examples/fmsynth/operator_algorithm.c              | 188 +++++++
 .../{igmp/igmp.h => fmsynth/operator_algorithm.h}  |  19 +-
 .../{netutils/dhcp6c.h => audioutils/fmsynth.h}    |  55 +-
 .../{netutils/dhcp6c.h => audioutils/fmsynth_eg.h} |  74 ++-
 .../{netutils/dhcp6c.h => audioutils/fmsynth_op.h} |  75 ++-
 .../{netutils/dhcp6c.h => audioutils/nxaudio.h}    |  61 +--
 31 files changed, 3572 insertions(+), 521 deletions(-)
 copy {examples/isl29023 => audioutils/fmsynth}/Kconfig (58%)
 copy {builtin => audioutils/fmsynth}/Make.defs (88%)
 copy {netutils/codecs => audioutils/fmsynth}/Makefile (92%)
 copy interpreters/bas/bas_vt100.h => audioutils/fmsynth/fmsynth.c (52%)
 create mode 100644 audioutils/fmsynth/fmsynth_eg.c
 create mode 100644 audioutils/fmsynth/fmsynth_op.c
 create mode 100644 audioutils/fmsynth/test/.gitignore
 create mode 100644 audioutils/fmsynth/test/Makefile
 create mode 100644 audioutils/fmsynth/test/fmsynth_alsa_test.c
 copy examples/relays/relays_main.c => audioutils/fmsynth/test/fmsynth_eg_test.c (55%)
 create mode 100644 audioutils/fmsynth/test/fmsynth_op_test.c
 copy examples/relays/relays_main.c => audioutils/fmsynth/test/fmsynth_test.c (52%)
 copy nshlib/nsh_usbtrace.c => audioutils/fmsynth/test/opfunc_test.c (51%)
 create mode 100644 audioutils/nxaudio/Kconfig
 copy {builtin => audioutils/nxaudio}/Make.defs (88%)
 copy {fsutils/inifile => audioutils/nxaudio}/Makefile (94%)
 create mode 100644 audioutils/nxaudio/nxaudio.c
 create mode 100644 examples/fmsynth/Kconfig
 copy {builtin => examples/fmsynth}/Make.defs (89%)
 copy examples/{foc => fmsynth}/Makefile (63%)
 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
 copy examples/{dhcpd/dhcpd_daemon.h => fmsynth/music_scale.h} (65%)
 create mode 100644 examples/fmsynth/operator_algorithm.c
 copy examples/{igmp/igmp.h => fmsynth/operator_algorithm.h} (75%)
 copy include/{netutils/dhcp6c.h => audioutils/fmsynth.h} (60%)
 copy include/{netutils/dhcp6c.h => audioutils/fmsynth_eg.h} (56%)
 copy include/{netutils/dhcp6c.h => audioutils/fmsynth_op.h} (50%)
 copy include/{netutils/dhcp6c.h => audioutils/nxaudio.h} (57%)


[incubator-nuttx-apps] 03/03: examples/fmsynth: Add examples for FM synthesizer lib

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

masayuki pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx-apps.git

commit 4d86c69a22092e803340f4c22c1a5dee4c592843
Author: Takayoshi Koizumi <ta...@gmail.com>
AuthorDate: Tue Aug 16 09:03:39 2022 +0000

    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(+)

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 <nuttx/config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sched.h>
+#include <limits.h>
+
+#include <nuttx/audio/audio.h>
+#include <audioutils/fmsynth.h>
+#include <audioutils/nxaudio.h>
+
+#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 <nuttx/config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sched.h>
+#include <limits.h>
+
+#include <nuttx/audio/audio.h>
+#include <audioutils/fmsynth.h>
+#include <audioutils/nxaudio.h>
+#include <audioutils/mml_parser.h>
+
+#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{[<A#>F#][<A#>F#]}{D#C#}8 R8{[<A#>F#][<A#>F#]}{D#C#}8"
+  "R8[<A#>F#]8R8[<A#>F#]8      R8{[<B>F][<B>F]}     {D#C#}8"
+  "R8{[<B>F][<B>F]}{D#C#}8     R8{[<B>F][<B>F]}{D#C#}8    "
+  "R8[<B>F]8R8[<B>F]8          R8{[<A#>F#][<A#>F#]} {D#C#}8"
+  "R8{[<A#>F#][<A#>F#]}{D#C#}8 R8{[<A#>F#][<A#>F#]}{D#C#}8"
+  "R8[<A#>F#]8R8[<A#>F#]8      R8{[<B>F][<B>F]}     {D#C#}8"
+  "R8{[<B>F][<B>F]}{D#C#}8     R8{[<B>F][<B>F]}{D#C#}8    "
+  "R8[<B>F]8R8[<B>F]8          R8{[<A#>F#][<A#>F#]} {D#C#}8"
+  "R8[<A#>F#]8R8[<A#>F#]8      R8[<A#>F#]8R8[<A#>F#]8     "
+  "R2                          R8{[<B>F][<B>F]}     {D#C#}8"
+  "R8[<B>F]8R8[<B>F]8          R8[<B>F]8R8[<B>F]8         "
+  "R2                          R8{[<A#>F#][<A#>F#]} {D#C#}8"
+  "R8{[<A#>F#][<A#>F#]}{D#C#}8 R8{[<A#>F#][<A#>F#]}{D#C#}8"
+  "R8[<A#>F#]8R8[<A#>F#]8      R8{[<B>F][<B>F]}     {D#C#}8"
+  "R8{[<B>F][<B>F]}{D#C#}8     R8{[<B>F][<B>F]}{D#C#}8    "
+  "R8[<B>F]8R8[<B>F]8          R8{[<A#>F#][<A#>F#]} {D#C#}8"
+  "[<A#>F#]8{CC}8{C#C}         R8[<B>F]8[<A#>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#       <A#.R8"
+  "<F#C#  F#C#  {F#F}{F#G} G#R"
+  "G#C#   G#C#  {G#G}{G#A} A#R"
+  "F#.R8  F#.R8 F#D#       C#.R8"
+  "C#.R8  C#.R8 C#D#       F#.R8"
+  "C#8R8R R8C#8<F#8R8";
+
+#endif  /* __APPS_EXAMPLES_FMSYNTH_MMLPLAYER_SCORE_H */
diff --git a/examples/fmsynth/music_scale.c b/examples/fmsynth/music_scale.c
new file mode 100644
index 000000000..7ec2e7709
--- /dev/null
+++ b/examples/fmsynth/music_scale.c
@@ -0,0 +1,97 @@
+/****************************************************************************
+ * apps/examples/fmsynth/music_scale.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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/* Musical Scale frequency
+ * Negative value means not supported.
+ */
+
+const float musical_scale[] =
+{
+  /* Octave 0 */
+
+  -1.f, -1.f, -1.f,
+  -1.f, -1.f, -1.f,
+  -1.f, -1.f, -1.f,
+  27.5f, 29.13523509f, 30.86770631f,
+
+  /* Octave 1 */
+
+  32.70319563f, 34.64782883f, 36.70809593f,
+  38.89087289f, 41.20344452f, 43.65352881f,
+  46.2493027f, 48.99942933f, 51.913087f,
+  55.f, 58.27047017f, 61.73541262f,
+
+  /* Octave 2 */
+
+  65.40639126f, 69.29565765f, 73.41619185f,
+  77.78174577f, 82.40688903f, 87.30705762f,
+  92.4986054f, 97.99885866f, 103.826174f,
+  110.f, 116.5409403f, 123.4708252f,
+
+  /* Octave 3 */
+
+  130.8127825f, 138.5913153f, 146.8323837f,
+  155.5634915f, 164.8137781f, 174.6141152f,
+  184.9972108f, 195.9977173f, 207.652348f,
+  220.f, 233.0818807f, 246.9416505f,
+
+  /* Octave 4 */
+
+  261.625565f, 277.1826306f, 293.6647674f,
+  311.1269831f, 329.6275561f, 349.2282305f,
+  369.9944216f, 391.9954347f, 415.304696f,
+  440.f, 466.1637614f, 493.8833009f,
+
+  /* Octave 5 */
+
+  523.2511301f, 554.3652612f, 587.3295348f,
+  622.2539662f, 659.2551123f, 698.456461f,
+  739.9888432f, 783.9908693f, 830.6093921f,
+  880.f, 932.3275227f, 987.7666018f,
+
+  /* Octave 6 */
+
+  1046.50226f, 1108.730522f, 1174.65907f,
+  1244.507932f, 1318.510225f, 1396.912922f,
+  1479.977686f, 1567.981739f, 1661.218784f,
+  1760.f, 1864.655045f, 1975.533204f,
+
+  /* Octave 7 */
+
+  2093.00452f, 2217.461045f, 2349.318139f,
+  2489.015865f, 2637.020449f, 2793.825844f,
+  2959.955373f, 3135.963477f, 3322.437568f,
+  3520.f, 3729.310091f, 3951.066407f,
+
+  /* Octave 8 */
+
+  4186.009041f, -1.f, -1.f,
+  -1.f, -1.f, -1.f,
+  -1.f, -1.f, -1.f,
+  -1.f, -1.f, -1.f,
+};
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
diff --git a/examples/fmsynth/music_scale.h b/examples/fmsynth/music_scale.h
new file mode 100644
index 000000000..577564a3c
--- /dev/null
+++ b/examples/fmsynth/music_scale.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+ * apps/examples/fmsynth/music_scale.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_UTILS_MUSIC_SCALE_H
+#define __APPS_EXAMPLES_FMSYNTH_UTILS_MUSIC_SCALE_H
+
+/****************************************************************************
+ * Pre-processor definisions
+ ****************************************************************************/
+
+#define MUSIC_SCALE_C  (0)
+#define MUSIC_SCALE_CS (1)
+#define MUSIC_SCALE_D  (2)
+#define MUSIC_SCALE_DS (3)
+#define MUSIC_SCALE_E  (4)
+#define MUSIC_SCALE_F  (5)
+#define MUSIC_SCALE_FS (6)
+#define MUSIC_SCALE_G  (7)
+#define MUSIC_SCALE_GS (8)
+#define MUSIC_SCALE_A  (9)
+#define MUSIC_SCALE_AS (10)
+#define MUSIC_SCALE_B  (11)
+#define OCTAVE(n, s) ((s) + ((n) * 12))
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+EXTERN const float musical_scale[];
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __APPS_EXAMPLES_FMSYNTH_UTILS_MUSIC_SCALE_H */
diff --git a/examples/fmsynth/operator_algorithm.c b/examples/fmsynth/operator_algorithm.c
new file mode 100644
index 000000000..2d6b6068e
--- /dev/null
+++ b/examples/fmsynth/operator_algorithm.c
@@ -0,0 +1,188 @@
+/****************************************************************************
+ * apps/examples/fmsynth/operator_algorithm.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 <stddef.h>
+
+#include <audioutils/fmsynth.h>
+
+/****************************************************************************
+ * 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 <audioutils/fmsynth.h>
+
+/****************************************************************************
+ * 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 */


[incubator-nuttx-apps] 02/03: audioutils/nxaudio: Add audio utility library for nuttx audio

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

masayuki pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx-apps.git

commit 80eb94da8a189fde637a26b408112180b7bead77
Author: Takayoshi Koizumi <ta...@gmail.com>
AuthorDate: Tue Aug 16 08:50:17 2022 +0000

    audioutils/nxaudio: Add audio utility library for nuttx audio
    
    Add an utility  library for easier use of NuttX's Audio driver.
---
 audioutils/nxaudio/Kconfig   |  26 ++++
 audioutils/nxaudio/Make.defs |  23 ++++
 audioutils/nxaudio/Makefile  |  25 ++++
 audioutils/nxaudio/nxaudio.c | 319 +++++++++++++++++++++++++++++++++++++++++++
 include/audioutils/nxaudio.h |  79 +++++++++++
 5 files changed, 472 insertions(+)

diff --git a/audioutils/nxaudio/Kconfig b/audioutils/nxaudio/Kconfig
new file mode 100644
index 000000000..e3828aff9
--- /dev/null
+++ b/audioutils/nxaudio/Kconfig
@@ -0,0 +1,26 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+config AUDIOUTILS_NXAUDIO_LIB
+	bool "NX Audio Library"
+	default n
+	---help---
+		Enable support for the NX Audio library.
+
+if AUDIOUTILS_NXAUDIO_LIB
+
+config AUDIOUTILS_NXAUDIO_DEVPATH
+	string "Audio Device file path"
+	default "/dev/audio/pcm1"
+	---help---
+		Audio device file path of target audio device.
+
+config AUDIOUTILS_NXAUDIO_MSGQNAME
+	string "Message queue name"
+	default "/tmp/fmaudio_mq"
+	---help---
+		Message queue name (file path) to communicate with audio message loop.
+
+endif
diff --git a/audioutils/nxaudio/Make.defs b/audioutils/nxaudio/Make.defs
new file mode 100644
index 000000000..4a00aeee8
--- /dev/null
+++ b/audioutils/nxaudio/Make.defs
@@ -0,0 +1,23 @@
+############################################################################
+# apps/audioutils/nxaudio/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.
+#
+############################################################################
+
+ifeq ($(CONFIG_AUDIOUTILS_NXAUDIO_LIB),y)
+CONFIGURED_APPS += $(APPDIR)/audioutils/nxaudio
+endif
diff --git a/audioutils/nxaudio/Makefile b/audioutils/nxaudio/Makefile
new file mode 100644
index 000000000..482ee57ae
--- /dev/null
+++ b/audioutils/nxaudio/Makefile
@@ -0,0 +1,25 @@
+############################################################################
+# apps/audioutils/nxaudio/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
+
+CSRCS   = nxaudio.c
+
+include $(APPDIR)/Application.mk
diff --git a/audioutils/nxaudio/nxaudio.c b/audioutils/nxaudio/nxaudio.c
new file mode 100644
index 000000000..68d709a0b
--- /dev/null
+++ b/audioutils/nxaudio/nxaudio.c
@@ -0,0 +1,319 @@
+/****************************************************************************
+ * apps/audioutils/nxaudio/nxaudio.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 <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <mqueue.h>
+#include <sys/ioctl.h>
+#include <nuttx/audio/audio.h>
+
+#include <errno.h>
+
+#include <audioutils/nxaudio.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * name: configure_audio
+ ****************************************************************************/
+
+static int configure_audio(int fd, int ch, int fs, int bps, int chmap)
+{
+  struct audio_caps_desc_s cap;
+
+  cap.caps.ac_len = sizeof(struct audio_caps_s);
+  cap.caps.ac_type = AUDIO_TYPE_OUTPUT;
+  cap.caps.ac_channels = ch;
+  cap.caps.ac_chmap = chmap;
+  cap.caps.ac_controls.hw[0] = fs;
+  cap.caps.ac_controls.b[2] = bps;
+  cap.caps.ac_controls.b[3] = 0;  /* Just set 0 */
+
+  return ioctl(fd, AUDIOIOC_CONFIGURE, (unsigned long)(uintptr_t)&cap);
+}
+
+/****************************************************************************
+ * name: create_audiomq
+ ****************************************************************************/
+
+static mqd_t create_audiomq(int fd, int buf_num)
+{
+  mqd_t ret;
+  struct mq_attr attr;
+
+  attr.mq_maxmsg = buf_num;
+  attr.mq_msgsize = sizeof(struct audio_msg_s);
+  attr.mq_curmsgs = 0;
+  attr.mq_flags = 0;
+
+  ret = mq_open(CONFIG_AUDIOUTILS_NXAUDIO_MSGQNAME,
+                O_RDWR | O_CREAT, 0644, &attr);
+  if (ret >= (mqd_t)0)
+    {
+      int rr;
+      if ((rr = ioctl(fd, AUDIOIOC_REGISTERMQ, (unsigned long)ret)) < 0)
+        {
+          printf("mq register failed: %d, %d\n", rr, errno);
+        }
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * name: create_audio_buffers
+ ****************************************************************************/
+
+static FAR struct ap_buffer_s **create_audio_buffers(int fd, int num, int sz)
+{
+  int i;
+  struct audio_buf_desc_s desc;
+  FAR struct ap_buffer_s **ret;
+
+  ret = (FAR struct ap_buffer_s **)calloc(num, sizeof(FAR void *));
+
+  for (i = 0; i < num; i++)
+    {
+      desc.numbytes = sz;
+      desc.u.pbuffer = &ret[i];
+
+      ioctl(fd, AUDIOIOC_ALLOCBUFFER, (unsigned long)(uintptr_t)&desc);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * name: free_audio_buffers
+ ****************************************************************************/
+
+static void free_audio_buffers(FAR struct nxaudio_s *nxaudio)
+{
+  int x;
+  struct audio_buf_desc_s desc;
+
+  for (x = 0; x < nxaudio->abufnum; x++)
+    {
+      if (nxaudio->abufs[x] != NULL)
+        {
+          desc.u.buffer = nxaudio->abufs[x];
+          ioctl(nxaudio->fd, AUDIOIOC_FREEBUFFER,
+                (unsigned long)(uintptr_t)&desc);
+        }
+    }
+
+  free(nxaudio->abufs);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * name: fin_nxaudio
+ ****************************************************************************/
+
+void fin_nxaudio(FAR struct nxaudio_s *nxaudio)
+{
+  free_audio_buffers(nxaudio);
+  ioctl(nxaudio->fd, AUDIOIOC_STOP, 0);
+  ioctl(nxaudio->fd, AUDIOIOC_UNREGISTERMQ, (unsigned long)nxaudio->mq);
+  ioctl(nxaudio->fd, AUDIOIOC_RELEASE, 0);
+  ioctl(nxaudio->fd, AUDIOIOC_SHUTDOWN, 0);
+  mq_close(nxaudio->mq);
+  mq_unlink(CONFIG_AUDIOUTILS_NXAUDIO_MSGQNAME);
+  close(nxaudio->fd);
+}
+
+/****************************************************************************
+ * name: init_nxaudio
+ ****************************************************************************/
+
+int init_nxaudio(FAR struct nxaudio_s *nxaudio,
+                 int fs, int bps, int chnum)
+{
+  struct ap_buffer_info_s buf_info;
+
+  nxaudio->fd = open(CONFIG_AUDIOUTILS_NXAUDIO_DEVPATH, O_RDWR | O_CLOEXEC);
+  if (nxaudio->fd >= 0)
+    {
+      if (ioctl(nxaudio->fd, AUDIOIOC_RESERVE, 0) < 0)
+        {
+          close(nxaudio->fd);
+          return -1;
+        }
+
+      /* Audio configuration: set channel num, FS and bps */
+
+      configure_audio(nxaudio->fd, chnum, fs, bps, 0);
+
+      nxaudio->chnum = chnum;
+
+      ioctl(nxaudio->fd, AUDIOIOC_GETBUFFERINFO,
+            (unsigned long)(uintptr_t)&buf_info);
+
+      /* Create message queue to communicate with audio driver */
+
+      nxaudio->mq = create_audiomq(nxaudio->fd, buf_info.nbuffers + 8);
+
+      /* Create audio buffers to inject audio sample */
+
+      nxaudio->abufs = create_audio_buffers(nxaudio->fd,
+                               buf_info.nbuffers, buf_info.buffer_size);
+      nxaudio->abufnum = buf_info.nbuffers;
+
+      return 0;
+    }
+  else
+    {
+      return -1;
+    }
+}
+
+/****************************************************************************
+ * name: nxaudio_enqbuffer
+ ****************************************************************************/
+
+int nxaudio_enqbuffer(FAR struct nxaudio_s *nxaudio,
+                      FAR struct ap_buffer_s *apb)
+{
+  struct audio_buf_desc_s desc;
+
+  desc.numbytes = apb->nbytes;
+  desc.u.buffer = apb;
+
+  return ioctl(nxaudio->fd, AUDIOIOC_ENQUEUEBUFFER,
+               (unsigned long)(uintptr_t)&desc);
+}
+
+/****************************************************************************
+ * name: nxaudio_setvolume
+ ****************************************************************************/
+
+int nxaudio_setvolume(FAR struct nxaudio_s *nxaudio, uint16_t vol)
+{
+  struct audio_caps_desc_s cap_desc;
+
+  cap_desc.caps.ac_len            = sizeof(struct audio_caps_s);
+  cap_desc.caps.ac_type           = AUDIO_TYPE_FEATURE;
+  cap_desc.caps.ac_format.hw      = AUDIO_FU_VOLUME;
+  cap_desc.caps.ac_controls.hw[0] = vol;
+
+  return ioctl(nxaudio->fd, AUDIOIOC_CONFIGURE,
+               (unsigned long)(uintptr_t)&cap_desc);
+}
+
+/****************************************************************************
+ * name: nxaudio_start
+ ****************************************************************************/
+
+int nxaudio_start(FAR struct nxaudio_s *nxaudio)
+{
+  return ioctl(nxaudio->fd, AUDIOIOC_START, 0);
+}
+
+/****************************************************************************
+ * name: nxaudio_start
+ ****************************************************************************/
+
+int nxaudio_stop(FAR struct nxaudio_s *nxaudio)
+{
+  struct audio_msg_s term_msg;
+
+  term_msg.msg_id = AUDIO_MSG_STOP;
+  term_msg.u.data = 0;
+  mq_send(nxaudio->mq, (FAR const char *)&term_msg, sizeof(term_msg), 0);
+
+  return OK;
+}
+
+/****************************************************************************
+ * name: nxaudio_msgloop
+ ****************************************************************************/
+
+int nxaudio_msgloop(FAR struct nxaudio_s *nxaudio,
+                    FAR struct nxaudio_callbacks_s *cbs,
+                    unsigned long arg)
+{
+  bool running = true;
+  struct audio_msg_s msg;
+  unsigned int prio;
+  ssize_t size;
+
+  if (!cbs)
+    {
+      return -1;
+    }
+
+  while (running)
+    {
+      size = mq_receive(nxaudio->mq, (FAR char *)&msg, sizeof(msg), &prio);
+      if (size != sizeof(msg))
+        {
+          continue;
+        }
+
+      switch (msg.msg_id)
+        {
+          case AUDIO_MSG_DEQUEUE:
+            if (cbs->dequeue)
+              {
+                cbs->dequeue(arg, msg.u.ptr);
+              }
+            break;
+          case AUDIO_MSG_COMPLETE:
+            if (cbs->complete)
+              {
+                cbs->complete(arg);
+              }
+            break;
+
+          case AUDIO_MSG_STOP:
+            running = false;
+            break;
+
+          case AUDIO_MSG_USER:
+            if (cbs->user)
+              {
+                cbs->user(arg, &msg, &running);
+              }
+            break;
+          default:
+            break;
+        }
+    }
+
+  return 0;
+}
diff --git a/include/audioutils/nxaudio.h b/include/audioutils/nxaudio.h
new file mode 100644
index 000000000..0d0acb133
--- /dev/null
+++ b/include/audioutils/nxaudio.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+ * apps/include/audioutils/nxaudio.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_INCLUDE_AUDIOUTILS_NXAUDIO_H
+#define __APPS_INCLUDE_AUDIOUTILS_NXAUDIO_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+#include <mqueue.h>
+#include <nuttx/audio/audio.h>
+
+/****************************************************************************
+ * Public Data Types
+ ****************************************************************************/
+
+struct nxaudio_s
+{
+  int fd;
+
+  int abufnum;
+  FAR struct ap_buffer_s **abufs;
+  mqd_t mq;
+
+  int chnum;
+};
+
+struct nxaudio_callbacks_s
+{
+  void CODE (*dequeue)(unsigned long arg, FAR struct ap_buffer_s *apb);
+  void CODE (*complete)(unsigned long arg);
+  void CODE (*user)(unsigned long arg, FAR struct audio_msg_s *msg,
+                    FAR bool *running);
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+int init_nxaudio(FAR struct nxaudio_s *nxaudio,
+                 int fs, int bps, int chnum);
+void fin_nxaudio(FAR struct nxaudio_s *nxaudio);
+int nxaudio_enqbuffer(FAR struct nxaudio_s *nxaudio,
+                      FAR struct ap_buffer_s *apb);
+int nxaudio_setvolume(FAR struct nxaudio_s *nxaudio, uint16_t vol);
+int nxaudio_start(FAR struct nxaudio_s *nxaudio);
+int nxaudio_msgloop(FAR struct nxaudio_s *nxaudio,
+                    FAR struct nxaudio_callbacks_s *cbs, unsigned long arg);
+int nxaudio_stop(FAR struct nxaudio_s *nxaudio);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __APPS_INCLUDE_AUDIOUTILS_NXAUDIO_H */


[incubator-nuttx-apps] 01/03: audioutils/fmsynth: Add FM synthesizer library

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

masayuki pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx-apps.git

commit 40c506f3a0b688fc34eb227ec51b992c0def47e4
Author: Takayoshi Koizumi <ta...@gmail.com>
AuthorDate: Tue Aug 16 08:48:53 2022 +0000

    audioutils/fmsynth: Add FM synthesizer library
    
    Add simple FM synthesizer library in audioutils.
---
 audioutils/fmsynth/Kconfig                  |  10 +
 audioutils/fmsynth/Make.defs                |  23 ++
 audioutils/fmsynth/Makefile                 |  25 ++
 audioutils/fmsynth/fmsynth.c                | 237 +++++++++++++
 audioutils/fmsynth/fmsynth_eg.c             | 197 +++++++++++
 audioutils/fmsynth/fmsynth_op.c             | 521 ++++++++++++++++++++++++++++
 audioutils/fmsynth/test/.gitignore          |   5 +
 audioutils/fmsynth/test/Makefile            |  24 ++
 audioutils/fmsynth/test/fmsynth_alsa_test.c | 324 +++++++++++++++++
 audioutils/fmsynth/test/fmsynth_eg_test.c   | 127 +++++++
 audioutils/fmsynth/test/fmsynth_op_test.c   | 143 ++++++++
 audioutils/fmsynth/test/fmsynth_test.c      | 130 +++++++
 audioutils/fmsynth/test/opfunc_test.c       | 165 +++++++++
 include/audioutils/fmsynth.h                |  81 +++++
 include/audioutils/fmsynth_eg.h             | 100 ++++++
 include/audioutils/fmsynth_op.h             |  99 ++++++
 16 files changed, 2211 insertions(+)

diff --git a/audioutils/fmsynth/Kconfig b/audioutils/fmsynth/Kconfig
new file mode 100644
index 000000000..81d7977d0
--- /dev/null
+++ b/audioutils/fmsynth/Kconfig
@@ -0,0 +1,10 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+config AUDIOUTILS_FMSYNTH_LIB
+	bool "FM Synthesizer Library"
+	default n
+	---help---
+		Enable support for the FM Synthesizer library.
diff --git a/audioutils/fmsynth/Make.defs b/audioutils/fmsynth/Make.defs
new file mode 100644
index 000000000..0350823ed
--- /dev/null
+++ b/audioutils/fmsynth/Make.defs
@@ -0,0 +1,23 @@
+############################################################################
+# apps/audioutils/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.
+#
+############################################################################
+
+ifeq ($(CONFIG_AUDIOUTILS_FMSYNTH_LIB),y)
+CONFIGURED_APPS += $(APPDIR)/audioutils/fmsynth
+endif
diff --git a/audioutils/fmsynth/Makefile b/audioutils/fmsynth/Makefile
new file mode 100644
index 000000000..f291a74b1
--- /dev/null
+++ b/audioutils/fmsynth/Makefile
@@ -0,0 +1,25 @@
+############################################################################
+# apps/audioutils/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
+
+CSRCS   = fmsynth.c fmsynth_eg.c fmsynth_op.c
+
+include $(APPDIR)/Application.mk
diff --git a/audioutils/fmsynth/fmsynth.c b/audioutils/fmsynth/fmsynth.c
new file mode 100644
index 000000000..86a791fab
--- /dev/null
+++ b/audioutils/fmsynth/fmsynth.c
@@ -0,0 +1,237 @@
+/****************************************************************************
+ * apps/audioutils/fmsynth/fmsynth.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.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define WRAP_ROUND_TIME_SEC (10)
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static int max_phase_time;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * name: fetch_feedback
+ ****************************************************************************/
+
+static void fetch_feedback(FAR fmsynth_op_t *ops)
+{
+  while (ops != NULL)
+    {
+      fmsynthop_update_feedback(ops);
+      ops = ops->parallelop;
+    }
+}
+
+/****************************************************************************
+ * name: update_phase
+ ****************************************************************************/
+
+static void update_phase(FAR fmsynth_sound_t *snd)
+{
+  snd->phase_time++;
+  if (snd->phase_time >= max_phase_time)
+    {
+      snd->phase_time = 0;
+    }
+}
+
+/****************************************************************************
+ * name: sound_modulate
+ ****************************************************************************/
+
+static int sound_modulate(FAR fmsynth_sound_t *snd)
+{
+  int out = 0;
+  FAR fmsynth_op_t *op;
+
+  if (snd->operators == NULL)
+    {
+      return out;
+    }
+
+  fetch_feedback(snd->operators);
+
+  for (op = snd->operators; op != NULL; op = op->parallelop)
+    {
+      out += fmsynthop_operate(op, snd->phase_time);
+    }
+
+  update_phase(snd);
+
+  return out * snd->volume / FMSYNTH_MAX_VOLUME;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * name: fmsynth_initialize
+ ****************************************************************************/
+
+int fmsynth_initialize(int fs)
+{
+  max_phase_time = fs * WRAP_ROUND_TIME_SEC;
+  return fmsynthop_set_samplerate(fs);
+}
+
+/****************************************************************************
+ * name: fmsynthsnd_create
+ ****************************************************************************/
+
+FAR fmsynth_sound_t *fmsynthsnd_create(void)
+{
+  FAR fmsynth_sound_t *ret;
+  ret = (FAR fmsynth_sound_t *)malloc(sizeof(fmsynth_sound_t));
+  if (ret)
+    {
+      ret->phase_time = 0;
+      ret->volume = FMSYNTH_MAX_VOLUME;
+      ret->operators = NULL;
+      ret->next_sound = NULL;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * name: fmsynthsnd_delete
+ ****************************************************************************/
+
+void fmsynthsnd_delete(FAR fmsynth_sound_t *snd)
+{
+  if (snd != NULL)
+    {
+      free(snd);
+    }
+}
+
+/****************************************************************************
+ * name: fmsynthsnd_set_operator
+ ****************************************************************************/
+
+int fmsynthsnd_set_operator(FAR fmsynth_sound_t *snd, FAR fmsynth_op_t *op)
+{
+  snd->operators = op;
+
+  return OK;
+}
+
+/****************************************************************************
+ * name: fmsynthsnd_set_soundfreq
+ ****************************************************************************/
+
+void fmsynthsnd_set_soundfreq(FAR fmsynth_sound_t *snd, float freq)
+{
+  FAR fmsynth_op_t *op;
+
+  for (op = snd->operators; op != NULL; op = op->parallelop)
+    {
+      fmsynthop_set_soundfreq(op, freq);
+      fmsynthop_start(op);
+    }
+}
+
+/****************************************************************************
+ * name: fmsynthsnd_set_volume
+ ****************************************************************************/
+
+void fmsynthsnd_set_volume(FAR fmsynth_sound_t *snd, float vol)
+{
+  snd->volume = vol * FMSYNTH_MAX_VOLUME;
+}
+
+/****************************************************************************
+ * name: fmsynthsnd_add_subsound
+ ****************************************************************************/
+
+int fmsynthsnd_add_subsound(FAR fmsynth_sound_t *top,
+                            FAR fmsynth_sound_t *sub)
+{
+  FAR fmsynth_sound_t *s = top;
+
+  if (!top || !sub)
+    {
+      return ERROR;
+    }
+
+  for (s = top; s->next_sound; s = s->next_sound);
+
+  s->next_sound = sub;
+
+  return OK;
+}
+
+/****************************************************************************
+ * name: fmsynth_rendering
+ ****************************************************************************/
+
+int fmsynth_rendering(FAR fmsynth_sound_t *snd,
+                      FAR int16_t *sample, int sample_num, int chnum,
+                      fmsynth_tickcb_t cb, unsigned long cbarg)
+{
+  int i;
+  int ch;
+  int out;
+  FAR fmsynth_sound_t *itr;
+
+  for (i = 0; i < sample_num; i += chnum)
+    {
+      out = 0;
+      for (itr = snd; itr != NULL; itr = itr->next_sound)
+        {
+          out = out + sound_modulate(itr);
+        }
+
+      for (ch = 0; ch < chnum; ch++)
+        {
+          *sample++ = (int16_t)out;
+        }
+
+      if (cb != NULL)
+        {
+          cb(cbarg);
+        }
+    }
+
+  if (i > sample_num)
+    {
+      i -= chnum;
+    }
+
+  /* Return total bytes stored in the buffer */
+
+  return i * sizeof(int16_t);
+}
diff --git a/audioutils/fmsynth/fmsynth_eg.c b/audioutils/fmsynth/fmsynth_eg.c
new file mode 100644
index 000000000..6f7ca4852
--- /dev/null
+++ b/audioutils/fmsynth/fmsynth_eg.c
@@ -0,0 +1,197 @@
+/****************************************************************************
+ * 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;
+}
diff --git a/audioutils/fmsynth/fmsynth_op.c b/audioutils/fmsynth/fmsynth_op.c
new file mode 100644
index 000000000..3f5fda521
--- /dev/null
+++ b/audioutils/fmsynth/fmsynth_op.c
@@ -0,0 +1,521 @@
+/****************************************************************************
+ * 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;
+}
diff --git a/audioutils/fmsynth/test/.gitignore b/audioutils/fmsynth/test/.gitignore
new file mode 100644
index 000000000..34211c6ab
--- /dev/null
+++ b/audioutils/fmsynth/test/.gitignore
@@ -0,0 +1,5 @@
+/fmsynth_alsa
+/fmsynth_test
+/fmsyntheg_test
+/fmsynthop_test
+/opfunctest
diff --git a/audioutils/fmsynth/test/Makefile b/audioutils/fmsynth/test/Makefile
new file mode 100644
index 000000000..7031c15e0
--- /dev/null
+++ b/audioutils/fmsynth/test/Makefile
@@ -0,0 +1,24 @@
+SRCS = ../fmsynth_eg.c ../fmsynth_op.c ../fmsynth.c
+CFLAGS = -DFAR= -DCODE= -DOK=0 -DERROR=-1 -I .. -I ../../../include -g
+
+TARGETS = opfunctest fmsyntheg_test fmsynthop_test fmsynth_test fmsynth_alsa
+
+all: $(TARGETS)
+
+opfunctest: $(SRCS) opfunc_test.c
+	gcc $(CFLAGS) -o $@ $^ -lm
+
+fmsyntheg_test: $(SRCS) fmsynth_eg_test.c
+	gcc $(CFLAGS) -o $@ $^
+
+fmsynthop_test: $(SRCS) fmsynth_op_test.c
+	gcc $(CFLAGS) -o $@ $^
+
+fmsynth_test: $(SRCS) fmsynth_test.c
+	gcc $(CFLAGS) -o $@ $^
+
+fmsynth_alsa: $(SRCS) fmsynth_alsa_test.c
+	gcc $(CFLAGS) -o $@ $^ -lasound
+
+clean:
+	rm -rf $(TARGETS)
diff --git a/audioutils/fmsynth/test/fmsynth_alsa_test.c b/audioutils/fmsynth/test/fmsynth_alsa_test.c
new file mode 100644
index 000000000..1de8fcb3c
--- /dev/null
+++ b/audioutils/fmsynth/test/fmsynth_alsa_test.c
@@ -0,0 +1,324 @@
+/****************************************************************************
+ * 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, (const void *)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;
+}
diff --git a/audioutils/fmsynth/test/fmsynth_eg_test.c b/audioutils/fmsynth/test/fmsynth_eg_test.c
new file mode 100644
index 000000000..1be811cf2
--- /dev/null
+++ b/audioutils/fmsynth/test/fmsynth_eg_test.c
@@ -0,0 +1,127 @@
+/****************************************************************************
+ * apps/audioutils/fmsynth/test/fmsynth_eg_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 <audioutils/fmsynth_eg.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define FS (48000)
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * name: state_name
+ ****************************************************************************/
+
+static const char *state_name(int s)
+{
+  switch (s)
+    {
+      case EGSTATE_ATTACK:
+        return "Attack    ";
+      case EGSTATE_DECAYBREAK:
+        return "DecayBreak";
+      case EGSTATE_DECAY:
+        return "Decay     ";
+      case EGSTATE_SUSTAIN:
+        return "Sustain   ";
+      case EGSTATE_RELEASE:
+        return "Release   ";
+      case EGSTATE_RELEASED:
+      case -1:
+        return "RELEASED..";
+    }
+
+  return "";
+}
+
+/****************************************************************************
+ * name: dump_eg
+ ****************************************************************************/
+
+static void dump_eg(fmsynth_eg_t *env)
+{
+  int i;
+  fmsynth_egparam_t *last = &env->state_params[EGSTATE_RELEASED];
+
+  printf("===== STATE : %s =======\n", state_name(env->state));
+  for (i = -1; i < EGSTATE_RELEASED; i++)
+    {
+      printf("   [%s] %5d <--------------> [%s] %5d\n",
+             state_name(i), last->initval,
+             state_name(i + 1), env->state_params[i + 1].initval);
+      printf("              per %d\n", env->state_params[i + 1].period);
+      printf("              dlt %d\n", env->state_params[i + 1].diff2next);
+      last = &env->state_params[i + 1];
+    }
+
+  printf("\n");
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * name: main
+ ****************************************************************************/
+
+int main(void)
+{
+  fmsynth_eg_t *eg;
+  fmsynth_eglevels_t levels;
+
+  levels.attack.level = 0.6f;
+  levels.attack.period_ms = 10;
+  levels.decaybrk.level = 0.3f;
+  levels.decaybrk.period_ms = 20;
+  levels.decay.level = 0.5f;
+  levels.decay.period_ms = 15;
+  levels.sustain.level = 0.65f;
+  levels.sustain.period_ms = 5;
+  levels.release.level = 0.f;
+  levels.release.period_ms = 70;
+
+  eg = fmsyntheg_create();
+  fmsyntheg_set_param(eg, FS, &levels);
+  dump_eg(eg);
+
+  fmsyntheg_start(eg);
+  dump_eg(eg);
+
+  while (eg->state != EGSTATE_RELEASED)
+    {
+      printf("%d\n", fmsyntheg_operate(eg));
+    }
+
+  fmsyntheg_delete(eg);
+
+  return 0;
+}
diff --git a/audioutils/fmsynth/test/fmsynth_op_test.c b/audioutils/fmsynth/test/fmsynth_op_test.c
new file mode 100644
index 000000000..581eb03a8
--- /dev/null
+++ b/audioutils/fmsynth/test/fmsynth_op_test.c
@@ -0,0 +1,143 @@
+/****************************************************************************
+ * apps/audioutils/fmsynth/test/fmsynth_op_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 <audioutils/fmsynth_op.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define FS (48000)
+#define SOUNDFREQ (261.f)
+#define TEST_LOOP (FS * (30 + 10 + 100 + 30 + 1) / 1000)
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * 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 phase_time;
+  fmsynth_eglevels_t levels;
+
+  fmsynth_op_t *envop;
+  fmsynth_op_t *fbop;
+  fmsynth_op_t *triop;
+  fmsynth_op_t *mainop;
+  fmsynth_op_t *subop;
+
+  fmsynth_op_t *conv1;
+  fmsynth_op_t *conv2;
+
+  phase_time = 0;
+  fmsynthop_set_samplerate(FS);
+
+  set_levels(&levels, 0.12f, 10, 0.06f, 20, 0.1f, 16, 0.1f, 5, 0.f, 70);
+
+  envop  = fmsynthop_create();
+  fmsynthop_set_envelope(envop, &levels);
+  fmsynthop_select_opfunc(envop, FMSYNTH_OPFUNC_SIN);
+  fmsynthop_set_soundfreq(envop, SOUNDFREQ);
+  fmsynthop_start(envop);
+
+  triop  = fmsynthop_create();
+  fmsynthop_select_opfunc(triop, FMSYNTH_OPFUNC_TRIANGLE);
+  fmsynthop_set_soundfreq(triop, SOUNDFREQ);
+  fmsynthop_start(triop);
+
+  fbop   = fmsynthop_create();
+  fmsynthop_select_opfunc(fbop, FMSYNTH_OPFUNC_SIN);
+  fmsynthop_set_soundfreq(fbop, SOUNDFREQ);
+  fmsynthop_bind_feedback(fbop, fbop, 0.6f);
+  fmsynthop_start(fbop);
+
+  mainop = fmsynthop_create();
+  subop  = fmsynthop_create();
+  fmsynthop_select_opfunc(mainop, FMSYNTH_OPFUNC_SIN);
+  fmsynthop_select_opfunc(subop, FMSYNTH_OPFUNC_SIN);
+  fmsynthop_cascade_subop(mainop, subop);
+  fmsynthop_set_soundfreq(mainop, SOUNDFREQ);
+  fmsynthop_set_soundfreqrate(subop, 2.f);
+  fmsynthop_start(mainop);
+
+  printf("idx,EnvTest,FeedbackTest,CascadeTest,Triangle\n");
+  while (phase_time < TEST_LOOP)
+    {
+      fmsynthop_update_feedback(envop);
+      fmsynthop_update_feedback(fbop);
+      fmsynthop_update_feedback(mainop);
+      fmsynthop_update_feedback(triop);
+
+      printf("%d,%d,%d,%d,%d\n",
+            phase_time,
+            fmsynthop_operate(envop, phase_time),
+            fmsynthop_operate(fbop, phase_time),
+            fmsynthop_operate(mainop, phase_time),
+            fmsynthop_operate(triop, phase_time));
+
+      phase_time++;
+    }
+
+  fmsynthop_delete(envop);
+  fmsynthop_delete(fbop);
+  fmsynthop_delete(mainop);
+  fmsynthop_delete(subop);
+
+  return 0;
+}
diff --git a/audioutils/fmsynth/test/fmsynth_test.c b/audioutils/fmsynth/test/fmsynth_test.c
new file mode 100644
index 000000000..72bb4e9fa
--- /dev/null
+++ b/audioutils/fmsynth/test/fmsynth_test.c
@@ -0,0 +1,130 @@
+/****************************************************************************
+ * apps/audioutils/fmsynth/test/fmsynth_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 <audioutils/fmsynth_eg.h>
+#include <audioutils/fmsynth_op.h>
+#include <audioutils/fmsynth.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define FS (48000)
+#define SOUNDFREQ (3000.f)
+#define TEST_LENGTH ((FS / 1000) * (10 + 20 + 16 + 5 + 30))
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static int16_t my_sample[TEST_LENGTH];
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * name: set_levels
+ ****************************************************************************/
+
+static fmsynth_eglevels_t *set_levels(fmsynth_eglevels_t *level,
+                                      int atk_lvl, int atk_peri,
+                                      int decbrk_lvl, int decbrk_peri,
+                                      int dec_lvl, int dec_peri,
+                                      int sus_lvl, int sus_peri,
+                                      int 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 phase_time;
+
+  fmsynth_eglevels_t levels;
+  fmsynth_sound_t *snd1;
+  fmsynth_op_t *envop;
+  fmsynth_op_t *fbop;
+
+  /* Initialize FM synthesizer */
+
+  fmsynth_initialize(FS);
+
+  /* Operator setup */
+
+  envop  = fmsynthop_create();
+  fbop   = fmsynthop_create();
+
+  set_levels(&levels, 0.12f, 10, 0.06f, 20, 0.1f, 16, 0.1f, 5, 0.f, 70);
+
+  fmsynthop_set_envelope(envop, &levels);
+  fmsynthop_select_opfunc(envop, FMSYNTH_OPFUNC_SIN);
+
+  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, SOUNDFREQ);
+
+  fmsynth_rendering(snd1, my_sample, TEST_LENGTH, 1, NULL, 0);
+
+  for (int i = 0; i < TEST_LENGTH; i++)
+    {
+      printf("%d\n", my_sample[i]);
+    }
+
+  fmsynthop_delete(envop);
+  fmsynthop_delete(fbop);
+
+  fmsynthsnd_delete(snd1);
+
+  return 0;
+}
diff --git a/audioutils/fmsynth/test/opfunc_test.c b/audioutils/fmsynth/test/opfunc_test.c
new file mode 100644
index 000000000..9b928c054
--- /dev/null
+++ b/audioutils/fmsynth/test/opfunc_test.c
@@ -0,0 +1,165 @@
+/****************************************************************************
+ * apps/audioutils/fmsynth/test/opfunc_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 <stdlib.h>
+#include <math.h>
+#include <limits.h>
+
+#include <audioutils/fmsynth_op.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define FS  (48000)
+#define DUMP_PERIOD  (FS * 3 / 2000)
+#define ACCURACY_TEST_PERIOD  (FS * 3)
+
+#define HZ  (4186)
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static opfunc_t func_sin;
+static opfunc_t func_tri;
+static opfunc_t func_saw;
+static opfunc_t func_sqa;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * name: prepare_opfuncs
+ ****************************************************************************/
+
+static void prepare_opfuncs(void)
+{
+  fmsynth_op_t *op;
+
+  op = fmsynthop_create();
+
+  fmsynthop_select_opfunc(op, FMSYNTH_OPFUNC_SIN);
+  func_sin = op->wavegen;
+
+  fmsynthop_select_opfunc(op, FMSYNTH_OPFUNC_TRIANGLE);
+  func_tri = op->wavegen;
+
+  fmsynthop_select_opfunc(op, FMSYNTH_OPFUNC_SAWTOOTH);
+  func_saw = op->wavegen;
+
+  fmsynthop_select_opfunc(op, FMSYNTH_OPFUNC_SQUARE);
+  func_sqa = op->wavegen;
+
+  fmsynthop_delete(op);
+}
+
+/****************************************************************************
+ * name: wavegen_dump
+ ****************************************************************************/
+
+static void wavegen_dump(void)
+{
+  int t;
+  float deltaact;
+
+  deltaact = (float)FMSYNTH_PI * 2. * (float)HZ / (float)FS;
+
+  printf("===== Wave generator Dump ====\n");
+  printf("SIN, TRIANGLE, SAWTOOTH, SQUARE\n");
+  for (t = 0; t < DUMP_PERIOD; t++)
+    {
+      printf("%d, %d, %d, %d\n",
+              func_sin((int)(deltaact * t)),
+              func_tri((int)(deltaact * t)),
+              func_saw((int)(deltaact * t)),
+              func_sqa((int)(deltaact * t))
+      );
+    }
+
+  printf("\n");
+}
+
+/****************************************************************************
+ * name: sin_accuracy_test
+ ****************************************************************************/
+
+static void sin_accuracy_test(void)
+{
+  int t;
+  float delta;
+  float deltaact;
+  float max_diff = 0.f;
+  float ref_sin;
+  int sin_val;
+  float norm_sin;
+  float diff;
+
+  delta  = M_PI * 2. * (float)HZ / (float)FS;
+  deltaact = (float)FMSYNTH_PI * 2. * (float)HZ / (float)FS;
+
+  printf("===== Local SIN function ACCURACY TEST ====\n");
+  for (t = 0; t < ACCURACY_TEST_PERIOD; t++)
+    {
+      sin_val = func_sin((int)(deltaact * t));
+      ref_sin = sinf(delta * t);
+
+      norm_sin = (float)sin_val / (float)SHRT_MAX;
+      printf("t=%d, operator-sin(%d)=%d, norm_sin=%f, sinf(%f)=%f ",
+              t, (int)(deltaact * t), sin_val, norm_sin, delta * t, ref_sin);
+
+      diff = fabsf(norm_sin - ref_sin);
+      max_diff = max_diff < diff ? diff :  max_diff;
+
+      if (diff >= 0.005)
+        {
+          printf("  BIG-DIFF  : %f\n", diff);
+        }
+      else
+        {
+          printf("\n");
+        }
+    }
+
+  printf("\n\nMAX DIFF = %f\n\n", max_diff);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * name: main
+ ****************************************************************************/
+
+int main(void)
+{
+  prepare_opfuncs();
+  sin_accuracy_test();
+  wavegen_dump();
+
+  return 0;
+}
diff --git a/include/audioutils/fmsynth.h b/include/audioutils/fmsynth.h
new file mode 100644
index 000000000..827be95df
--- /dev/null
+++ b/include/audioutils/fmsynth.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+ * apps/include/audioutils/fmsynth.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 __INCLUDE_AUDIOUTILS_FMSYNTH_H
+#define __INCLUDE_AUDIOUTILS_FMSYNTH_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <limits.h>
+#include <stdint.h>
+
+#include <audioutils/fmsynth_eg.h>
+#include <audioutils/fmsynth_op.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define FMSYNTH_MAX_VOLUME (SHRT_MAX)
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+typedef struct fmsynth_sound_s
+{
+  int phase_time;
+  int max_phase_time;
+  int volume;
+  FAR fmsynth_op_t *operators;
+
+  FAR struct fmsynth_sound_s *next_sound;
+} fmsynth_sound_t;
+
+typedef CODE void (*fmsynth_tickcb_t)(unsigned long cbarg);
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+int fmsynth_initialize(int fs);
+FAR fmsynth_sound_t *fmsynthsnd_create(void);
+void fmsynthsnd_delete(FAR fmsynth_sound_t *snd);
+int fmsynthsnd_set_operator(FAR fmsynth_sound_t *snd, FAR fmsynth_op_t *op);
+void fmsynthsnd_set_soundfreq(FAR fmsynth_sound_t *snd, float freq);
+void fmsynthsnd_set_volume(FAR fmsynth_sound_t *snd, float vol);
+int fmsynthsnd_add_subsound(FAR fmsynth_sound_t *top,
+                            FAR fmsynth_sound_t *sub);
+int fmsynth_rendering(FAR fmsynth_sound_t *snd,
+                      FAR int16_t *sample, int sample_num, int chnum,
+                      fmsynth_tickcb_t cb, unsigned long cbarg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* __INCLUDE_AUDIOUTILS_FMSYNTH_H */
diff --git a/include/audioutils/fmsynth_eg.h b/include/audioutils/fmsynth_eg.h
new file mode 100644
index 000000000..040cc7b98
--- /dev/null
+++ b/include/audioutils/fmsynth_eg.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+ * apps/include/audioutils/fmsynth_eg.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_INCLUDE_AUDIOUTILS_FMSYNTH_EG_H
+#define __APPS_INCLUDE_AUDIOUTILS_FMSYNTH_EG_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <limits.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define FMSYNTH_MAX_EGLEVEL  (SHRT_MAX / 8)
+
+#define EGSTATE_ATTACK     (0)
+#define EGSTATE_DECAYBREAK (1)
+#define EGSTATE_DECAY      (2)
+#define EGSTATE_SUSTAIN    (3)
+#define EGSTATE_RELEASE    (4)
+#define EGSTATE_RELEASED   (5)
+#define EGSTATE_MAX        (6)
+
+#define EGSTATE_NUM        EGSTATE_RELEASED
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+struct fmsynth_eglevel_s
+{
+  float level;
+  int period_ms;
+};
+
+typedef struct fmsynth_eglevels_s
+{
+  struct fmsynth_eglevel_s attack;
+  struct fmsynth_eglevel_s decaybrk;
+  struct fmsynth_eglevel_s decay;
+  struct fmsynth_eglevel_s sustain;
+  struct fmsynth_eglevel_s release;
+} fmsynth_eglevels_t;
+
+typedef struct fmsynth_egparam_s
+{
+  int initval;
+  int period;
+  int diff2next;
+} fmsynth_egparam_t;
+
+typedef struct fmsynth_eg_s
+{
+  int state;
+  int state_counter;
+  fmsynth_egparam_t state_params[EGSTATE_MAX];
+} fmsynth_eg_t;
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+FAR fmsynth_eg_t *fmsyntheg_create(void);
+void fmsyntheg_delete(FAR fmsynth_eg_t *eg);
+int fmsyntheg_set_param(FAR fmsynth_eg_t *eg,
+                        int fs, FAR fmsynth_eglevels_t *levels);
+void fmsyntheg_start(FAR fmsynth_eg_t *eg);
+void fmsyntheg_stop(FAR fmsynth_eg_t *eg);
+int fmsyntheg_operate(FAR fmsynth_eg_t *eg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* __APPS_INCLUDE_AUDIOUTILS_FMSYNTH_EG_H */
diff --git a/include/audioutils/fmsynth_op.h b/include/audioutils/fmsynth_op.h
new file mode 100644
index 000000000..aad10de16
--- /dev/null
+++ b/include/audioutils/fmsynth_op.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+ * apps/include/audioutils/fmsynth_op.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_INCLUDE_AUDIOUTILS_FMSYNTH_OP_H
+#define __APPS_INCLUDE_AUDIOUTILS_FMSYNTH_OP_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <audioutils/fmsynth_eg.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define FMSYNTH_PI (0x10000)
+
+#define FMSYNTH_OPFUNC_SIN      (0)
+#define FMSYNTH_OPFUNC_TRIANGLE (1)
+#define FMSYNTH_OPFUNC_SAWTOOTH (2)
+#define FMSYNTH_OPFUNC_SQUARE   (3)
+#define FMSYNTH_OPFUNC_NUM      (4)
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+typedef CODE int (*opfunc_t)(int theta);
+
+typedef struct fmsynth_op_s
+{
+  FAR fmsynth_eg_t  *eg;
+  opfunc_t wavegen;
+  struct fmsynth_op_s *cascadeop;
+  struct fmsynth_op_s *parallelop;
+
+  FAR int *feedback_ref;
+  int feedback_val;
+  int feedbackrate;
+  int last_sigval;
+
+  float freq_rate;
+  float sound_freq;
+  float delta_phase;
+  float current_phase;
+} fmsynth_op_t;
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+int fmsynthop_set_samplerate(int fs);
+
+FAR fmsynth_op_t *fmsynthop_create(void);
+void fmsynthop_delete(FAR fmsynth_op_t *op);
+int fmsynthop_select_opfunc(FAR fmsynth_op_t *op, int type);
+int fmsynthop_set_envelope(FAR fmsynth_op_t *op,
+                           FAR fmsynth_eglevels_t *levels);
+int fmsynthop_cascade_subop(FAR fmsynth_op_t *op,
+                            FAR fmsynth_op_t *subop);
+int fmsynthop_parallel_subop(FAR fmsynth_op_t *op,
+                             FAR fmsynth_op_t *subop);
+int fmsynthop_bind_feedback(FAR fmsynth_op_t *op,
+                            FAR fmsynth_op_t *subop, float ratio);
+int fmsynthop_update_feedback(FAR fmsynth_op_t *op);
+void fmsynthop_set_soundfreq(FAR fmsynth_op_t *op, float freq);
+void fmsynthop_set_soundfreqrate(FAR fmsynth_op_t *op, float rate);
+void fmsynthop_start(FAR fmsynth_op_t *op);
+void fmsynthop_stop(FAR fmsynth_op_t *op);
+int fmsynthop_operate(FAR fmsynth_op_t *op, int phase_time);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* __APPS_INCLUDE_AUDIOUTILS_FMSYNTH_OP_H */