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

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

commit a5c0e9c188cb1f4a4feb7e812651e8505904a1da
Author: Matteo Golin <[email protected]>
AuthorDate: Tue Jul 21 13:04:04 2026 -0400

    apps/games/NXDoom: Add support for RTTTL DOOM theme
    
    Implements and RTTTL player music module to play the DOOM theme song
    over RTTTL.
    
    Signed-off-by: Matteo Golin <[email protected]>
---
 games/NXDoom/Kconfig             |  19 ++
 games/NXDoom/Makefile            |   6 +
 games/NXDoom/src/i_rtttl_music.c | 522 +++++++++++++++++++++++++++++++++++++++
 games/NXDoom/src/i_sound.c       |  22 +-
 games/NXDoom/src/i_sound.h       |   8 +-
 5 files changed, 567 insertions(+), 10 deletions(-)

diff --git a/games/NXDoom/Kconfig b/games/NXDoom/Kconfig
index cbf47a97a..5b82b7633 100644
--- a/games/NXDoom/Kconfig
+++ b/games/NXDoom/Kconfig
@@ -107,6 +107,25 @@ config GAMES_NXDOOM_SOUND
        ---help---
                Not ported! Will not work.
 
+if GAMES_NXDOOM_SOUND
+
+config GAMES_NXDOOM_RTTTL_MUSIC
+       bool "RTTTL music"
+       default n
+       depends on AUDIOUTILS_RTTTL_C
+       depends on !DISABLE_PTHREAD
+       ---help---
+               Play music using RTTTL.
+
+config GAMES_NXDOOM_RTTTL_DEVICE
+       string "RTTTL device"
+       default "/dev/pwm0"
+       depends on GAMES_NXDOOM_RTTTL_MUSIC
+       ---help---
+               Device for RTTTL output (only PWM currently supported).
+
+endif # GAMES_NXDOOM_SOUND
+
 comment "Graphics options"
 
 config GAMES_NXDOOM_FBPATH
diff --git a/games/NXDoom/Makefile b/games/NXDoom/Makefile
index 45df1a6de..b22390654 100644
--- a/games/NXDoom/Makefile
+++ b/games/NXDoom/Makefile
@@ -198,6 +198,12 @@ CSRCS += $(DOOMSRCDIR)/s_sound.c
 CSRCS += $(DOOMSRCDIR)/deh_sound.c
 endif
 
+# If we have chosen RTTTL music, then add that source
+
+ifeq ($(CONFIG_GAMES_NXDOOM_RTTTL_MUSIC),y)
+CSRCS += $(COMMONSRCDIR)/i_rtttl_music.c
+endif
+
 MAINSRC  = $(COMMONSRCDIR)/i_main.c
 
 include $(APPDIR)/Application.mk
diff --git a/games/NXDoom/src/i_rtttl_music.c b/games/NXDoom/src/i_rtttl_music.c
new file mode 100644
index 000000000..78dd7122c
--- /dev/null
+++ b/games/NXDoom/src/i_rtttl_music.c
@@ -0,0 +1,522 @@
+/****************************************************************************
+ * apps/games/NXDoom/src/i_rtttl_music.c
+ *
+ * SPDX-License-Identifier: GPLv2
+ *
+ * Copyright(C) 1993-1996 Id Software, Inc.
+ * Copyright(C) 2005-2014 Simon Howard
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * DESCRIPTION:
+ *  System interface for music.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include "i_glob.h"
+
+#include "config.h"
+#include "doomtype.h"
+#include "memio.h"
+#include "mus2mid.h"
+
+#include "deh_str.h"
+#include "gusconf.h"
+#include "i_sound.h"
+#include "i_swap.h"
+#include "i_system.h"
+#include "m_argv.h"
+#include "m_config.h"
+#include "m_misc.h"
+#include "sha1.h"
+#include "w_wad.h"
+#include "z_zone.h"
+
+#ifdef CONFIG_PWM
+#include <nuttx/timers/pwm.h>
+#endif
+
+#include <audioutils/rtttl.h>
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static boolean i_rtttl_init_music(void);
+static void i_rtttl_shutdown_music(void);
+static void i_rtttl_set_music_volume(int volume);
+static void i_rtttl_pause_song(void);
+static void i_rtttl_resume_song(void);
+static void *i_rtttl_register_song(void *data, int len);
+static void i_rtttl_unregister_song(void *handle);
+static void i_rtttl_play_song(void *handle, boolean looping);
+static void i_rtttl_stop_song(void);
+static boolean i_rtttl_musicisplaying(void);
+static void i_rtttl_pollmusic(void);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+const char g_doom_rtttl[] =
+  "Doom:d=32,o=5,b=56:f,f,f6,f,f,d#6,f,f,c#6,f,f,b,f,f,c6,c#6,f,f,f6,f,f,d#"
+  "6,f,f,c#6,f,f,8b.,f,f,f6,f,f,d#6,f,f,c#6,f,f,b,f,f,c6,c#6,f,f,f6,f,f,d#"
+  "6,f,f,c#6,f,f,8b.,a#,a#,a#6,a#,a#,g#6,a#,a#,f#6,a#,a#,e6,a#,a#,f6,f#6,a#"
+  ",a#,a#6,a#,a#,g#6,a#,a#,f#6,a#,a#,8e6";
+
+/* List of sound devices that this sound module is used for.
+ * It appears in i_sound.c that the default music device is 'SNDDEVICE_SB',
+ * so I have just put that here. In reality, we may either wish to add an
+ * additional sound device for RTTL, or put this as something else
+ * (PCSPEAKER?). I don't know what most of the snddevice_t enum options
+ * refer to.
+ */
+
+static const snddevice_t g_rtttl_sound_devices[] =
+{
+  SNDDEVICE_SB,
+};
+
+/* Player state */
+
+static void (*g_player)(struct rtttl_tone);
+static int g_fd = -1;
+static bool g_playing = false;
+static bool g_loop = true;
+
+/* Player thread state */
+
+static pthread_t g_thread;
+static pthread_mutex_t g_player_lock;
+static pthread_cond_t g_pausecond;
+static pthread_cond_t g_playingcond;
+static bool g_paused;
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+const music_module_t g_rtttl_music_module =
+{
+  .sound_devices = g_rtttl_sound_devices,
+  .num_sound_devices = arrlen(g_rtttl_sound_devices),
+  .init = i_rtttl_init_music,
+  .shutdown = i_rtttl_shutdown_music,
+  .set_music_volume = i_rtttl_set_music_volume,
+  .pause_music = i_rtttl_pause_song,
+  .resume_music = i_rtttl_resume_song,
+  .register_song = i_rtttl_register_song,
+  .unregister_song = i_rtttl_unregister_song,
+  .play_song = i_rtttl_play_song,
+  .stop_song = i_rtttl_stop_song,
+  .music_is_playing = i_rtttl_musicisplaying,
+  .poll = i_rtttl_pollmusic,
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: wait_while_paused
+ *
+ * Description:
+ *   Wait while the music is paused.
+ *
+ ****************************************************************************/
+
+static void wait_while_paused(void)
+{
+  pthread_mutex_lock(&g_player_lock);
+
+  while (g_paused)
+    {
+      pthread_cond_wait(&g_pausecond, &g_player_lock);
+    }
+
+  pthread_mutex_unlock(&g_player_lock);
+}
+
+/****************************************************************************
+ * Name: wait_while_stopped
+ *
+ * Description:
+ *   Wait until we are told that a song is started (i.e. while song stopped
+ *   and nothing is playing).
+ *
+ ****************************************************************************/
+
+static void wait_while_stopped(void)
+{
+  pthread_mutex_lock(&g_player_lock);
+
+  while (!g_playing)
+    {
+      pthread_cond_wait(&g_playingcond, &g_player_lock);
+    }
+
+  pthread_mutex_unlock(&g_player_lock);
+}
+
+/****************************************************************************
+ * Name: play_default
+ *
+ * Description:
+ *   Default player which does nothing.
+ *
+ * Input Parameters:
+ *   sound - The sound to play
+ *
+ ****************************************************************************/
+
+static void play_default(struct rtttl_tone sound)
+{
+  wait_while_paused();
+  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;
+
+  wait_while_paused(); /* Wait until unpaused */
+
+  /* If we are not meant to be playing (i.e. song was stopped), we should
+   * return from this function.
+   */
+
+  if (!g_playing)
+    {
+      return;
+    }
+
+  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(g_fd, PWMIOC_SETCHARACTERISTICS, &info);
+  if (err < 0)
+    {
+      fprintf(stderr, "Failed to set PWM characteristics: %d\n", errno);
+      return;
+    }
+
+  err = ioctl(g_fd, PWMIOC_START, 0);
+  if (err < 0)
+    {
+      fprintf(stderr, "Failed to start PWM: %d\n", errno);
+      return;
+    }
+
+  usleep(sound.duration_us); /* Wait */
+
+  err = ioctl(g_fd, PWMIOC_STOP, 0);
+  if (err < 0)
+    {
+      fprintf(stderr, "Failed to stop PWM: %d\n", errno);
+    }
+}
+#endif
+
+static void *player_thread(void *arg)
+{
+  for (; ; )
+    {
+      /* If there is nothing playing (i.e. song stopped or done), block here
+       * until the main DOOM player logic tells us it wants to play a song.
+       */
+
+      wait_while_stopped();
+
+      rtttl_play(g_doom_rtttl, g_player);
+
+      /* If we are not meant to loop, then after one play we will indicate
+       * that we are 'not playing'. This will causes the thread to block.
+       * Otherwise, this will continue forever.
+       */
+
+      if (!g_loop)
+        {
+          pthread_mutex_lock(&g_player_lock);
+          g_playing = false;
+          g_paused = false;
+          pthread_mutex_unlock(&g_player_lock);
+        }
+    }
+
+  return NULL;
+}
+
+/****************************************************************************
+ * Name: i_rtttl_init_music
+ *
+ * Description:
+ *   Choose the appropriate player implementation for RTTTl based on the
+ *   configured music device.
+ *
+ * Returned Value:
+ *   True on success, false otherwise.
+ *
+ ****************************************************************************/
+
+static boolean i_rtttl_init_music(void)
+{
+  int err;
+  g_player = play_default;
+#ifdef CONFIG_PWM
+  if (strstr(CONFIG_GAMES_NXDOOM_RTTTL_DEVICE, "pwm"))
+    {
+      g_player = play_pwm;
+    }
+#endif
+
+  /* Initialize thread state */
+
+  pthread_mutex_init(&g_player_lock, NULL);
+  pthread_cond_init(&g_pausecond, NULL);
+  pthread_cond_init(&g_playingcond, NULL);
+  g_paused = true;
+  g_loop = false;
+  g_playing = false;
+
+  /* Start the music playing thread */
+
+  err = pthread_create(&g_thread, NULL, player_thread, NULL);
+  if (err)
+    {
+      return false;
+    }
+
+  /* We only managed to match with the default player. */
+
+  if (g_player == play_default)
+    {
+      return true;
+    }
+
+  /* If we're not using the default player, we need to open a file
+   * descriptor.
+   */
+
+  g_fd = open(CONFIG_GAMES_NXDOOM_RTTTL_DEVICE, O_RDWR);
+  if (g_fd < 0)
+    {
+      fprintf(stderr,
+              "Couldn't open RTTL device '" CONFIG_GAMES_NXDOOM_RTTTL_DEVICE
+              "': %d",
+              errno);
+      return false;
+    }
+
+  return true;
+}
+
+/****************************************************************************
+ * Name: i_rtttl_shutdown_music
+ *
+ * Description:
+ *   Turns off the music and de-initializes the module.
+ *
+ ****************************************************************************/
+
+static void i_rtttl_shutdown_music(void)
+{
+  i_rtttl_stop_song();      /* Stop music */
+  pthread_cancel(g_thread); /* Kill thread */
+  close(g_fd);              /* Clean up device */
+}
+
+static void i_rtttl_set_music_volume(int volume)
+{
+  return; /* Currently not possible */
+}
+
+/****************************************************************************
+ * Name: i_rtttl_pause_song
+ *
+ * Description:
+ *   Pauses the currently playing song. Right now, this is implemented by
+ *   causing the RTTL `play` function to block on a condition variable.
+ *
+ ****************************************************************************/
+
+static void i_rtttl_pause_song(void)
+{
+  pthread_mutex_lock(&g_player_lock);
+  g_paused = true;
+  pthread_mutex_unlock(&g_player_lock);
+}
+
+/****************************************************************************
+ * Name: i_rtttl_resume_song
+ *
+ * Description:
+ *   Resume the currently paused song. Unblocks the RTTL `play` function by
+ *   signalling the condition variable.
+ *
+ ****************************************************************************/
+
+static void i_rtttl_resume_song(void)
+{
+  pthread_mutex_lock(&g_player_lock);
+  g_paused = false;
+  pthread_cond_signal(&g_pausecond);
+  pthread_mutex_unlock(&g_player_lock);
+}
+
+/****************************************************************************
+ * Name: i_rtttl_register_song
+ *
+ * Description:
+ *   Appears to be used to register song data from the DOOM lump. Right now,
+ *   we are using a statically stored RTTL string so we just ignore this. It
+ *   would be good to have RTTL data substituted for lump data in the future
+ *   so we can play the different songs provided by DOOM WADS.
+ *
+ * Input Parameters:
+ *   data - Song data
+ *   len - The length of the song data in bytes
+ *
+ * Returns: In theory, a handle representing the registered song. However, in
+ * practice, we return NULL to indicate this function isn't doing anything.
+ *
+ ****************************************************************************/
+
+static void *i_rtttl_register_song(void *data, int len)
+{
+  /* TODO: no idea what this would be for; right now we play only the theme */
+
+  return NULL;
+}
+
+/****************************************************************************
+ * Name: i_rtttl_unregister_song
+ *
+ * Description:
+ *   Unregisters one of the registered songs from `i_rtttl_register_song`.
+ *   Currently unimplemented since we register nothing.
+ *
+ * Input Parameters:
+ *   handle - The returned handle to the registered song
+ *
+ ****************************************************************************/
+
+static void i_rtttl_unregister_song(void *handle)
+{
+  /* TODO: no idea what this would be for */
+
+  return;
+}
+
+/****************************************************************************
+ * Name: i_rtttl_play_song
+ *
+ * Description:
+ *   Plays a previously registered song via the RTTTL library.
+ *
+ * Input Parameters:
+ *   handle - The handle to a previously registered song
+ *   looping - True if the song should loop, false otherwise
+ *
+ ****************************************************************************/
+
+static void i_rtttl_play_song(void *handle, boolean looping)
+{
+  pthread_mutex_lock(&g_player_lock);
+  g_playing = true;
+  g_paused = false;
+  g_loop = looping;
+  pthread_cond_signal(&g_playingcond);
+  pthread_mutex_unlock(&g_player_lock);
+}
+
+/****************************************************************************
+ * Name: i_rtttl_stop_song
+ *
+ * Description:
+ *   Stops playing the currently playing song (fully, not paused).
+ *
+ ****************************************************************************/
+
+static void i_rtttl_stop_song(void)
+{
+  pthread_mutex_lock(&g_player_lock);
+  g_playing = false;
+
+  /* Song not paused, allows play function to return early by also unblocking
+   * it. Then control is returned to the thread's main loop instead of the
+   * loop inside the RTTL library thread, where the thread can then block
+   * until a new song is played.
+   */
+
+  if (g_paused)
+    {
+      g_paused = false;
+      pthread_cond_signal(&g_pausecond);
+    }
+
+  g_loop = false;
+  pthread_mutex_unlock(&g_player_lock);
+}
+
+static boolean i_rtttl_musicisplaying(void)
+{
+  return g_playing;
+}
+
+static void i_rtttl_pollmusic(void)
+{
+  /* Unsure what this function does */
+
+  return;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
diff --git a/games/NXDoom/src/i_sound.c b/games/NXDoom/src/i_sound.c
index e86a7e65b..8c8cafaed 100644
--- a/games/NXDoom/src/i_sound.c
+++ b/games/NXDoom/src/i_sound.c
@@ -118,10 +118,13 @@ static const sound_module_t *sound_modules[] =
 
 /* Compiled-in music modules: */
 
-static const music_module_t *music_modules[] =
+static const music_module_t *g_music_modules[] =
 {
 #if 0
     &music_opl_module,
+#endif
+#ifdef CONFIG_GAMES_NXDOOM_RTTTL_MUSIC
+    &g_rtttl_music_module,
 #endif
     NULL,
 };
@@ -186,21 +189,21 @@ static void init_music_module(void)
 
   music_module = NULL;
 
-  for (i = 0; music_modules[i] != NULL; ++i)
+  for (i = 0; g_music_modules[i] != NULL; ++i)
     {
       /* Is the music device in the list of devices supported
        * by this module?
        */
 
       if (snd_device_in_list(snd_musicdevice,
-                             music_modules[i]->sound_devices,
-                             music_modules[i]->num_sound_devices))
+                             g_music_modules[i]->sound_devices,
+                             g_music_modules[i]->num_sound_devices))
         {
           /* initialize the module */
 
-          if (music_modules[i]->init())
+          if (g_music_modules[i]->init())
             {
-              music_module = music_modules[i];
+              music_module = g_music_modules[i];
               return;
             }
         }
@@ -239,7 +242,10 @@ static void check_volume_separation(int *vol, int *sep)
 
 void i_init_sound(gamemission_t mission)
 {
-  boolean nosound, nosfx, nomusic, nomusicpacks;
+  boolean nosound;
+  boolean nosfx;
+  boolean nomusic;
+  boolean nomusicpacks;
 
   /* @vanilla
    *
@@ -401,6 +407,7 @@ void i_precache_sounds(sfxinfo_t *sounds, int num_sounds)
 
 void i_init_music(void)
 {
+    printf("i_init_music: Music initialized\n");
 }
 
 void i_shutdown_music(void)
@@ -479,6 +486,7 @@ void i_unregister_song(void *handle)
 
 void i_play_song(void *handle, boolean looping)
 {
+    printf("i_play_song: Playing on %p\n", active_music_module);
   if (active_music_module != NULL)
     {
       active_music_module->play_song(handle, looping);
diff --git a/games/NXDoom/src/i_sound.h b/games/NXDoom/src/i_sound.h
index d1ad8d8eb..6827df992 100644
--- a/games/NXDoom/src/i_sound.h
+++ b/games/NXDoom/src/i_sound.h
@@ -266,11 +266,13 @@ extern int snd_pitchshift;
 extern int use_libsamplerate;
 extern float libsamplerate_scale;
 
-extern const sound_module_t sound_sdl_module;
 extern const sound_module_t sound_pcsound_module;
-extern const music_module_t music_sdl_module;
+
 extern const music_module_t music_pack_module;
-extern const music_module_t music_fl_module;
+
+#ifdef CONFIG_GAMES_NXDOOM_RTTTL_MUSIC
+extern const music_module_t g_rtttl_music_module;
+#endif
 
 /* For native music module: */
 

Reply via email to