You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2022/05/07 05:56:57 UTC

[incubator-nuttx-apps] branch master updated: audioutils/mml_parser: Add mml_parser library

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 504071991 audioutils/mml_parser: Add mml_parser library
504071991 is described below

commit 5040719911bc7f5cfbe410cd042187b10be649bf
Author: Takayoshi Koizumi <ta...@gmail.com>
AuthorDate: Mon May 2 09:54:30 2022 -0700

    audioutils/mml_parser: Add mml_parser library
    
    Add Music Macro Language parser library in audioutils.
    audioutils is also created for several audio utilities
    not only mml_parser.
---
 audioutils/.gitignore                 |   1 +
 audioutils/Make.defs                  |  21 +
 audioutils/Makefile                   |  23 +
 audioutils/mml_parser/Kconfig         |  10 +
 audioutils/mml_parser/Make.defs       |  23 +
 audioutils/mml_parser/Makefile        |  27 ++
 audioutils/mml_parser/README.md       | 211 +++++++++
 audioutils/mml_parser/mml_parser.c    | 850 ++++++++++++++++++++++++++++++++++
 examples/mml_parser/Kconfig           |  11 +
 examples/mml_parser/Make.defs         |  23 +
 examples/mml_parser/Makefile          |  32 ++
 examples/mml_parser/mml_parser_main.c | 195 ++++++++
 include/audioutils/mml_parser.h       | 102 ++++
 13 files changed, 1529 insertions(+)

diff --git a/audioutils/.gitignore b/audioutils/.gitignore
new file mode 100644
index 000000000..9e1d2593e
--- /dev/null
+++ b/audioutils/.gitignore
@@ -0,0 +1 @@
+/Kconfig
diff --git a/audioutils/Make.defs b/audioutils/Make.defs
new file mode 100644
index 000000000..67f1a3033
--- /dev/null
+++ b/audioutils/Make.defs
@@ -0,0 +1,21 @@
+############################################################################
+# apps/audioutils/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.
+#
+############################################################################
+
+include $(wildcard $(APPDIR)/audioutils/*/Make.defs)
diff --git a/audioutils/Makefile b/audioutils/Makefile
new file mode 100644
index 000000000..7b4abe0e9
--- /dev/null
+++ b/audioutils/Makefile
@@ -0,0 +1,23 @@
+############################################################################
+# apps/audioutils/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.
+#
+############################################################################
+
+MENUDESC = "Audio Utility libraries"
+
+include $(APPDIR)/Directory.mk
diff --git a/audioutils/mml_parser/Kconfig b/audioutils/mml_parser/Kconfig
new file mode 100644
index 000000000..2fe9d3681
--- /dev/null
+++ b/audioutils/mml_parser/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_MMLPARSER_LIB
+	bool "Music Macro Language(MML) Library"
+	default n
+	---help---
+		Enable support for the Music Macro Language library.
diff --git a/audioutils/mml_parser/Make.defs b/audioutils/mml_parser/Make.defs
new file mode 100644
index 000000000..32fb08cd1
--- /dev/null
+++ b/audioutils/mml_parser/Make.defs
@@ -0,0 +1,23 @@
+############################################################################
+# apps/audioutils/mml_parser/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_MMLPARSER_LIB),y)
+CONFIGURED_APPS += $(APPDIR)/audioutils/mml_parser
+endif
diff --git a/audioutils/mml_parser/Makefile b/audioutils/mml_parser/Makefile
new file mode 100644
index 000000000..361369c8c
--- /dev/null
+++ b/audioutils/mml_parser/Makefile
@@ -0,0 +1,27 @@
+############################################################################
+# apps/audioutils/mml_parser/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
+
+# NSH Library
+
+CSRCS   = mml_parser.c
+
+include $(APPDIR)/Application.mk
diff --git a/audioutils/mml_parser/README.md b/audioutils/mml_parser/README.md
new file mode 100644
index 000000000..07573ad4a
--- /dev/null
+++ b/audioutils/mml_parser/README.md
@@ -0,0 +1,211 @@
+# Music Macro Language (MML) Parser library
+
+MML has often been used as a language for describing music in strings, for example,
+in the BASIC language. The mml_parser is a minimalistic Music Macro Language parser
+library written in pure C intended for resource-constrained platforms, especially
+microcontrollers and other embedded systems.
+
+## Supported Syntax on this library
+
+### Notes
+
+* **C** (Do)
+* **D** (Re)
+* **E** (Mi)
+* **F** (Fa)
+* **G** (Sol)
+* **A** (La)
+* **B** (Ti)
+
+### Sharp and Flat
+
+Add "#" or "+" after the note indicates Sharp. ex: ``"C#"`` ``"C+."``
+Add "-" after the note indicates Flat. ex: ``"C-"``
+
+### Length
+
+Length of the tone can be specified in two ways.
+One is to specify a length for each note. Add a number after the note in 1, 2, 4, 8, 16, 32 or 64.
+ex: ``"C8 C16 C#4."``
+
+The other is to use ``L`` . The ``L`` sets default length. If the note without length, the number which is
+indicated by the "L" is used.  The number followed after the "L" can be in 1, 2, 4, 8, 16, 32 or 64.
+ex: ``"L4 A"`` means "A" with length 4.
+
+In addition, dot is supported. For example, length of "C4." is 4 + 8. Length of "C16.." is 4 + 8 + 16.
+
+### Rest
+
+Rest is represented by "R".
+Length of the rest is following after the "R" as the same as the note length.  If no length is
+specified, the length specified by the ”L" is used.
+
+### Chord
+
+Chord is supported. If some notes are enclosed in parentheses by ``[`` and ``]``, they are interpreted as
+a chord. ex: ``"[CEG]"`` is a chord of Do, Mi and Sol.
+Chord's length can be put after ``]`` . ex: ``"[CEG]4"`` is a chord with 4 length.
+
+Note: Max notes in a chord is defined as MAX_CHORD_NOTES in ``mml_parser.h``.
+
+### Tuplet
+
+Tuplet is supported. If some notes and Chord are enclosed in parentheses by ``{`` and ``}``, they are
+interpreted as a tuplet. ex: ``"{C E G [CEG]}"`` is a tuplet with C, E, G and chord of CEG.
+Tuplet's length can be put after "}", and the length is divided equally among each note.
+ex: in ``"{C E G [CEG]}4"`` case, C, E, G and chord CEG has each a quarter of the L4 length.
+
+### Octave
+
+Octarve is controlled by "O", ">" or "<".
+When "O" is used, the O is followed by a number indicating the octave.
+When ">" is used, the value of the new octave is the current octave plus one.
+When "<" is used, the value of the new octave is the current octave minus one.
+ex: ``"CDEFGAB > C R C < BAGFEDC"``, ``"O4 CDEFGAB O5 C R C O4 BAGFEDC"``
+
+### Tempo
+
+Tempo is indicated as "T" and numter following after the "T".
+Tempo number decide a speed of the score. This value is used for culculating sample number for 
+the note (or rest).
+ex: ``"T120"``
+
+### Volume
+
+Volume can be controlled by "V". And numter following after the "V".
+ex: ``"V4"``
+
+## Example of a score
+
+### The beginning of the Do Re Mi Song.
+
+Tempo 120, Voulume 10, Octave 4, Default length is 4.
+
+``"T120 V10 O4 L4 C. D8 E. C8 E C E2 D. E8 {FF} {ED} F2"``
+
+## Provided C Functions
+
+mml_parser is providing just 2 functions.
+
+### init_mml()
+
+Initialize an instance of mml parser.
+
+#### Synopsis
+
+```c
+#include <audioutils/mml_parser.h>
+
+int init_mml(FAR struct music_macro_lang_s *mml,
+                               int fs, int tempo, int octave, int length);
+```
+
+#### Description
+
+The function initializes `struct music_macro_lang_s` instance provided as 1st argument.
+The argument `fs` is a sampling frequency of target audio output system, and this value is used for
+calculating sample number in case of a tempo and length of note.
+`tempo`, `octave` and `length` specify initial values for tempo, octave, and length, respectively.
+
+#### Return value
+
+On success, init_mml() returns 0. On error, it returns an negative value.
+
+#### Errors
+
+Currently no error is happened.
+
+### parse_mml()
+
+Parse MML from given string.
+
+#### Synopsis
+
+```c
+#include <audioutils/mml_parser.h>
+
+int parse_mml(FAR struct music_macro_lang_s *mml,
+                        FAR char **score, FAR struct mml_result_s *result);
+```
+
+#### Description
+
+parse_mml() parses the first MML of the string given by the argument ``score`` and gives
+the result in the return value and the argument ``result``.
+The ``result`` is an instance of mml_result_s, which contains note_idx, length, and
+chord_notes as members. The meaning of the value of each member depends on the return value.
+
+#### Return value
+
+On error, a nevative value is returned.
+On success, following values can be returned. And those values are defined in ``mml_parser.h``.
+
+| Return values        | Description |
+| -------------------- | ----------- |
+| MML_TYPE_EOF         | This means that it have reached the end of the string. The content of the ``result`` has no meaning. |
+| MML_TYPE_NOTE        | This indicates that some note has been parsed. The scale of the note is stored in ``note_idx[0]``. The length of the note is given by the ``length`` member as the number of samples. In the case of tuplet, this return value is returned at the time each note is parsed. In other words, a tuplet is parsed as a single note. |
+| MML_TYPE_REST        | This indicates the ``rest`` has been parsed. The length of it is given by the ``length`` member as the number of samples. |
+| MML_TYPE_TEMPO       | This indicates ``"T"`` is parsed. ``length`` member in ``result`` has the value of the tempo. But tempo value is kept in mml instance for calculating sample number for each notes. So basically, no need to handle this return value in your code. |
+| MML_TYPE_LENGTH      | This indicates ``"L"`` is parsed. ``length`` member in ``result`` has the value of the parsed length. But current length value is kept in mml instance. So basically, no need to handle this return value in your code. |
+| MML_TYPE_OCTAVE      | This indicates ``"O"``, ``">"``, or ``"<"`` is parsed. ``length`` member in ``result`` has the value of the octave. But the octave is encoded in ``note_idx`` in ``MML_TYPE_NOTE`` case. So basically, no need to handle this return value in your code. |
+| MML_TYPE_TUPLETSTART | This indicates tuplet is just started. And total length of the tuplet is stored in ``length`` of ``result`` members. |
+| MML_TYPE_TUPLETDONE  | This indicates the tuplet is just finished. |
+| MML_TYPE_VOLUME      | This indicates ``"V"`` is parsed. ``length`` member in ``result`` has the value of the parsed volume. |
+| MML_TYPE_TONE        | T.B.D. |
+| MML_TYPE_CHORD       | This indicates a chord is parsed. Chord has some notes, and how many notes is stored in ``chord_notes`` member of ``result``. And each notes are stored in ``note_idx[]``. Length of the chord is stored in ``length`` member of ``result``. |
+
+The value of ``note_idx[]`` is encoding octave, like
+
+| Octave | Note | node_idx value |
+| ------ | ---- |:--------------:|
+|  O0    |  C   |       0        |
+|  O0    |  C#  |       1        |
+|  O0    |  D   |       2        |
+|  O0    |  D#  |       3        |
+|  O0    |  E   |       4        |
+|  O0    |  F   |       5        |
+|  O0    |  F#  |       6        |
+|  O0    |  G   |       7        |
+|  O0    |  G#  |       8        |
+|  O0    |  A   |       9        |
+|  O0    |  A#  |       10       |
+|  O0    |  B   |       11       |
+|  O1    |  C   |       12       |
+
+And so on.
+
+So for example, G# at Octave 4 is encoded as 56.
+
+#### Errors
+
+Following error code can be received as return value.
+
+|  Error code                    |   Description  |
+| ------------------------------ | -------------- |
+| MML_TYPE_NOTE_ERROR            |                |
+| MML_TYPE_REST_ERROR            |                |
+| MML_TYPE_TEMPO_ERROR           |                |
+| MML_TYPE_LENGTH_ERROR          |                |
+| MML_TYPE_OCTAVE_ERROR          |                |
+| MML_TYPE_VOLUME_ERROR          |                |
+| MML_TYPE_TUPLET_ERROR          |                |
+| MML_TYPE_TONE_ERROR            |                |
+| MML_TYPE_CHORD_ERROR           |                |
+| MML_TYPE_ILLIGAL_COMPOSITION   |                |
+| MML_TYPE_ILLIGAL_TOOMANY_NOTES |                |
+| MML_TYPE_ILLIGAL_TOOFEW_NOTES  |                |
+| MML_TYPE_ILLIGAL_DOUBLE_TUPLET |                |
+
+
+## Running unit tests
+
+Please see examples/mml_parser
+
+## Bugs
+
+There are plenty. Report them on GitHub, or - even better - open a pull request.
+Please write unit tests for any new functions you add - it's fun!
+
+## Author
+
+mml_parser was written by Takayoshi Koizumi &lt;takayoshi.koizumi@gmail.com&gt;
diff --git a/audioutils/mml_parser/mml_parser.c b/audioutils/mml_parser/mml_parser.c
new file mode 100644
index 000000000..7920fbb41
--- /dev/null
+++ b/audioutils/mml_parser/mml_parser.c
@@ -0,0 +1,850 @@
+/****************************************************************************
+ * apps/audioutils/mml_parser/mml_parser.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 <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <audioutils/mml_parser.h>
+
+#ifdef DEBUG_ON 
+#include <stdio.h>
+#endif
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define LENGTH_STATE_IDLE     (0)
+#define LENGTH_STATE_NUMBERD  (1)
+#define LENGTH_STATE_PLUS     (2)
+
+#define CHORD_START '['
+#define CHORD_END   ']'
+
+#define TUPLET_START '{'
+#define TUPLET_END   '}'
+
+#ifdef DEBUG_ON 
+#define DEBUG printf
+#else
+#define DEBUG(...)
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * name: skip_space
+ ****************************************************************************/
+
+static char *skip_space(char *str)
+{
+  while (isspace(*str))
+    {
+      str++;
+    }
+
+  return str;
+}
+
+/****************************************************************************
+ * name: next_code
+ ****************************************************************************/
+
+static char next_code(char **score)
+{
+  char ret;
+  *score = skip_space(*score);
+  ret = **score;
+  *score += 1;
+  return ret;
+}
+
+/****************************************************************************
+ * name: note_index
+ ****************************************************************************/
+
+static int note_index(char code)
+{
+  int i;
+  const char *code_types = "C+D+EF+G+A+B";
+
+  for (i = 0; i < 12; i++)
+    {
+      if (code_types[i] == code)
+        {
+          return i;
+        }
+    }
+
+  return -1;
+}
+
+/****************************************************************************
+ * name: halfscale
+ ****************************************************************************/
+
+static int halfscale(char **score)
+{
+  int ret = 0;
+
+  while (1)
+    {
+      switch (**score)
+        {
+          case '+':
+          case '#':
+            ret++;
+            break;
+          case '-':
+            ret--;
+            break;
+          default:
+            return ret;
+        }
+
+      *score += 1;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * name: strlendigits
+ ****************************************************************************/
+
+static int strlendigits(FAR const char *str)
+{
+  int ret = 0;
+
+  while (isdigit(*str))
+    {
+      str++;
+      ret++;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * name: calc_samples
+ ****************************************************************************/
+
+static int calc_samples(int fs, int tempo, int num, int dots)
+{
+  int div = 0;
+  int n = 0;
+  int mul = 16;
+
+  DEBUG("fs=%d, tempo=%d, num=%d, dots=%d\n", fs, tempo, num, dots);
+
+  switch (num)
+    {
+      case 0:  n   = 3; break;
+      case 1:  n   = 2; break;
+      case 2:  n   = 1; break;
+      case 4:  n   = 0; break;
+      case 8:  div = 1; break;
+      case 16: div = 2; break;
+      case 32: div = 3; break;
+      case 64: div = 4; break;
+      default: div = -1; break;
+    }
+
+  if (dots <= 4)
+    {
+      while (dots)
+        {
+          mul += (1 << (4 - dots));
+          dots--;
+        }
+    }
+  else
+    {
+      dots = -1;
+    }
+
+  if (div < 0 || dots < 0)
+    {
+      return -EINVAL;
+    }
+
+  return (((15 * fs * mul) << n) >> (2 + div)) / tempo;
+}
+
+/****************************************************************************
+ * name: get_samples
+ ****************************************************************************/
+
+static int get_samples(FAR struct music_macro_lang_s *mml,
+                       int samples, int num, int dots, bool plus_mode)
+{
+  int len;
+
+  num = num < 0 ? mml->def_length : num;
+  len = calc_samples(mml->fs, mml->cur_tempo, num, dots);
+
+  if (len > 0)
+    {
+      return plus_mode ? samples + len : len;
+    }
+
+  return len;
+}
+
+/****************************************************************************
+ * name: is_qualifire
+ ****************************************************************************/
+
+static bool is_qualifire(char c)
+{
+  if (isdigit(c) || c == '.')
+    {
+      return true;
+    }
+
+  return false;
+}
+
+/****************************************************************************
+ * name: sample_length
+ ****************************************************************************/
+
+static int sample_length(FAR struct music_macro_lang_s *mml,
+                         FAR char **score)
+{
+  int dots = 0;
+  int samples = 0;
+  int state = LENGTH_STATE_IDLE;
+  bool plus_mode = false;
+  bool parsing = true;
+  int num = -1;
+
+  samples = get_samples(mml, samples, num, dots, plus_mode);
+
+  if (!is_qualifire(**score))
+    {
+      return samples;
+    }
+
+  while (parsing)
+    {
+      DEBUG("In Length parser\n");
+      switch (state)
+        {
+          case LENGTH_STATE_IDLE:
+            if (isdigit(**score))
+              {
+                DEBUG("state[IDLE]: Digits\n");
+                num = atoi(*score);
+                *score += strlendigits(*score);
+                state = LENGTH_STATE_NUMBERD;
+              }
+            else if (**score == '.')
+              {
+                DEBUG("state[IDLE]: Dot\n");
+                state = LENGTH_STATE_NUMBERD;
+                dots++;
+                *score += 1;
+              }
+            else
+              {
+                DEBUG("state[IDLE]: Other\n");
+                samples = get_samples(mml, samples, num, dots, plus_mode);
+                parsing = false;
+              }
+
+          case LENGTH_STATE_NUMBERD:
+            if (**score == '.')
+              {
+                DEBUG("state[NUM]: Dot\n");
+                dots++;
+                *score += 1;
+              }
+            else if (**score == '+')
+              {
+                DEBUG("state[NUM]: PLUS\n");
+                samples = get_samples(mml, samples, num, dots, plus_mode);
+                if (samples < 0)
+                  {
+                    parsing = false;
+                  }
+
+                plus_mode = true;
+                num = -1;
+                *score += 1;
+                state = LENGTH_STATE_PLUS;
+              }
+            else
+              {
+                DEBUG("state[NUM]: Other\n");
+                samples = get_samples(mml, samples, num, dots, plus_mode);
+                parsing = false;
+              }
+
+            break;
+
+          case LENGTH_STATE_PLUS:
+            if (isdigit(**score))
+              {
+                num = atoi(*score);
+                *score += strlendigits(*score);
+                DEBUG("state[PLUS]: Digits num=%d, restscore=%s,"
+                      " parsing=%s\n",
+                       num, *score, parsing ? "True" : "False");
+                state = LENGTH_STATE_NUMBERD;
+              }
+            else
+              {
+                DEBUG("state[PLUS]: Other\n");
+                samples = -EINVAL;
+                parsing = false;
+              }
+
+            break;
+
+          default:
+            parsing = false;
+            samples = -EPROTO;
+            break;
+        }
+
+      DEBUG("Out switch : state[%s]: Digits num=%d, restscore=%s,"
+                        " parse=%s\n",
+                        state == LENGTH_STATE_IDLE ? "IDLE" :
+                        state == LENGTH_STATE_NUMBERD ? "NUM" :
+                        state == LENGTH_STATE_PLUS ? "PLUS" : "Unknown",
+                        num, *score, parsing ? "True" : "False");
+    }
+
+  DEBUG("Out while\n");
+
+  return samples;
+}
+
+/****************************************************************************
+ * name: tuple_length
+ ****************************************************************************/
+
+static int tuplet_length(FAR struct music_macro_lang_s *mml)
+{
+  int ret;
+
+  ret = mml->tuplet_length / mml->tuplet_notes;
+  mml->cur_tuplet++;
+  if (mml->cur_tuplet == mml->tuplet_notes)
+    {
+      /* Adjust surplus */
+
+      ret = mml->tuplet_length - (ret * (mml->tuplet_notes - 1));
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * name: handle_note
+ ****************************************************************************/
+
+static int handle_note(FAR struct music_macro_lang_s *mml, char code,
+                       FAR char **score, FAR struct mml_result_s *result)
+{
+  result->note_idx[0] = note_index(code) + halfscale(score)
+                    + mml->cur_octave * 12;
+  result->length = sample_length(mml, score);
+
+  return result->length < 0 ? MML_TYPE_NOTE_ERROR : MML_TYPE_NOTE;
+}
+
+/****************************************************************************
+ * name: handle_rest
+ ****************************************************************************/
+
+static int handle_rest(FAR struct music_macro_lang_s *mml,
+                       FAR char **score, FAR struct mml_result_s *result)
+{
+  if (mml->state == MML_STATE_TUPLET)
+    {
+      DEBUG("Tuplet : TTL %d, CUR %d\n", mml->tuplet_notes, mml->cur_tuplet);
+      result->length = tuplet_length(mml);
+      return MML_TYPE_REST;
+    }
+  else
+    {
+      result->length = sample_length(mml, score);
+      return result->length < 0 ? MML_TYPE_REST_ERROR : MML_TYPE_REST;
+    }
+}
+
+/****************************************************************************
+ * name: handle_tempo
+ ****************************************************************************/
+
+static int handle_tempo(FAR struct music_macro_lang_s *mml,
+                        FAR char **score, FAR struct mml_result_s *result)
+{
+  int ret = MML_TYPE_TEMPO;
+
+  if (isdigit(**score))
+    {
+      mml->cur_tempo = result->length = atoi(*score);
+      *score += strlendigits(*score);
+    }
+  else
+    {
+      ret = MML_TYPE_TEMPO_ERROR;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * name: handle_length
+ ****************************************************************************/
+
+static int handle_length(FAR struct music_macro_lang_s *mml,
+                         FAR char **score, FAR struct mml_result_s *result)
+{
+  int ret = MML_TYPE_LENGTH_ERROR;
+
+  DEBUG("length str : %c\n", **score);
+  if (isdigit(**score))
+    {
+      mml->def_length = result->length = atoi(*score);
+      *score += strlendigits(*score);
+      ret = MML_TYPE_LENGTH;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * name: handle_octave
+ ****************************************************************************/
+
+static int handle_octave(FAR struct music_macro_lang_s *mml, char code,
+                         FAR char **score, FAR struct mml_result_s *result)
+{
+  int ret = MML_TYPE_OCTAVE;
+
+  switch (code)
+    {
+      case '>':
+        mml->cur_octave++;
+        result->length = mml->cur_octave;
+        break;
+
+      case '<':
+        mml->cur_octave--;
+        result->length = mml->cur_octave;
+        break;
+
+      default:
+        if (isdigit(**score))
+          {
+            mml->cur_octave = result->length = atoi(*score);
+            *score += strlendigits(*score);
+          }
+        else
+          {
+            ret = MML_TYPE_OCTAVE_ERROR;
+          }
+
+        break;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * name: handle_volume
+ ****************************************************************************/
+
+static int handle_volume(FAR struct music_macro_lang_s *mml,
+                         FAR char **score, FAR struct mml_result_s *result)
+{
+  int ret = MML_TYPE_VOLUME;
+
+  result->length = atoi(*score);
+  *score += strlendigits(*score);
+
+  if (result->length < 0 || result->length > 100)
+    {
+      ret = MML_TYPE_VOLUME_ERROR;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * name: skip_until
+ ****************************************************************************/
+
+static char *skip_until(FAR char *score, char until)
+{
+  while (*score != until && *score != '\0')
+    {
+      score++;
+    }
+
+  return score;
+}
+
+/****************************************************************************
+ * name: count_tupletnotes
+ ****************************************************************************/
+
+static int count_tupletnotes(FAR struct music_macro_lang_s *mml,
+                             FAR char *score)
+{
+  const char *notes = "CDEFGABR";
+
+  score = skip_space(score);
+
+  while (*score != TUPLET_END)
+    {
+      if (strchr(notes, *score))
+        {
+          mml->tuplet_notes++;
+        }
+      else if (*score == CHORD_START)
+        {
+          score = skip_until(score, CHORD_END);
+          mml->tuplet_notes++;
+        }
+
+      if (*score == '\0')
+        {
+          return -EINVAL;
+        }
+
+      score++;
+      score = skip_space(score);
+    }
+
+  score++;  /* Skip TUPLET_END */
+  mml->tuplet_length = sample_length(mml, &score);
+
+  return mml->tuplet_notes != 0 ? OK : -EINVAL;
+}
+
+/****************************************************************************
+ * name: handle_starttuplet
+ ****************************************************************************/
+
+static int handle_starttuplet(FAR struct music_macro_lang_s *mml,
+                              FAR char **score,
+                              FAR struct mml_result_s *result)
+{
+  int ret;
+
+  if (mml->state != MML_STATE_NORMAL)
+    {
+      return MML_TYPE_ILLIGAL_DOUBLE_TUPLET;
+    }
+
+  mml->tuplet_notes = 0;
+  ret = count_tupletnotes(mml, *score);
+  if (ret < 0 || mml->tuplet_notes == 0)
+    {
+      return MML_TYPE_TUPLET_ERROR;
+    }
+
+  mml->state = MML_STATE_TUPLET;
+  mml->cur_tuplet = 0;
+
+  result->length = mml->tuplet_length;
+
+  return MML_TYPE_TUPLETSTART;
+}
+
+/****************************************************************************
+ * name: handle_stoptuplet
+ ****************************************************************************/
+
+static int handle_stoptuplet(FAR struct music_macro_lang_s *mml,
+                             FAR char **score,
+                             FAR struct mml_result_s *result)
+{
+  int ret = MML_TYPE_TUPLETDONE;
+
+  mml->state = MML_STATE_NORMAL;
+
+  /* Just for skip of length block */
+
+  sample_length(mml, score);
+
+  if (mml->cur_tuplet != mml->tuplet_notes)
+    {
+      ret = MML_TYPE_ILLIGAL_TOOFEW_NOTES;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * name: handle_tupletnote
+ ****************************************************************************/
+
+static int handle_tupletnote(FAR struct music_macro_lang_s *mml, char code,
+                             FAR char **score,
+                             FAR struct mml_result_s *result)
+{
+  int ret = MML_TYPE_NOTE;
+
+  DEBUG("Tuplet : TTL %d, CUR %d\n", mml->tuplet_notes, mml->cur_tuplet);
+  if (mml->cur_tuplet < mml->tuplet_notes)
+    {
+      result->note_idx[0] = note_index(code) + halfscale(score)
+                         + mml->cur_octave * 12;
+      result->length = tuplet_length(mml);
+    }
+  else
+    {
+      ret = MML_TYPE_ILLIGAL_TOOMANY_NOTES;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * name: handle_notes
+ ****************************************************************************/
+
+static int handle_notes(FAR struct music_macro_lang_s *mml, char code,
+                        FAR char **score,
+                        FAR struct mml_result_s *result)
+{
+  int ret;
+
+  result->chord_notes = 1;
+
+  if (mml->state == MML_STATE_TUPLET)
+    {
+      ret = handle_tupletnote(mml, code, score, result);
+    }
+  else
+    {
+      ret = handle_note(mml, code, score, result);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * name: handle_startchord
+ ****************************************************************************/
+
+static int handle_startchord(FAR struct music_macro_lang_s *mml,
+                             FAR char **score,
+                             FAR struct mml_result_s *result)
+{
+  char code;
+  int note_idx;
+
+  code = next_code(score);
+
+  while (code != CHORD_END && code != '\0')
+    {
+      DEBUG("CHORD: %c\n", code);
+      note_idx = note_index(code);
+      DEBUG("CHORD note_idx = %d\n", note_idx);
+      if (note_idx >= 0)
+        {
+          note_idx += halfscale(score) + mml->cur_octave * 12;
+
+          if (result->chord_notes < MAX_CHORD_NOTES)
+            {
+              result->note_idx[result->chord_notes] = note_idx;
+              result->chord_notes++;
+            }
+
+          /* In case of chord notes are over than MAX_CHORD_NOTES,
+           * just ignore overflowed notes. Not behave as an error.
+           */
+        }
+      else
+        {
+          switch (code)
+            {
+              case 'O':
+              case '>':
+              case '<':
+                if (handle_octave(mml, code, score, result) < 0)
+                  {
+                    return MML_TYPE_CHORD_ERROR;
+                  }
+                break;
+
+              default:
+                return MML_TYPE_CHORD_ERROR;
+                break;
+            }
+        }
+
+      code = next_code(score);
+    }
+
+  if (code == '\0')
+    {
+      return MML_TYPE_CHORD_ERROR;
+    }
+
+  if (mml->state == MML_STATE_TUPLET)
+    {
+      result->length = tuplet_length(mml);
+    }
+  else
+    {
+      result->length = sample_length(mml, score);
+    }
+
+  return result->length < 0 ? MML_TYPE_CHORD_ERROR : MML_TYPE_CHORD;
+}
+
+/****************************************************************************
+ * name: handle_tone
+ ****************************************************************************/
+
+static int handle_tone(FAR struct music_macro_lang_s *mml,
+                       FAR char **score, FAR struct mml_result_s *result)
+{
+  int ret = MML_TYPE_TONE_ERROR;
+
+  if (isdigit(**score))
+    {
+      result->note_idx[0] = atoi(*score);
+      *score += strlendigits(*score);
+      ret = MML_TYPE_TONE;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * name: init_mml
+ ****************************************************************************/
+
+int init_mml(FAR struct music_macro_lang_s *mml, int fs,
+             int tempo, int octave, int length)
+{
+  mml->fs = fs;
+  mml->cur_tempo = tempo;
+  mml->cur_octave = octave;
+  mml->def_length = length;
+
+  mml->state = MML_STATE_NORMAL;
+  mml->cur_tuplet = 0;
+  mml->tuplet_notes = 0;
+  mml->tuplet_length = 0;
+
+  return 0;
+}
+
+/****************************************************************************
+ * name: parse_mml
+ ****************************************************************************/
+
+int parse_mml(FAR struct music_macro_lang_s *mml, FAR char **score,
+              FAR struct mml_result_s *result)
+{
+  int ret;
+  char code;
+
+  code = next_code(score);
+  DEBUG("code=%c\n", code);
+  result->chord_notes = 0;
+
+  switch (code)
+    {
+      case 'A':
+      case 'B':
+      case 'C':
+      case 'D':
+      case 'E':
+      case 'F':
+      case 'G':
+        ret = handle_notes(mml, code, score, result);
+        break;
+
+      case 'R':
+        ret = handle_rest(mml, score, result);
+        break;
+
+      case 'T':
+        ret = handle_tempo(mml, score, result);
+        break;
+
+      case 'L':
+        ret = handle_length(mml, score, result);
+        break;
+
+      case 'O':
+      case '>':
+      case '<':
+        ret = handle_octave(mml, code, score, result);
+        break;
+
+      case 'V':
+        ret = handle_volume(mml, score, result);
+        break;
+
+      case CHORD_START:
+        ret = handle_startchord(mml, score, result);
+        break;
+
+      case TUPLET_START:
+        ret = handle_starttuplet(mml, score, result);
+        break;
+
+      case TUPLET_END:
+        ret = handle_stoptuplet(mml, score, result);
+        break;
+
+      case '@':
+        ret = handle_tone(mml, score, result);
+        break;
+
+      case '\0':
+        ret = MML_TYPE_EOF;
+        *score -= 1;  /* Backward */
+        break;
+
+      default:
+        ret = MML_TYPE_ILLIGAL_COMPOSITION;
+        break;
+    }
+
+  return ret;
+}
diff --git a/examples/mml_parser/Kconfig b/examples/mml_parser/Kconfig
new file mode 100644
index 000000000..8ff8c4135
--- /dev/null
+++ b/examples/mml_parser/Kconfig
@@ -0,0 +1,11 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+config EXAMPLES_MMLPARSER
+	tristate "mml_parser example"
+	default n
+	---help---
+		Enable the mml_parser example
+
diff --git a/examples/mml_parser/Make.defs b/examples/mml_parser/Make.defs
new file mode 100644
index 000000000..e24a6d54e
--- /dev/null
+++ b/examples/mml_parser/Make.defs
@@ -0,0 +1,23 @@
+############################################################################
+# apps/examples/mml_parser/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_MMLPARSER),)
+CONFIGURED_APPS += $(APPDIR)/examples/mml_parser
+endif
diff --git a/examples/mml_parser/Makefile b/examples/mml_parser/Makefile
new file mode 100644
index 000000000..99bf56503
--- /dev/null
+++ b/examples/mml_parser/Makefile
@@ -0,0 +1,32 @@
+############################################################################
+# apps/examples/mml_parser/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
+
+MAINSRC = mml_parser_main.c
+
+# application info
+
+PROGNAME = mmlparser
+PRIORITY = SCHED_PRIORITY_DEFAULT
+STACKSIZE = $(CONFIG_DEFAULT_TASK_STACKSIZE)
+MODULE = $(CONFIG_EXAMPLES_MMLPARSER)
+
+include $(APPDIR)/Application.mk
diff --git a/examples/mml_parser/mml_parser_main.c b/examples/mml_parser/mml_parser_main.c
new file mode 100644
index 000000000..9bd1adeba
--- /dev/null
+++ b/examples/mml_parser/mml_parser_main.c
@@ -0,0 +1,195 @@
+/****************************************************************************
+ * apps/examples/mml_parser/mml_parser_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>
+
+#ifndef CONFIG_AUDIOUTILS_MMLPARSER_LIB
+#error "This example needs to enable config of AUDIOUTILS_MMLPARSER_LIB," \
+       " please enable it"
+#endif
+
+#include <audioutils/mml_parser.h>
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#define SIMPLE_SCORE  \
+  "O0 CC+DD+EE+FF+GG+AA+BB+" \
+  "O1 CC-DD-EE-FF-GG-AA-BB-" \
+  "O2 CC#DD#EE#FF#GG#AA#BB#"
+
+#define TEST_SCORE \
+  "T240L0O3D>F+>E8 V8{R [CE O7 < G] C+#- C >B}8 [G B > C]16" \
+  "T120 D> C A8 B. A8+4 C+8.D#+4+4 E- C.+4."
+
+#define 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"
+
+#define 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"
+
+static const char *test_scores[] =
+{
+  SIMPLE_SCORE, TEST_SCORE, FLOH_WALZER_RIGHT, FLOH_WALZER_LEFT,
+};
+
+#define TEST_SCORES_NUM (sizeof(test_scores)/sizeof(test_scores[0]))
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: print_parse_result
+ ****************************************************************************/
+
+int print_parse_result(int ret_code, FAR struct mml_result_s *result)
+{
+  int passed_tick = 0;
+
+  switch (ret_code)
+    {
+      case MML_TYPE_NOTE:
+        printf("Note(%2d) : length = %d\n",
+            result->note_idx[0], result->length);
+        passed_tick += result->length;
+        break;
+      case MML_TYPE_REST:
+        printf("Rest     : length = %d\n", result->length);
+        passed_tick += result->length;
+        break;
+      case MML_TYPE_TEMPO:
+        printf("Tempo    : length = %d\n", result->length);
+        break;
+      case MML_TYPE_LENGTH:
+        printf("Length   : length = %d\n", result->length);
+        break;
+      case MML_TYPE_OCTAVE:
+        printf("Octave   : length = %d\n", result->length);
+        break;
+      case MML_TYPE_TUPLETSTART:
+        printf("Tuplet B : length = %d\n", result->length);
+        break;
+      case MML_TYPE_TUPLETDONE:
+        printf("Tuplet D :\n");
+        break;
+      case MML_TYPE_VOLUME:
+        printf("Volume   : length = %d\n", result->length);
+        break;
+      case MML_TYPE_CHORD:
+        printf("Chord    : length = %d, chord_num = %d\n",
+            result->length, result->chord_notes);
+        printf("         : Notes ");
+        {
+          int i;
+          for (i = 0; i < result->chord_notes; i++)
+            {
+              printf("%d ", result->note_idx[i]);
+            }
+
+          printf("\n");
+        }
+
+        passed_tick += result->length;
+        break;
+    }
+
+  return passed_tick;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: main
+ ****************************************************************************/
+
+int main(void)
+{
+  int i;
+  int ret;
+  int total_tick;
+
+  FAR char *score;
+  struct music_macro_lang_s mml;
+  struct mml_result_s result;
+
+  for (i = 0; i < TEST_SCORES_NUM; i++)
+    {
+      init_mml(&mml, 48000, 1, 0, 4);
+
+      score = (char *)test_scores[i];
+      printf("===================================\n");
+      printf("Test Score :\n%s\n\n", score);
+
+      total_tick = 0;
+      while ((ret = parse_mml(&mml, &score, &result)) > 0)
+        {
+          total_tick += print_parse_result(ret, &result);
+        }
+
+      if (ret < 0)
+        {
+          printf("\nret = %d\n", ret);
+          printf("Error was occured below:\n");
+          printf("%s\n", score);
+          break;
+        }
+
+      printf("\n=== Total Tick : %d\n\n", total_tick);
+    }
+
+  return 0;
+}
diff --git a/include/audioutils/mml_parser.h b/include/audioutils/mml_parser.h
new file mode 100644
index 000000000..e2018051f
--- /dev/null
+++ b/include/audioutils/mml_parser.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+ * apps/include/audioutils/mml_parser.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_MML_PARSER_MML_PARSER_H
+#define __APPS_INCLUDE_AUDIOUTILS_MML_PARSER_MML_PARSER_H
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MML_TYPE_EOF         (0)
+#define MML_TYPE_NOTE        (1)
+#define MML_TYPE_REST        (2)
+#define MML_TYPE_TEMPO       (3)
+#define MML_TYPE_LENGTH      (4)
+#define MML_TYPE_OCTAVE      (5)
+#define MML_TYPE_TUPLETSTART (6)
+#define MML_TYPE_TUPLETDONE  (7)
+#define MML_TYPE_VOLUME      (8)
+#define MML_TYPE_TONE        (9)
+#define MML_TYPE_CHORD       (10)
+
+#define MML_TYPE_NOTE_ERROR   (-MML_TYPE_NOTE)
+#define MML_TYPE_REST_ERROR   (-MML_TYPE_REST)
+#define MML_TYPE_TEMPO_ERROR  (-MML_TYPE_TEMPO)
+#define MML_TYPE_LENGTH_ERROR (-MML_TYPE_LENGTH)
+#define MML_TYPE_OCTAVE_ERROR (-MML_TYPE_OCTAVE)
+#define MML_TYPE_VOLUME_ERROR (-MML_TYPE_VOLUME)
+#define MML_TYPE_TUPLET_ERROR (-MML_TYPE_TUPLETSTART)
+#define MML_TYPE_TONE_ERROR   (-MML_TYPE_TONE)
+#define MML_TYPE_CHORD_ERROR  (-MML_TYPE_CHORD)
+
+#define MML_TYPE_ILLIGAL_COMPOSITION   (-100)
+#define MML_TYPE_ILLIGAL_TOOMANY_NOTES (-101)
+#define MML_TYPE_ILLIGAL_TOOFEW_NOTES  (-102)
+#define MML_TYPE_ILLIGAL_DOUBLE_TUPLET (-103)
+
+#define MML_STATE_NORMAL (0)
+#define MML_STATE_TUPLET (1)
+
+#define MAX_CHORD_NOTES  (5)
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+struct music_macro_lang_s
+{
+  int cur_tempo;
+  int cur_octave;
+  int def_length;
+  int fs;
+
+  int state;
+  int cur_tuplet;
+  int tuplet_notes;
+  int tuplet_length;
+};
+
+struct mml_result_s
+{
+  int note_idx[MAX_CHORD_NOTES];
+  int length;
+  int chord_notes;
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+int init_mml(FAR struct music_macro_lang_s *mml,
+             int fs, int tempo, int octave, int length);
+int parse_mml(FAR struct music_macro_lang_s *mml,
+              FAR char **score, FAR struct mml_result_s *result);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* __APPS_INCLUDE_AUDIOUTILS_MML_PARSER_MML_PARSER_H */