This is an automated email from the ASF dual-hosted git repository.
xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
The following commit(s) were added to refs/heads/master by this push:
new b39228cd4 !examples/audio_rtttl: Implement example using RTTTL library
b39228cd4 is described below
commit b39228cd41ed882bbaa0e62cdacccd4afea93790
Author: Matteo Golin <[email protected]>
AuthorDate: Sat Jul 11 19:31:00 2026 -0400
!examples/audio_rtttl: Implement example using RTTTL library
This commit fixes the audio_rtttl example (which was undocumented and
previously used kernel-level functions) such that now it uses the RTTTL
parsing library. Contributors can implement 'players' for different
types of audio sinks. Currently, PWM is supported.
BREAKING: The previous version of this example only supported a single
board (Spresense) and uses public interfaces from the kernel code. Now
it only uses user-space interfaces and supports any board implementing
those interfaces. Users who relied on the old behaviour should submit a
patch to add a new player to this application which uses the desired
audio sink for the Spresense through user-space interfaces.
Signed-off-by: Matteo Golin <[email protected]>
---
examples/audio_rttl/CMakeLists.txt | 2 +-
examples/audio_rttl/Kconfig | 12 +-
examples/audio_rttl/Makefile | 2 +-
examples/audio_rttl/audio_rttl.cxx | 361 ----------------------------------
examples/audio_rttl/audio_rttl.h | 24 ---
examples/audio_rttl/audio_rttl_main.c | 305 ++++++++++++++++++++++++++++
include/.gitignore | 1 +
7 files changed, 319 insertions(+), 388 deletions(-)
diff --git a/examples/audio_rttl/CMakeLists.txt
b/examples/audio_rttl/CMakeLists.txt
index ec7709bd3..3553cfec6 100644
--- a/examples/audio_rttl/CMakeLists.txt
+++ b/examples/audio_rttl/CMakeLists.txt
@@ -29,5 +29,5 @@ if(CONFIG_EXAMPLES_AUDIO_SOUND)
STACKSIZE
${CONFIG_EXAMPLES_AUDIO_SOUND_STACKSIZE}
SRCS
- audio_rttl.cxx)
+ audio_rttl_main.c)
endif()
diff --git a/examples/audio_rttl/Kconfig b/examples/audio_rttl/Kconfig
index 2dae260b0..3a594e867 100644
--- a/examples/audio_rttl/Kconfig
+++ b/examples/audio_rttl/Kconfig
@@ -5,9 +5,11 @@
config EXAMPLES_AUDIO_SOUND
bool "Audio tone generator example (RTTL player)"
+ depends on AUDIO
+ depends on AUDIOUTILS_RTTTL_C
default n
---help---
- Enable the audio RTTL player example
+ Enable the audio RTTL player example.
if EXAMPLES_AUDIO_SOUND
@@ -23,4 +25,12 @@ config EXAMPLES_AUDIO_SOUND_STACKSIZE
int "Audio sound stack size"
default DEFAULT_TASK_STACKSIZE
+comment "Program options"
+
+config EXAMPLES_AUDIO_SOUND_MAXLEN
+ int "Maximum RTTL string length"
+ default 256
+ ---help---
+ The maximum length of an RTTL string that can be read from a
file.
+
endif
diff --git a/examples/audio_rttl/Makefile b/examples/audio_rttl/Makefile
index 1816513a7..b2a4630b3 100644
--- a/examples/audio_rttl/Makefile
+++ b/examples/audio_rttl/Makefile
@@ -30,6 +30,6 @@ STACKSIZE = $(CONFIG_EXAMPLES_AUDIO_SOUND_STACKSIZE)
# Audio Example
-MAINSRC = audio_rttl.cxx
+MAINSRC = audio_rttl_main.c
include $(APPDIR)/Application.mk
diff --git a/examples/audio_rttl/audio_rttl.cxx
b/examples/audio_rttl/audio_rttl.cxx
deleted file mode 100644
index 3533258c1..000000000
--- a/examples/audio_rttl/audio_rttl.cxx
+++ /dev/null
@@ -1,361 +0,0 @@
-/****************************************************************************
- * apps/examples/audio_rttl/audio_rttl.cxx
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership. The
- * ASF licenses this file to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the
- * License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- ****************************************************************************/
-
-/****************************************************************************
- * Included Files
- ****************************************************************************/
-
-#include <stdio.h>
-#include <math.h>
-#include <string.h>
-#include <arch/board/board.h>
-#include <arch/chip/audio.h>
-
-#include "audio_rttl.h"
-
-/****************************************************************************
- * Private Types
- ****************************************************************************/
-
-/* Note - note freq on octave 1 */
-
-#define C 16.35
-#define CD 17.32
-#define D 18.35
-#define DE 19.45
-#define E 20.60
-#define F 21.83
-#define FG 23.12
-#define G 24.50
-#define A 27.50
-#define AB 29.14
-#define B 30.84
-
-/* Octave - base freq multiplier for the base freq */
-
-#define OCT2 4
-#define OCT3 8
-#define OCT4 16
-#define OCT5 32
-#define OCT6 64
-#define OCT7 128
-#define OCT8 256
-
-/****************************************************************************
- * Public Functions
- ****************************************************************************/
-
-extern "C" int main(int argc, FAR char *argv[])
-{
- int freq;
- float base_freq;
- char temp_note;
- int octave;
- int duration;
- int note_duration;
- int base_duration;
- int bpm;
- int cnt;
-
- /* Set I/O parameters for power on. */
-
- if (!board_audio_power_control(true))
- {
- printf("Error: board_audio_power_control() failure.\n");
- return 1;
- }
-
- printf("Start Audio RTTL player\n");
-
- bpm = 0;
- octave = 0;
- base_duration = 0;
- note_duration = 0;
-
- while (*g_song != ':')
- {
- g_song++;
- }
-
- g_song++;
-
- if (*g_song == 'd')
- {
- g_song = g_song + 2;
- base_duration = *g_song - '0';
- g_song++;
- if ((*g_song >= '0') && (*g_song <= '9'))
- {
- base_duration = (base_duration * 10) + (*g_song - '0');
- g_song++;
- }
-
- g_song++;
- }
-
- if (*g_song == 'o')
- {
- g_song = g_song + 2;
- octave = *g_song - '0';
- g_song = g_song + 2;
- }
-
- if (octave == 2)
- {
- octave = OCT2;
- }
- else if (octave == 3)
- {
- octave = OCT3;
- }
- else if (octave == 4)
- {
- octave = OCT4;
- }
- else if (octave == 5)
- {
- octave = OCT5;
- }
- else if (octave == 6)
- {
- octave = OCT6;
- }
- else if (octave == 7)
- {
- octave = OCT7;
- }
- else if (octave == 8)
- {
- octave = OCT8;
- }
-
- if (*g_song == 'b')
- {
- g_song = g_song + 2;
- bpm = 0;
- for (cnt = 1; cnt <= 3; cnt++)
- {
- if (*g_song != ':')
- {
- bpm = bpm * 10 + (*g_song - '0');
- g_song++;
- }
-
- }
-
- g_song++;
- }
-
- do
- {
- if ((*g_song >= '0') && (*g_song <= '9'))
- {
- note_duration = *g_song - '0';
- g_song++;
- }
-
- if ((*g_song >= '0') && (*g_song <= '9'))
- {
- note_duration = (note_duration * 10) + (*g_song - '0');
- g_song++;
- }
-
- if (note_duration > 0)
- {
- duration = ((60 * 1000L / bpm) * 4) / note_duration;
- }
- else
- {
- duration = ((60 * 1000L / bpm) * 4) / base_duration;
- }
-
- base_freq = 0;
- temp_note = ' ';
- switch (*g_song)
- {
- case 'c':
- {
- temp_note = 'c';
- base_freq = C;
- break;
- }
-
- case 'd':
- {
- temp_note = 'd';
- base_freq = D;
- break;
- }
-
- case 'e':
- {
- temp_note = 'e';
- base_freq = E;
- break;
- }
-
- case 'f':
- {
- temp_note = 'f';
- base_freq = F;
- break;
- }
-
- case 'g':
- {
- temp_note = 'g';
- base_freq = G;
- break;
- }
-
- case 'a':
- {
- temp_note = 'a';
- base_freq = A;
- break;
- }
-
- case 'b':
- {
- temp_note = 'b';
- base_freq = B;
- break;
- }
-
- case 'p':
- {
- temp_note = 'p';
- break;
- }
-
- default:
- break;
- }
-
- g_song++;
-
- if (*g_song == '.')
- {
- duration += duration / 2;
- g_song++;
- }
-
- if (*g_song == '#')
- {
- if (temp_note == 'c')
- {
- base_freq = CD;
- }
-
- if (temp_note == 'd')
- {
- base_freq = DE;
- }
-
- if (temp_note == 'f')
- {
- base_freq = FG;
- }
-
- if (temp_note == 'a')
- {
- base_freq = AB;
- }
-
- g_song++;
- }
-
- if ((*g_song - '0') == '2')
- {
- octave = OCT2;
- }
-
- if ((*g_song - '0') == '3')
- {
- octave = OCT3;
- }
-
- if ((*g_song - '0') == '4')
- {
- octave = OCT4;
- }
-
- if ((*g_song - '0') == '5')
- {
- octave = OCT5;
- }
-
- if ((*g_song - '0') == '6')
- {
- octave = OCT6;
- }
-
- if ((*g_song - '0') == '7')
- {
- octave = OCT7;
- }
-
- if ((*g_song - '0') == '8')
- {
- octave = OCT8;
- }
-
- g_song++;
-
- if (*g_song == ',')
- {
- g_song++;
- }
-
- /* play the sound */
-
- freq = ceil(base_freq * octave);
-
- if (!board_audio_tone_generator(1, -40, freq))
- {
- break;
- }
-
- usleep(duration * 1000L);
- }
- while (*g_song);
-
- /* Sound off. */
-
- if (!board_audio_tone_generator(0, 0, 0))
- {
- printf("Error: board_audio_tone_generator() failure.\n");
- return 1;
- }
-
- /* Set I/O parameters for power off. */
-
- if (!board_audio_power_control(false))
- {
- printf("Error: board_audio_power_control() failure.\n");
- return 1;
- }
-
- printf("Done.\n");
-
- return 0;
-}
diff --git a/examples/audio_rttl/audio_rttl.h b/examples/audio_rttl/audio_rttl.h
deleted file mode 100644
index 41c5386d3..000000000
--- a/examples/audio_rttl/audio_rttl.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/****************************************************************************
- * apps/examples/audio_rttl/audio_rttl.h
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * 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.
- *
- ****************************************************************************/
-
-FAR char *g_song = (FAR char *)
- "Test:d=4,o=6,b=90:c,c#,d,d#,e,f,f#,g,a,a#,b,p,b,a#,a,g,f#,f,e,d#,d,c#,c";
diff --git a/examples/audio_rttl/audio_rttl_main.c
b/examples/audio_rttl/audio_rttl_main.c
new file mode 100644
index 000000000..5e9e7e960
--- /dev/null
+++ b/examples/audio_rttl/audio_rttl_main.c
@@ -0,0 +1,305 @@
+/****************************************************************************
+ * apps/examples/audio_rttl/audio_rttl_main.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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 <fcntl.h>
+#include <getopt.h>
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/ioctl.h>
+
+#ifdef CONFIG_PWM
+#include <nuttx/timers/pwm.h>
+#endif
+
+#include <audioutils/rtttl.h>
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct player_s
+{
+ void (*play)(struct rtttl_tone); /* Play RTTTL sound function */
+ void (*teardown)(void); /* Clean-up function */
+ void *arg; /* Private data for functions */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Default song if none was passed. Otherwise, used as buffer for reading
+ * from a file.
+ */
+
+static char g_default_song[CONFIG_EXAMPLES_AUDIO_SOUND_MAXLEN] =
+ "Test:d=4,o=6,b=90:c,c#,d,d#,e,f,f#,g,a,a#,b,p,b,a#,a,g,"
+ "f#,f,e,d#,d,c#,c";
+
+/* The selected player is a global variable so it can be accessed from within
+ * the player functions properly.
+ */
+
+static struct player_s g_player;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: play_default
+ *
+ * Description:
+ * The default player choice; prints an error and exits.
+ *
+ * Input Parameters:
+ * sound - The sound to play
+ *
+ ****************************************************************************/
+
+static void play_default(struct rtttl_tone sound)
+{
+ fprintf(stderr, "No player selected!\n");
+ return;
+}
+
+/****************************************************************************
+ * Name: teardown_default
+ *
+ * Description:
+ * Default tear-down function. Does nothing.
+ *
+ ****************************************************************************/
+
+static void teardown_default(void)
+{
+ /* No-op */
+
+ return;
+}
+
+#ifdef CONFIG_PWM
+
+/****************************************************************************
+ * Name: play_pwm
+ *
+ * Description:
+ * RTTTL player for audio sinks using PWM devices
+ *
+ * Input Parameters:
+ * sound - The sound to play
+ *
+ ****************************************************************************/
+
+static void play_pwm(struct rtttl_tone sound)
+{
+ int err;
+ int i;
+ struct pwm_info_s info;
+ int fd = *((int *)g_player.arg);
+
+ info.channels[0].channel = 1;
+ info.channels[0].duty = b16divi(uitoub16(50) - 1, 100); /* 50% duty cycle */
+ info.channels[0].cpol = PWM_CPOL_NDEF; /* Default */
+ info.channels[0].dcpol = PWM_DCPOL_NDEF; /* Default */
+ info.frequency = sound.frequency_100hz / 100;
+
+ /* Copy settings to all channels */
+
+ for (i = 1; i < CONFIG_PWM_NCHANNELS; i++)
+ {
+ info.channels[i] = info.channels[0];
+ info.channels[i].channel = i + 1;
+ }
+
+ err = ioctl(fd, PWMIOC_SETCHARACTERISTICS, &info);
+ if (err < 0)
+ {
+ fprintf(stderr, "Failed to set PWM characteristics: %d\n", errno);
+ return;
+ }
+
+ err = ioctl(fd, PWMIOC_START, 0);
+ if (err < 0)
+ {
+ fprintf(stderr, "Failed to start PWM: %d\n", errno);
+ return;
+ }
+
+ usleep(sound.duration_us); /* Wait */
+
+ err = ioctl(fd, PWMIOC_STOP, 0);
+ if (err < 0)
+ {
+ fprintf(stderr, "Failed to stop PWM: %d\n", errno);
+ }
+}
+
+/****************************************************************************
+ * Name: teardown_pwm
+ *
+ * Description:
+ * Teardown function for PWM players
+ *
+ ****************************************************************************/
+
+static void teardown_pwm(void)
+{
+ int fd = *((int *)g_player.arg);
+ close(fd);
+}
+#endif /* CONFIG_PWM */
+
+/****************************************************************************
+ * Name: print_usage
+ *
+ * Description:
+ * Prints out the usage information for the program.
+ *
+ * Input Parameters:
+ * sink - The stream to output the usage information
+ *
+ ****************************************************************************/
+
+static void print_usage(FILE *sink)
+{
+ fprintf(sink, "USAGE: audio_rttl <device path> [-s string] [-f file]\n");
+ fprintf(sink, "Ex: audio_rttl /dev/tone0\n");
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int main(int argc, FAR char *argv[])
+{
+ int err;
+ int fd;
+ int c;
+ FILE *fptr;
+ size_t bread;
+ const char *song = g_default_song;
+ const char *device = NULL;
+ const char *file = NULL;
+
+ g_player.play = play_default;
+ g_player.teardown = teardown_default;
+ g_player.arg = NULL;
+
+ while ((c = getopt(argc, argv, ":hs:f:")) != -1)
+ {
+ switch (c)
+ {
+ case 'h':
+ print_usage(stdout);
+ break;
+ case 's':
+ song = optarg;
+ break;
+ case 'f':
+ file = optarg;
+ break;
+ case '?':
+ fprintf(stderr, "Unknown option -%c\n", optopt);
+ print_usage(stderr);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (argc <= optind)
+ {
+ fprintf(stderr, "Missing argument for device path.\n");
+ return EXIT_FAILURE;
+ }
+
+ device = argv[optind++];
+
+ /* Based on the device path, we choose which player should be used and
+ * perform the setup.
+ */
+
+#ifdef CONFIG_PWM
+ if (strstr(device, "pwm"))
+ {
+ fd = open(device, O_RDWR);
+ if (fd < 0)
+ {
+ fprintf(stderr, "Failed to open %s: %d\n", device, errno);
+ return EXIT_FAILURE;
+ }
+ else
+ {
+ g_player.arg = &fd;
+ }
+
+ g_player.play = play_pwm;
+ g_player.teardown = teardown_pwm;
+ }
+#endif /* CONFIG_PWM */
+
+ /* If the user passed a file and a song string, use the string. Otherwise,
+ * read from the file
+ */
+
+ if (file != NULL && song == g_default_song)
+ {
+ fptr = fopen(file, "r");
+ if (fptr == NULL)
+ {
+ fprintf(stderr, "Couldn't open file '%s': %d", file, errno);
+ g_player.teardown();
+ return EXIT_FAILURE;
+ }
+
+ bread =
+ fread(g_default_song, sizeof(char), sizeof(g_default_song), fptr);
+
+ /* Ensure null termination of the string. */
+
+ if (bread <= sizeof(g_default_song))
+ {
+ g_default_song[bread - 1] = '\0';
+ }
+
+ fclose(fptr);
+ }
+
+ /* Play the song */
+
+ err = rtttl_play(song, g_player.play);
+ if (err != 0)
+ {
+ fprintf(stderr, "Failed to play song: %d\n", err);
+ return err;
+ }
+
+ /* Tear down the player and exit */
+
+ g_player.teardown();
+ return EXIT_SUCCESS;
+}
diff --git a/include/.gitignore b/include/.gitignore
index 9d6149edc..1c2896091 100644
--- a/include/.gitignore
+++ b/include/.gitignore
@@ -1,2 +1,3 @@
/netutils/cJSON.h
/netutils/cJSON_Utils.h
+/audioutils/rtttl.h