You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by GitBox <gi...@apache.org> on 2022/05/04 07:34:29 UTC

[GitHub] [incubator-nuttx-apps] pkarashchenko commented on a diff in pull request #1158: audioutils/mml_parser: Add mml_parser library

pkarashchenko commented on code in PR #1158:
URL: https://github.com/apache/incubator-nuttx-apps/pull/1158#discussion_r864525167


##########
audioutils/mml_parser/mml_parser.c:
##########
@@ -0,0 +1,844 @@
+/****************************************************************************
+ * 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)

Review Comment:
   ```suggestion
   static int get_samples(FAR struct music_macro_lang_s *mml,
                          int samples, int num, int dots, bool plus_mode)
   ```



##########
audioutils/mml_parser/mml_parser.c:
##########
@@ -0,0 +1,844 @@
+/****************************************************************************
+ * 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, char **score)

Review Comment:
   ```suggestion
   static int sample_length(FAR struct music_macro_lang_s *mml,
                            FAR char **score)
   ```



##########
audioutils/mml_parser/mml_parser.c:
##########
@@ -0,0 +1,844 @@
+/****************************************************************************
+ * 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, 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, 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, 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, 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, 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,
+                      char **score, FAR struct mml_result_s *result)

Review Comment:
   ```suggestion
   static int handle_stoptuplet(FAR struct music_macro_lang_s *mml,
                                FAR char **score,
                                FAR struct mml_result_s *result)
   ```



##########
audioutils/mml_parser/mml_parser.c:
##########
@@ -0,0 +1,844 @@
+/****************************************************************************
+ * 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, 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, 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, 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, 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, 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,
+                      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,
+                      char **score, FAR struct mml_result_s *result)

Review Comment:
   ```suggestion
   static int handle_startchord(FAR struct music_macro_lang_s *mml,
                                FAR char **score,
                                FAR struct mml_result_s *result)
   ```



##########
examples/mml_parser/mml_parser_main.c:
##########
@@ -0,0 +1,197 @@
+/****************************************************************************
+ * 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, 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;
+
+  char *score;

Review Comment:
   ```suggestion
     FAR char *score;
   ```



##########
audioutils/mml_parser/mml_parser.c:
##########
@@ -0,0 +1,844 @@
+/****************************************************************************
+ * 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, 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, char **score,
+                       FAR struct mml_result_s *result)

Review Comment:
   ```suggestion
   static int handle_rest(FAR struct music_macro_lang_s *mml,
                          FAR char **score, FAR struct mml_result_s *result)
   ```



##########
audioutils/mml_parser/mml_parser.c:
##########
@@ -0,0 +1,844 @@
+/****************************************************************************
+ * 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, 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, 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, char **score,
+                       FAR struct mml_result_s *result)

Review Comment:
   ```suggestion
   static int handle_tempo(FAR struct music_macro_lang_s *mml,
                           FAR char **score, FAR struct mml_result_s *result)
   ```



##########
examples/mml_parser/mml_parser_main.c:
##########
@@ -0,0 +1,197 @@
+/****************************************************************************
+ * 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, struct mml_result_s *result)

Review Comment:
   ```suggestion
   int print_parse_result(int ret_code, FAR struct mml_result_s *result)
   ```



##########
audioutils/mml_parser/mml_parser.c:
##########
@@ -0,0 +1,844 @@
+/****************************************************************************
+ * 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, 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, 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, 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, char **score,
+                       FAR struct mml_result_s *result)

Review Comment:
   ```suggestion
   static int handle_length(FAR struct music_macro_lang_s *mml,
                            FAR char **score, FAR struct mml_result_s *result)
   ```



##########
audioutils/mml_parser/mml_parser.c:
##########
@@ -0,0 +1,844 @@
+/****************************************************************************
+ * 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, 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, 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, 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, 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, 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)

Review Comment:
   ```suggestion
   static int handle_starttuplet(FAR struct music_macro_lang_s *mml,
                                 FAR char **score,
                                 FAR struct mml_result_s *result)
   ```



##########
audioutils/mml_parser/mml_parser.c:
##########
@@ -0,0 +1,844 @@
+/****************************************************************************
+ * 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, 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, 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, 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, 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, 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,
+                      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)

Review Comment:
   ```suggestion
   static int handle_tupletnote(FAR struct music_macro_lang_s *mml, char code,
                                FAR char **score,
                                FAR struct mml_result_s *result)
   ```



##########
audioutils/mml_parser/mml_parser.c:
##########
@@ -0,0 +1,844 @@
+/****************************************************************************
+ * 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, 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, 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, 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, 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, char **score,
+                       FAR struct mml_result_s *result)

Review Comment:
   ```suggestion
   static int handle_volume(FAR struct music_macro_lang_s *mml,
                            FAR char **score, FAR struct mml_result_s *result)
   ```



##########
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__

Review Comment:
   ```suggestion
   #ifndef __APPS_INCLUDE_AUDIOUTILS_MML_PARSER_MML_PARSER_H
   #define __APPS_INCLUDE_AUDIOUTILS_MML_PARSER_MML_PARSER_H
   ```



##########
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);

Review Comment:
   ```suggestion
   int parse_mml(FAR struct music_macro_lang_s *mml,
                 FAR char **score, FAR struct mml_result_s *result);
   ```



##########
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);

Review Comment:
   ```suggestion
   int init_mml(FAR struct music_macro_lang_s *mml, int fs,
                int tempo, int octave, int length);
   ```



##########
audioutils/mml_parser/mml_parser.c:
##########
@@ -0,0 +1,844 @@
+/****************************************************************************
+ * 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, 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, 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, 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, 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, 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,
+                      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)

Review Comment:
   ```suggestion
   static int handle_notes(FAR struct music_macro_lang_s *mml, char code,
                           FAR char **score, FAR struct mml_result_s *result)
   ```



##########
audioutils/mml_parser/mml_parser.c:
##########
@@ -0,0 +1,844 @@
+/****************************************************************************
+ * 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, 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, 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, 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, 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, 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,
+                      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,
+                      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, 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)

Review Comment:
   ```suggestion
   int init_mml(FAR struct music_macro_lang_s *mml, int fs,
                int tempo, int octave, int length)
   ```



##########
audioutils/mml_parser/mml_parser.c:
##########
@@ -0,0 +1,844 @@
+/****************************************************************************
+ * 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, 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, 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, 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, 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, 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,
+                      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,
+                      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, char **score,
+                       FAR struct mml_result_s *result)

Review Comment:
   ```suggestion
   static int handle_tone(FAR struct music_macro_lang_s *mml,
                          FAR char **score, FAR struct mml_result_s *result)
   ```



##########
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__*/

Review Comment:
   ```suggestion
   #endif	/* __APPS_INCLUDE_AUDIOUTILS_MML_PARSER_MML_PARSER_H */
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org