Complete.Org: Mailing Lists: Archives: freeciv-dev: April 2004:
[Freeciv-Dev] Re: (PR#8509) ALSA sound plugin for freeciv
Home

[Freeciv-Dev] Re: (PR#8509) ALSA sound plugin for freeciv

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [Freeciv-Dev] Re: (PR#8509) ALSA sound plugin for freeciv
From: "Javier Pello" <jpello@xxxxxxxxxxxxx>
Date: Wed, 14 Apr 2004 07:49:44 -0700
Reply-to: rt@xxxxxxxxxxx

<URL: http://rt.freeciv.org/Ticket/Display.html?id=8509 >

Hi again:

Sorry I did not attach the patch in the previous message.

--- freeciv-1.14.1/m4/alsa.m4   1970-01-01 01:00:00.000000000 +0100
+++ freeciv-alsa/m4/alsa.m4     2004-03-15 11:53:16.000000000 +0100
@@ -0,0 +1,37 @@
+dnl AM_ALSA_SUPPORT([ACTION-IF-SUPPORTS [, ACTION-IF-NOT-SUPPORTS]])
+dnl Partially stolen from alsaplayer
+dnl
+AC_DEFUN(AM_ALSA_SUPPORT,
+[dnl
+  AC_MSG_CHECKING(for ALSA version)
+  AC_EGREP_CPP([AP_maGiC_VALUE],
+  [
+#include <sys/asoundlib.h>
+#if defined(SND_LIB_MAJOR) && defined(SND_LIB_MINOR)
+#if SND_LIB_MAJOR>0 || (SND_LIB_MAJOR==0 && SND_LIB_MINOR>=6)
+AP_maGiC_VALUE
+#endif
+#endif
+  ],
+  AC_MSG_RESULT([>= 0.6])
+  AC_MSG_CHECKING(for Audio File Library version)
+  AC_EGREP_CPP([AP_maGiC_VALUE],
+  [
+#include <audiofile.h>
+#if defined(LIBAUDIOFILE_MAJOR_VERSION) && defined(LIBAUDIOFILE_MINOR_VERSION)
+#if LIBAUDIOFILE_MAJOR_VERSION>0 || (LIBAUDIOFILE_MAJOR_VERSION==0 && 
LIBAUDIOFILE_MINOR_VERSION>=2)
+AP_maGiC_VALUE
+#endif
+#endif
+  ],
+  AC_MSG_RESULT([>= 0.2])
+  ALSA_CFLAGS=""
+  ALSA_LIB="-laudiofile -lasound"
+  ifelse([$1], , :, [$1]),
+  AC_MSG_RESULT([no])
+  ifelse([$2], , :, [$2])
+  ),
+  AC_MSG_RESULT([no])
+  ifelse([$2], , :, [$2])
+  )
+])
--- freeciv-1.14.1/m4/sound.m4  2003-12-02 19:57:25.000000000 +0100
+++ freeciv-alsa/m4/sound.m4    2004-03-10 15:57:30.000000000 +0100
@@ -7,6 +7,10 @@
    [  --disable-sdl-mixer     Do not try to use the SDL mixer],
    USE_SOUND=no, USE_SOUND_SDL=yes)
 
+ AC_ARG_ENABLE(alsa,
+   [  --disable-alsa          Do not try to use ALSA],
+   USE_SOUND=no, USE_SOUND_ALSA=yes)
+
  AC_ARG_ENABLE(winmm,
    [  --disable-winmm         Do not try to use WinMM for sound],
    USE_SOUND=no, USE_SOUND_WINMM=yes)
@@ -48,6 +52,18 @@
   fi
  fi
 
+ if test "x$USE_SOUND_ALSA" = "xyes"; then
+  dnl Add ALSA support to client
+  AM_ALSA_SUPPORT(ALSA=yes, ALSA=no)
+  if test "x$ALSA" != "xno"; then
+    SOUND_CFLAGS="$SOUND_CFLAGS $ALSA_CFLAGS"
+    SOUND_LIBS="$SOUND_LIBS $ALSA_LIBS"
+    AC_DEFINE(ALSA, 1, [ALSA support])
+    AC_MSG_CHECKING(building ALSA support)
+    AC_MSG_RESULT(yes)
+  fi
+ fi
+
  if test "x$USE_SOUND_WINMM" = "xyes"; then
   dnl Add WinMM sound support to client
   if test x"$MINGW32" = "xyes"; then
--- freeciv-1.14.1/configure.ac 2003-12-02 19:56:54.000000000 +0100
+++ freeciv-alsa/configure.ac   2004-03-10 14:04:06.000000000 +0100
@@ -507,6 +507,7 @@
 AC_SUBST(SOUND_LIBS)
 AM_CONDITIONAL(ESD, test "x$ESD" = "xyes")
 AM_CONDITIONAL(SDL, test "x$SDL_mixer" = "xyes")
+AM_CONDITIONAL(ALSA, test "x$ALSA" = "xyes")
 AM_CONDITIONAL(WINMM, test "x$WINMM" = "xyes")
 AM_CONDITIONAL(CLIENT_GUI_GTK, test "$gui_sources" = "gui-gtk")
 AM_CONDITIONAL(CLIENT_GUI_GTK_2_0, test "$gui_sources" = "gui-gtk-2.0")
--- freeciv-1.14.1/client/Makefile.am   2003-12-02 19:56:55.000000000 +0100
+++ freeciv-alsa/client/Makefile.am     2004-03-10 13:07:27.000000000 +0100
@@ -25,6 +25,7 @@
 
 ALL_ESD_FILES=audio_esd.c audio_esd.h
 ALL_SDL_FILES=audio_sdl.c audio_sdl.h
+ALL_ALSA_FILES=audio_alsa.c audio_alsa.h
 ALL_WINMM_FILES=audio_winmm.c audio_winmm.h
 ALL_AMIGA_FILES=audio_amiga.c audio_amiga.h
 
@@ -34,6 +35,9 @@
 if SDL
 SDL_FILES=$(ALL_SDL_FILES)
 endif
+if ALSA
+ALSA_FILES=$(ALL_ALSA_FILES)
+endif
 if WINMM
 WINMM_FILES=$(ALL_WINMM_FILES)
 endif
@@ -104,6 +108,7 @@
                \
                $(ALL_ESD_FILES)                 \
                $(ALL_SDL_FILES)                 \
+               $(ALL_ALSA_FILES)                \
                $(ALL_WINMM_FILES)               \
                $(ALL_AMIGA_FILES)
 
@@ -126,7 +131,7 @@
 
 ## Above, note -I../intl instead of -I$(top_srdir/intl) is deliberate.
 
-civclient_SOURCES = $(ESD_FILES) $(SDL_FILES) $(WINMM_FILES) \
+civclient_SOURCES = $(ESD_FILES) $(SDL_FILES) $(ALSA_FILES) $(WINMM_FILES) \
        attribute.h     \
        attribute.c     \
        citydlg_common.c \
--- freeciv-1.14.1/client/audio.c       2003-12-02 19:56:55.000000000 +0100
+++ freeciv-alsa/client/audio.c 2004-03-10 13:08:56.000000000 +0100
@@ -38,6 +38,10 @@
 #include "audio_sdl.h"
 #endif
 
+#ifdef ALSA
+#include "audio_alsa.h"
+#endif
+
 #ifdef WINMM
 #include "audio_winmm.h"
 #endif
@@ -48,7 +52,7 @@
 
 #include "audio.h"
 
-#define MAX_NUM_PLUGINS                3
+#define MAX_NUM_PLUGINS                4
 #define SNDSPEC_SUFFIX         ".soundspec"
 
 /* keep it open throughout */
@@ -161,6 +165,9 @@
 #ifdef SDL
   audio_sdl_init();
 #endif
+#ifdef ALSA
+  audio_alsa_init();
+#endif
 #ifdef WINMM
   audio_winmm_init();
 #endif
@@ -280,6 +287,9 @@
 #ifdef SDL
   if (audio_select_plugin("sdl")) return; 
 #endif
+#ifdef ALSA
+  if (audio_select_plugin("alsa")) return;
+#endif
 #ifdef WINMM
   if (audio_select_plugin("winmm")) return;
 #endif
--- freeciv-1.14.1/client/audio_alsa.h  1970-01-01 01:00:00.000000000 +0100
+++ freeciv-alsa/client/audio_alsa.h    2004-03-10 11:35:45.000000000 +0100
@@ -0,0 +1,19 @@
+/********************************************************************** 
+ Freeciv - Copyright (C) 2004 - J. Pello
+   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, 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.
+***********************************************************************/
+
+#ifndef FC__AUDIO_ALSA_H
+#define FC__AUDIO_ALSA_H
+
+void audio_alsa_init (void);
+
+#endif  /* FC__AUDIO_ALSA_H */
--- freeciv-1.14.1/client/audio_alsa.c  1970-01-01 01:00:00.000000000 +0100
+++ freeciv-alsa/client/audio_alsa.c    2004-03-15 11:42:06.000000000 +0100
@@ -0,0 +1,358 @@
+/********************************************************************** 
+ Freeciv - Copyright (C) 2004 - J. Pello
+   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, 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.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+
+#include <audiofile.h>
+#include <alsa/asoundlib.h>
+
+#include "support.h"
+#include "log.h"
+#include "audio.h"
+
+#include "audio_alsa.h"
+
+static snd_pcm_t *sound_handle = NULL;
+static snd_async_handler_t *ah;
+static snd_pcm_sframes_t period_size;
+
+static AFfilehandle file_handle = AF_NULL_FILEHANDLE;
+static double file_rate;
+static AFframecount file_fcount, left_fcount;
+static bool file_repeat;
+
+/*****************
+ Drain if running
+*****************/
+static inline void snd_pcm_drain_if (snd_pcm_t *h)
+{
+  switch ( snd_pcm_state(h) )  {
+    case SND_PCM_STATE_RUNNING:
+    case SND_PCM_STATE_XRUN:
+    case SND_PCM_STATE_PAUSED:
+      snd_pcm_drain (h);
+    default: ;
+  }
+}
+
+/********************
+ Reset as appropiate
+********************/
+static inline void snd_pcm_drop_free (snd_pcm_t *h)
+{
+  file_repeat = FALSE;
+  switch ( snd_pcm_state(h) )  {
+    case SND_PCM_STATE_RUNNING:
+    case SND_PCM_STATE_XRUN:
+    case SND_PCM_STATE_DRAINING:
+    case SND_PCM_STATE_PAUSED:
+      snd_pcm_drop (h);     /* fall through */
+    case SND_PCM_STATE_PREPARED:
+    case SND_PCM_STATE_SETUP:
+      snd_pcm_hw_free (h);
+    default: ;
+  }
+}
+
+/**********
+ Shut down
+**********/
+static void my_shutdown (void)
+{
+  snd_pcm_close (sound_handle);
+  sound_handle = NULL;
+}
+
+/*****
+ Stop
+*****/
+static void my_stop (void)
+{
+  file_repeat = FALSE;
+  snd_pcm_drop_free (sound_handle);
+}
+
+/*****
+ Wait
+*****/
+static void my_wait (void)
+{
+  file_repeat = FALSE;
+  snd_pcm_drain_if (sound_handle);
+  while ( snd_pcm_state (sound_handle) == SND_PCM_STATE_DRAINING )  {
+    usleep (100000);
+  }
+}
+
+/******************
+ Set HW parameters
+******************/
+static int set_hw_params (void)
+{
+  snd_pcm_hw_params_t *hwparams;
+  unsigned rrate;
+  unsigned period_time = 100000;
+
+  snd_pcm_hw_params_alloca (&hwparams);
+
+  if ( snd_pcm_hw_params_any (sound_handle, hwparams) < 0 )  {
+    return -1;
+  }
+  if ( snd_pcm_hw_params_set_access (sound_handle, hwparams,
+      SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0 )  {
+    return -1;
+  }
+  if ( snd_pcm_hw_params_set_format (sound_handle, hwparams,
+      SND_PCM_FORMAT_S16) < 0 )  {
+    return -1;
+  }
+  if ( snd_pcm_hw_params_set_channels (sound_handle, hwparams, 2) < 0 )  {
+    return -1;
+  }
+  rrate = file_rate;
+  if ( snd_pcm_hw_params_set_rate_near (sound_handle, hwparams, &rrate, 0)
+      < 0 )  {
+    return -1;
+  }
+  if ( rrate != (unsigned) file_rate )  {
+    freelog (LOG_VERBOSE, "ALSA: asked for rate %u, got %u",
+      (unsigned) file_rate, rrate);
+  }
+  snd_pcm_hw_params_set_period_time_near (sound_handle, hwparams,
+    &period_time, &rrate);
+  if ( snd_pcm_hw_params_get_period_size (hwparams, &period_size, &rrate)
+      < 0 )  {
+    return -1;
+  }
+  return snd_pcm_hw_params (sound_handle, hwparams);
+}
+
+/******************
+ Underrun recovery
+******************/
+static int xrun_recovery (const char *s, int e)
+{
+  if ( e == -EPIPE )  {
+    e = snd_pcm_prepare (sound_handle);
+    if (e < 0)  {
+      freelog (LOG_ERROR, "ALSA: Cannot recover from underrun: %s",
+               snd_strerror(e));
+    } else {
+      return 0;
+    }
+  } else if ( e < 0 )  {
+    freelog (LOG_ERROR, "ALSA: %s: %s", s, snd_strerror(e));
+  }
+  return e;
+}
+
+/************************
+ Send a burst of samples
+************************/
+static int snd_mmap_run (void)
+{
+  snd_pcm_uframes_t left, frames, offset;
+  snd_pcm_sframes_t commit;
+  const snd_pcm_channel_area_t *area;
+
+  left = period_size;
+  if ( left > left_fcount )  {
+    left = left_fcount;
+  }
+
+  while ( left > 0 )  {
+    frames = left;
+    if ( xrun_recovery ("MMAP begin",
+        snd_pcm_mmap_begin (sound_handle, &area, &offset, &frames)) < 0 )  {
+      return -1;
+    }
+
+    /* Confirm that our parameters match what we expect for
+     * SND_PCM_ACCESS_MMAP_INTERLEAVED and SND_PCM_FORMAT_S16.
+     * Of course, we could adapt the code below, but it is easier to leave it
+     * this way as long as ALSA's input format matches audiofile's output.
+     */
+    if ( area[0].step != 32 || area[1].step != 32
+        || area[0].addr != area[1].addr )  {
+      snd_pcm_mmap_commit (sound_handle, offset, 0);
+      freelog (LOG_ERROR, "ALSA: unexpected area parameters");
+      return -1;
+    }
+
+    if ( afReadFrames (file_handle, AF_DEFAULT_TRACK,
+        area->addr + 4*offset, frames) < 0 )  {
+      snd_pcm_mmap_commit (sound_handle, offset, 0);
+      freelog (LOG_ERROR, "ALSA: cannot read frames");
+      break;
+    }
+
+    commit = snd_pcm_mmap_commit (sound_handle, offset, frames);
+    if ( commit < 0 || commit != frames )  {
+      if ( xrun_recovery ("MMAP commit", commit<0 ? commit : -EPIPE) < 0 ) {
+        return -1;
+      }
+    }
+    left_fcount -= frames;
+    left -= frames;
+  }
+
+  return 0;
+}
+
+/**************
+ Play callback
+**************/
+static void play_callback (snd_async_handler_t *ah)
+{
+  snd_pcm_state_t state;
+  snd_pcm_sframes_t avail;
+  int restart = 0;
+
+  while (1)  {
+    if ( !left_fcount )  {
+      if ( file_repeat == FALSE )  {
+        return;
+      }
+      /* rewind */
+      if ( afSeekFrame (file_handle, AF_DEFAULT_TRACK, 0) < 0 )  {
+        break;
+      }
+      left_fcount = file_fcount;
+    }
+
+    state = snd_pcm_state (sound_handle);
+    if ( state == SND_PCM_STATE_XRUN )  {
+      if ( xrun_recovery(NULL,-EPIPE) < 0 )  {
+        break;
+      }
+    }
+
+    avail =
+      xrun_recovery ("avail update", snd_pcm_avail_update (sound_handle));
+    if ( avail < 0 )  {
+      restart = 1;
+      continue;
+    }
+    if ( avail < period_size )  {
+      if ( !restart )  {
+        return;
+      }
+      restart = 0;
+      if ( xrun_recovery ("start", snd_pcm_start (sound_handle)) < 0 )  {
+        break;
+      }
+    }
+
+    if ( snd_mmap_run() < 0 )  {
+      break;
+    }
+  }
+
+  /* error path */
+  snd_pcm_drop_free (sound_handle);
+}
+
+/*****
+ Play
+*****/
+static bool my_play (const char *tag, const char *fullpath, bool repeat)
+{
+  AFfilehandle new_handle;
+  double new_rate;
+  AFframecount new_fcount;
+
+  if (fullpath == NULL)  {
+    return FALSE;
+  }
+
+  new_handle = afOpenFile (fullpath, "r", 0);
+  if ( new_handle == AF_NULL_FILEHANDLE )  {
+    freelog (LOG_ERROR, "ALSA: cannot open %s", fullpath);
+    return FALSE;
+  }
+
+  afSetVirtualSampleFormat (new_handle, AF_DEFAULT_TRACK,
+    AF_SAMPFMT_TWOSCOMP, 16);
+  afSetVirtualChannels (new_handle, AF_DEFAULT_TRACK, 2);
+  new_rate = afGetRate (new_handle, AF_DEFAULT_TRACK);
+  new_fcount = afGetFrameCount (new_handle, AF_DEFAULT_TRACK);
+  freelog (LOG_VERBOSE, "ALSA: %s: rate %f, %d frames", fullpath,
+    new_rate, (int) new_fcount);
+
+  snd_pcm_drop_free (sound_handle);
+  if ( file_handle != AF_NULL_FILEHANDLE )  {
+    afCloseFile (file_handle);
+  }
+
+  file_handle = new_handle;
+  file_rate = new_rate;
+  left_fcount = file_fcount = new_fcount;
+  file_repeat = repeat;
+
+  if ( set_hw_params() < 0 )  {
+    freelog (LOG_ERROR, "ALSA: bad format in %s", fullpath);
+    return FALSE;
+  }
+
+  if ( snd_mmap_run() < 0 || snd_mmap_run() < 0 )  {
+    return FALSE;
+  }
+
+  if ( xrun_recovery ("ALSA: start error", snd_pcm_start(sound_handle) )
+      < 0 )  {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/***********
+ Initialize
+***********/
+static bool my_init (void)
+{
+  if (snd_pcm_open (&sound_handle, "default", SND_PCM_STREAM_PLAYBACK, 0)
+      < 0)  {
+    return FALSE;
+  }
+
+  if ( snd_async_add_pcm_handler (&ah, sound_handle, play_callback, &ah)
+      < 0 )  {
+    freelog (LOG_ERROR, "ALSA: cannot set callback handler");
+    return  FALSE;
+  }
+
+  return TRUE;
+} 
+
+/**************************************************
+ Initialize
+ Called early during startup--no logging available
+**************************************************/
+void audio_alsa_init (void)
+{
+  struct audio_plugin self;
+
+  sz_strlcpy (self.name, "alsa");
+  sz_strlcpy (self.descr, "ALSA plugin");
+  self.init = my_init;
+  self.shutdown = my_shutdown;
+  self.stop = my_stop;
+  self.wait = my_wait;
+  self.play = my_play;
+  audio_add_plugin (&self);
+}

[Prev in Thread] Current Thread [Next in Thread]