Linux/FreeBSD/Mac OS X etc sound

Found a bug? Please report it, but remember to follow the bug reporting guidelines.
Missing a sane feature? Let us know!
But please do NOT request ports to other systems.

Moderator: ZSNES Mods

Best audio API

SDL
10
20%
AO
10
20%
OpenAL
14
29%
Semi Portable OSS
6
12%
Non Portable ALSA
5
10%
Non Portable CoreAudio
1
2%
Other
3
6%
 
Total votes: 49

Deathlike2
ZSNES Developer
ZSNES Developer
Posts: 6747
Joined: Tue Dec 28, 2004 6:47 am

Post by Deathlike2 »

Skiessi wrote:And remember the people with multiple soundcards, there must be the ability to choose the output card. I haven't heard anything with this SDL driver.
IIRC, you have to specifically configure that. That's not something ZSNES is supposed to deal with.
Continuing [url=http://slickproductions.org/forum/index.php?board=13.0]FF4[/url] Research...
Nach
ZSNES Developer
ZSNES Developer
Posts: 3904
Joined: Tue Jul 27, 2004 10:54 pm
Location: Solar powered park bench
Contact:

Post by Nach »

Okay, I'm back with a new approach which should hopefully be feasable! :D

Now I'm using libao, which can wrap to oss, alsa, arts, esd, nas, openal, sdl, directsound and a bunch of other things for all sorts of OSS including Solaris and Mac OS X specific audio libraries.

First you will need the libao development and shared libraries.

Then apply this patch to SVN:

Code: Select all

Index: linux/sdllink.c
===================================================================
--- linux/sdllink.c     (revision 4229)
+++ linux/sdllink.c     (working copy)
@@ -23,6 +23,8 @@
 #include "sw_draw.h"
 #include "gl_draw.h"

+#include <ao/ao.h>
+
 #include <SDL_thread.h>

 #include <sys/time.h>
@@ -52,7 +54,7 @@
 typedef enum vidstate_e { vid_null, vid_none, vid_soft, vid_gl } vidstate_t;

 // SOUND RELATED VARIABLES
-SDL_AudioSpec audiospec;
+//SDL_AudioSpec audiospec;
 int SoundEnabled = 1;
 BYTE PrevStereoSound;
 DWORD PrevSoundQuality;
@@ -725,8 +727,126 @@
   }
 }

+typedef unsigned long long uint64;
+#define SAMPLE_NTSC_HI_SCALE 995ULL
+#define SAMPLE_NTSC_LO 59649ULL
+#define SAMPLE_PAL_HI_SCALE  1ULL
+#define SAMPLE_PAL_LO       50ULL
+uint64 sample_hi;
+uint64 sample_lo;
+uint64 sample_balance;
+
+static const int freqtab[7] = { 8000, 11025, 22050, 44100, 16000, 32000, 48000 };
+#define RATE freqtab[SoundQuality]
+
+static ao_device *device;
+
 int InitSound(void)
 {
+  if (!SoundEnabled)
+  {
+    return FALSE;
+  }
+
+  PrevSoundQuality = SoundQuality;
+  PrevStereoSound = StereoSound;
+
+  ao_initialize();
+
+  int driver_count;
+  ao_info **driver_info = ao_driver_info_list(&driver_count);
+  puts("Valid Audio Drivers:");
+  while (driver_count--)
+  {
+    if (driver_info[driver_count]->type == AO_TYPE_LIVE)
+    {
+      puts(driver_info[driver_count]->short_name);
+    }
+  }
+
+  int driver_id = ao_default_driver_id();
+  //driver_id = ao_driver_id("oss");
+
+  ao_sample_format driver_format;
+  driver_format.bits = 16;
+  driver_format.channels = StereoSound+1;
+  driver_format.rate = freqtab[SoundQuality = ((SoundQuality > 6) ? 1 : SoundQuality)];
+  driver_format.byte_format = AO_FMT_LITTLE;
+
+  device = ao_open_live(driver_id, &driver_format, 0);
+  if (device)
+  {
+    printf("Audio Opened.\nChannels: %u Rate: %u\n", driver_format.channels, driver_format.rate);
+  }
+  else
+  {
+    puts("Audio Open Failed");
+  }
+
+  if (romispal)
+  {
+    sample_hi = SAMPLE_PAL_HI_SCALE*RATE;
+    sample_lo = SAMPLE_PAL_LO;
+  }
+  else
+  {
+    sample_hi = SAMPLE_NTSC_HI_SCALE*RATE;
+    sample_lo = SAMPLE_NTSC_LO;
+  }
+  sample_balance = sample_hi;
+
+  return TRUE;
+}
+
+short stemp[1280];
+void WriteSamples(unsigned int samples)
+{
+  //extern unsigned char soundon, DSPDisable;
+  extern unsigned int BufferSizeB, BufferSizeW;
+  void ProcessSoundBuffer();
+  extern int DSPBuffer[1280];
+
+  int *d = 0;
+  short *p = 0;
+
+  if (samples > 1280)
+  {
+    WriteSamples(1280);
+    samples -= 1280;
+  }
+
+  //printf("samples %d\n", samples);
+
+  BufferSizeB = samples;
+  BufferSizeW = samples<<1;
+
+  asm_call(ProcessSoundBuffer);
+
+  d = DSPBuffer;
+  p = stemp;
+
+  for (; d < DSPBuffer+samples; d++, p++)
+  {
+    if ((unsigned int)(*d + 0x8000) <= 0xFFFF) { *p = *d; continue; }
+    if (*d > 0x7FFF) { *p = 0x7FFF; }
+    else { *p = 0x8000; }
+  }
+
+  ao_play(device, (char *)stemp, samples*2);
+}
+
+void WriteAudio()
+{
+  unsigned int samples = (unsigned int)((sample_balance/sample_lo) << StereoSound);
+  sample_balance %= sample_lo;
+  sample_balance += sample_hi;
+
+  WriteSamples(samples);
+}
+
+/*
+int InitSound(void)
+{
   SDL_AudioSpec wanted;
   const int samptab[7] = { 1, 1, 2, 4, 2, 4, 4 };
   const int freqtab[7] = { 8000, 11025, 22050, 44100, 16000, 32000, 48000 };
@@ -776,11 +896,12 @@
   SDL_PauseAudio(0);

   Buffer_len = (audiospec.size * 2);
-  Buffer_len = (Buffer_len + 255) & ~255; /* Align to SPCSize */
+  Buffer_len = (Buffer_len + 255) & ~255; // Align to SPCSize
   Buffer = malloc(Buffer_len);

   return TRUE;
 }
+*/

 int ReInitSound(void)
 {
@@ -1199,6 +1320,7 @@
   }
 }

+/*
 //Why in the world did someone make this use signed values??? -Nach
 void UpdateSound(void *userdata, Uint8 * stream, int len)
 {
@@ -1225,6 +1347,7 @@
     Buffer_fill -= len;
   }
 }
+*/

 void sem_sleep(void)
 {
@@ -1275,7 +1398,7 @@

 void UpdateVFrame(void)
 {
-  extern unsigned char DSPDisable;
+  //extern unsigned char DSPDisable;
   extern unsigned int BufferSizeB, BufferSizeW;

   //Quick fix for GUI CPU usage
@@ -1284,11 +1407,12 @@
   CheckTimers();
   Main_Proc();

-  /* Process sound */
+/*
+  // Process sound
   BufferSizeB = 256;
   BufferSizeW = BufferSizeB+BufferSizeB;

-  /* take care of the things we left behind last time */
+  // take care of the things we left behind last time
   SDL_LockAudio();
   while (Buffer_fill < Buffer_len)
   {
@@ -1317,6 +1441,7 @@
     if (Buffer_tail >= Buffer_len) { Buffer_tail = 0; }
   }
   SDL_UnlockAudio();
+  */
 }

 void clearwin()
@@ -1344,6 +1469,11 @@
   else
 #endif
     sw_drawwin();
+
+  if (!GUIOn2 && !GUIOn && !EMUPause)
+  {
+    WriteAudio();
+  }
 }

 void UnloadSDL()
Add -lao to the link step, then compile it.

When running ZSNES, you should see something like this:

Code: Select all

Valid Audio Drivers:
null
nas
oss
alsa09
esd
arts
By default I let libao pick what it thinks is best. However look in linux/sdllink.c on line 768 for:

Code: Select all

//driver_id = ao_driver_id("oss");
Uncomment it out, and change oss to any of the valid ones listed that you want to try. I don't get audio from some of them, and your list will probably differ from mine.

Let me know if with this, one of the modes give you good audio.

If people are happy with this, I will clean this code up (it is extremely messy, doesn't unload properly, no easy use selectable interface), and commit it.

Please let me know how this works for you and which OSS and sound system you used. Detailed results are appreciated.
May 9 2007 - NSRT 3.4, now with lots of hashing and even more accurate information! Go download it.
_____________
Insane Coding
Aerdan
Winter Knight
Posts: 467
Joined: Mon Aug 16, 2004 10:16 pm
Contact:

Post by Aerdan »

It works fine here with ALSA, but I got a few underrun messages with ALSA; it didn't seem to affect ingame sound much, so I'm not too worried.
Skiessi
Rookie
Posts: 14
Joined: Tue Dec 05, 2006 1:31 pm
Location: Finland, Rovaniemi
Contact:

Post by Skiessi »

How do I add -lao to the link step?
Nach
ZSNES Developer
ZSNES Developer
Posts: 3904
Joined: Tue Jul 27, 2004 10:54 pm
Location: Solar powered park bench
Contact:

Post by Nach »

Skiessi wrote:How do I add -lao to the link step?
You edit the Makefile and put -lao next to -lSDL.
May 9 2007 - NSRT 3.4, now with lots of hashing and even more accurate information! Go download it.
_____________
Insane Coding
Nach
ZSNES Developer
ZSNES Developer
Posts: 3904
Joined: Tue Jul 27, 2004 10:54 pm
Location: Solar powered park bench
Contact:

Post by Nach »

Okay take 2, now I have the audio threaded, this should fix fast forward and some occasional lag issues. I notice with this my CPU usage dropped way down. Also some unloading is done.

Code: Select all

Index: Makefile.in
===================================================================
--- Makefile.in	(revision 4270)
+++ Makefile.in	(working copy)
@@ -99,7 +99,7 @@
 default: main
 all: main tools
 main: makefile.dep $(Z_OBJS)
-	@ZC@ -o @ZSNESEXE@ $(Z_OBJS) @ZCFLAGS@ @LDFLAGS@
+	@ZC@ -o @ZSNESEXE@ $(Z_OBJS) @ZCFLAGS@ @LDFLAGS@ -lao
 	rm -f version.o
 
 $(PSR): parsegen.cpp
Index: linux/sdllink.c
===================================================================
--- linux/sdllink.c	(revision 4270)
+++ linux/sdllink.c	(working copy)
@@ -23,8 +23,10 @@
 #include "sw_draw.h"
 #include "gl_draw.h"
 
+#include <ao/ao.h>
+
 #include <SDL_thread.h>
-
+#include <pthread.h>
 #include <sys/time.h>
 #include <time.h>
 #include <dirent.h>
@@ -52,15 +54,15 @@
 typedef enum vidstate_e { vid_null, vid_none, vid_soft, vid_gl } vidstate_t;
 
 // SOUND RELATED VARIABLES
-SDL_AudioSpec audiospec;
+//SDL_AudioSpec audiospec;
 int SoundEnabled = 1;
 BYTE PrevStereoSound;
 DWORD PrevSoundQuality;
 Uint8 *Buffer = NULL;
-int Buffer_len = 0, Buffer_fill = 0;
-int Buffer_head = 0, Buffer_tail = 0;
+//int Buffer_len = 0, Buffer_fill = 0;
+//int Buffer_head = 0, Buffer_tail = 0;
 
-extern int DSPBuffer[];
+//extern int DSPBuffer[];
 
 /* VIDEO VARIABLES */
 SDL_Surface *surface;
@@ -725,63 +727,156 @@
   }
 }
 
-int InitSound(void)
+typedef unsigned long long uint64;
+#define SAMPLE_NTSC_HI_SCALE 995ULL
+#define SAMPLE_NTSC_LO 59649ULL
+#define SAMPLE_PAL_HI_SCALE  1ULL
+#define SAMPLE_PAL_LO       50ULL
+uint64 sample_hi;
+uint64 sample_lo;
+uint64 sample_balance;
+
+static const int freqtab[7] = { 8000, 11025, 22050, 44100, 16000, 32000, 48000 };
+#define RATE freqtab[SoundQuality]
+
+static ao_device *device;
+
+void WriteSamples(unsigned int samples)
 {
-  SDL_AudioSpec wanted;
-  const int samptab[7] = { 1, 1, 2, 4, 2, 4, 4 };
-  const int freqtab[7] = { 8000, 11025, 22050, 44100, 16000, 32000, 48000 };
+  extern unsigned int BufferSizeB, BufferSizeW;
+  extern int DSPBuffer[1280];
+  void ProcessSoundBuffer();
+  short stemp[1280];
 
-  SDL_CloseAudio();
+  int *d = 0;
+  short *p = 0;
 
+  if (samples > 1280)
+  {
+    WriteSamples(1280);
+    samples -= 1280;
+  }
+
+  //printf("samples %d\n", samples);
+
+  BufferSizeB = samples;
+  BufferSizeW = samples<<1;
+
+  asm_call(ProcessSoundBuffer);
+
+  d = DSPBuffer;
+  p = stemp;
+
+  for (; d < DSPBuffer+samples; d++, p++)
+  {
+    if ((unsigned int)(*d + 0x8000) <= 0xFFFF) { *p = *d; continue; }
+    if (*d > 0x7FFF) { *p = 0x7FFF; }
+    else { *p = 0x8000; }
+  }
+
+  ao_play(device, (char *)stemp, samples*2);
+}
+
+static unsigned int samples_waiting = 0;
+
+void *AudioThread(void *useless)
+{
+  for (;;)
+  {
+    if (samples_waiting)
+    {
+      int samples = samples_waiting;
+      samples_waiting = 0;
+      WriteSamples(samples);
+    }
+    else
+    {
+      usleep(20);
+    }
+  }
+  return(0);
+}
+
+pthread_t audio_thread;
+
+int InitSound(void)
+{
   if (!SoundEnabled)
   {
     return FALSE;
   }
 
-  if (Buffer)
-    free(Buffer);
-  Buffer = NULL;
-  Buffer_len = 0;
-
   PrevSoundQuality = SoundQuality;
   PrevStereoSound = StereoSound;
 
-  if (SoundQuality > 6)
-    SoundQuality = 1;
-  wanted.freq = freqtab[SoundQuality];
+  ao_initialize();
 
-  if (StereoSound)
+  int driver_count;
+  ao_info **driver_info = ao_driver_info_list(&driver_count);
+  puts("Valid Audio Drivers:");
+  while (driver_count--)
   {
-    wanted.channels = 2;
+    if (driver_info[driver_count]->type == AO_TYPE_LIVE)
+    {
+      puts(driver_info[driver_count]->short_name);
+    }
   }
+
+  int driver_id = ao_default_driver_id();
+  //driver_id = ao_driver_id("oss");
+
+  ao_sample_format driver_format;
+  driver_format.bits = 16;
+  driver_format.channels = StereoSound+1;
+  driver_format.rate = freqtab[SoundQuality = ((SoundQuality > 6) ? 1 : SoundQuality)];
+  driver_format.byte_format = AO_FMT_LITTLE;
+
+  if (device)
+  {
+    ao_close(device);
+  }
   else
   {
-    wanted.channels = 1;
+    pthread_create(&audio_thread, 0, AudioThread, 0);
   }
 
-  wanted.samples = samptab[SoundQuality] * 128 * wanted.channels;
+  device = ao_open_live(driver_id, &driver_format, 0);
+  if (device)
+  {
+    printf("Audio Opened.\nChannels: %u Rate: %u\n", driver_format.channels, driver_format.rate);
+  }
+  else
+  {
+    puts("Audio Open Failed");
+  }
 
-  wanted.format = AUDIO_S16LSB;
-  wanted.userdata = NULL;
-  wanted.callback = UpdateSound;
-
-  if (SDL_OpenAudio(&wanted, &audiospec) < 0)
+  if (romispal)
   {
-    fprintf(stderr, "Sound init failed!\n");
-    fprintf(stderr, "freq: %d, channels: %d, samples: %d\n",
-      wanted.freq, wanted.channels, wanted.samples);
-    SoundEnabled = 0;
-    return FALSE;
+    sample_hi = SAMPLE_PAL_HI_SCALE*RATE;
+    sample_lo = SAMPLE_PAL_LO;
   }
-  SDL_PauseAudio(0);
+  else
+  {
+    sample_hi = SAMPLE_NTSC_HI_SCALE*RATE;
+    sample_lo = SAMPLE_NTSC_LO;
+  }
+  sample_balance = sample_hi;
 
-  Buffer_len = (audiospec.size * 2);
-  Buffer_len = (Buffer_len + 255) & ~255; /* Align to SPCSize */
-  Buffer = malloc(Buffer_len);
-
   return TRUE;
 }
 
+void WriteAudio()
+{
+  unsigned int samples = (unsigned int)((sample_balance/sample_lo) << StereoSound);
+  sample_balance %= sample_lo;
+  sample_balance += sample_hi;
+
+  if (!samples_waiting)
+  {
+    samples_waiting = samples;
+  }
+}
+
 int ReInitSound(void)
 {
   return InitSound();
@@ -1199,6 +1294,7 @@
   }
 }
 
+/*
 //Why in the world did someone make this use signed values??? -Nach
 void UpdateSound(void *userdata, Uint8 * stream, int len)
 {
@@ -1225,6 +1321,7 @@
     Buffer_fill -= len;
   }
 }
+*/
 
 void sem_sleep(void)
 {
@@ -1275,7 +1372,7 @@
 
 void UpdateVFrame(void)
 {
-  extern unsigned char DSPDisable;
+  //extern unsigned char DSPDisable;
   extern unsigned int BufferSizeB, BufferSizeW;
 
   //Quick fix for GUI CPU usage
@@ -1284,11 +1381,19 @@
   CheckTimers();
   Main_Proc();
 
-  /* Process sound */
+/*
+  if (!GUIOn2 && !GUIOn && !EMUPause)
+  {
+    WriteAudio();
+  }
+*/
+
+/*
+  // Process sound
   BufferSizeB = 256;
   BufferSizeW = BufferSizeB+BufferSizeB;
 
-  /* take care of the things we left behind last time */
+  // take care of the things we left behind last time
   SDL_LockAudio();
   while (Buffer_fill < Buffer_len)
   {
@@ -1317,6 +1422,7 @@
     if (Buffer_tail >= Buffer_len) { Buffer_tail = 0; }
   }
   SDL_UnlockAudio();
+  */
 }
 
 void clearwin()
@@ -1344,10 +1450,18 @@
   else
 #endif
     sw_drawwin();
+
+  if (!GUIOn2 && !GUIOn && !EMUPause)
+  {
+    WriteAudio();
+  }
+
 }
 
 void UnloadSDL()
 {
+  if (device) { ao_close(device); }
+  ao_shutdown();
   sem_sleep_die(); // Shutdown semaphore
   if (Buffer) { free(Buffer); }
   if (sdl_state == vid_soft) { sw_end(); }
May 9 2007 - NSRT 3.4, now with lots of hashing and even more accurate information! Go download it.
_____________
Insane Coding
Nach
ZSNES Developer
ZSNES Developer
Posts: 3904
Joined: Tue Jul 27, 2004 10:54 pm
Location: Solar powered park bench
Contact:

Post by Nach »

3rd times the charm! Apply against latest SVN.

Should now work for all sorts of cases, fast fowarding is fast, CPU usage is low, command line loading, NTSC and PAL. You can see driver list with zsnes --help, you can select which driver to use via command line or config file, stuff is unloaded right.

Make sure you have libao and libao-dev installed. Please play with the various available sound drivers.

Please post results you're having with the various drivers, list your CPU, RAM, OS, and Sound Card.

If you're using 64 bit, you'll need libao 32 bit drivers installed. I notice for me, that I have my 32 bit drivers going in /var/chroot/sid-ia32/usr/lib/, with 64 bit drivers in /usr/lib. Now I'm not sure if you can edit a config file somewhere, but my libao seems to be hardcoded to find it's drivers in /lib/ao/plugins-2/. To get around this problem, I symlinked everything in /var/chroot/sid-ia32/usr/lib/ao/plugins-2/ into /usr/lib/ao/plugins-2/.
For example:

Code: Select all

ln -s /var/chroot/sid-ia32/usr/lib/ao/plugins-2/liboss.so /usr/lib/ao/plugins-2/liboss32.so
And repeat for all drivers you have that you want to use.

And without further rambling, the patch:

Code: Select all

Index: zloader.c
===================================================================
--- zloader.c	(revision 4274)
+++ zloader.c	(working copy)
@@ -21,6 +21,7 @@
 
 #ifdef __UNIXSDL__
 #include "gblhdr.h"
+#include <ao/ao.h>
 #else
 #define _POSIX_
 #include <stdio.h>
@@ -73,6 +74,11 @@
 {
   size_t lines_out = 0;
   bool tty = isatty(fileno(stdout));
+#ifdef __UNIXSDL__
+  int driver_count;
+  ao_info **driver_info;
+  char line[75];
+#endif
 
   put_line("Usage : zsnes [-d,-f #, ... ] <filename.sfc>");
   put_line("   Eg : zsnes -s -r 2 game.sfc");
@@ -97,6 +103,20 @@
 #ifdef __WIN32__
   put_line("  -6 #    Force a user-specified refresh rate for fullscreen modes [50..180]");
 #endif
+#ifdef __UNIXSDL__
+  put_line("  -ad <>  Select Audio Driver :");
+  snprintf(line, sizeof(line), "%22s = automatically select", "auto");
+  driver_info = ao_driver_info_list(&driver_count);
+  put_line(line);
+  while (driver_count--)
+  {
+    if (driver_info[driver_count]->type == AO_TYPE_LIVE)
+    {
+      snprintf(line, sizeof(line), "%22s = %s", driver_info[driver_count]->short_name, driver_info[driver_count]->name);
+      put_line(line);
+    }
+  }
+#endif
 #ifdef __MSDOS__
   put_line("  -8      Force 8-bit sound");
   put_line("  -c      Enable full/wide screen (when available)");
@@ -714,6 +734,22 @@
           DSPDisable = 1;
         }
 
+        #ifdef __UNIXSDL__
+        else if (tolower(argv[i][1]) == 'a' && tolower(argv[i][2]) == 'd') //Disable sound DSP emulation
+        {
+          i++;
+          if (!strcmp(argv[i], "auto") || (ao_driver_id(argv[i]) >= 0))
+          {
+            strcpy(libAoDriver, argv[i]);
+          }
+          else
+          {
+            puts("Audio driver selection invalid.");
+            exit(1);
+          }
+        }
+        #endif
+
         else if (tolower(argv[i][1]) == 'd' && tolower(argv[i][2]) == 's') //Disable sound output
         {
           soundon = 0;
@@ -899,6 +935,9 @@
 {
   if (init_paths(*zargv))
   {
+    #ifdef __UNIXSDL__
+    ao_initialize();
+    #endif
     handle_params(zargc, zargv);
 
     atexit(ZCleanup);
Index: Makefile.in
===================================================================
--- Makefile.in	(revision 4274)
+++ Makefile.in	(working copy)
@@ -99,7 +99,7 @@
 default: main
 all: main tools
 main: makefile.dep $(Z_OBJS)
-	@ZC@ -o @ZSNESEXE@ $(Z_OBJS) @ZCFLAGS@ @LDFLAGS@
+	@ZC@ -o @ZSNESEXE@ $(Z_OBJS) @ZCFLAGS@ @LDFLAGS@ -lao
 	rm -f version.o
 
 $(PSR): parsegen.cpp
Index: initc.c
===================================================================
--- initc.c	(revision 4274)
+++ initc.c	(working copy)
@@ -2139,6 +2139,10 @@
 
 void SetupROM()
 {
+  #ifdef __UNIXSDL__
+  void init_sample_control();
+  #endif
+
   static bool CLforce = false;
   unsigned char *ROM = (unsigned char *)romdata;
 
@@ -2175,6 +2179,10 @@
       romispal = ((!BSEnable) && (ROM[infoloc+CountryOffset] > 1) && (ROM[infoloc+CountryOffset] < 0xD));
   }
 
+  #ifdef __UNIXSDL__
+  init_sample_control();
+  #endif
+
   if (romispal)
   {
     totlines = 314;
Index: linux/sdllink.c
===================================================================
--- linux/sdllink.c	(revision 4274)
+++ linux/sdllink.c	(working copy)
@@ -23,8 +23,10 @@
 #include "sw_draw.h"
 #include "gl_draw.h"
 
+#include <ao/ao.h>
+
 #include <SDL_thread.h>
-
+#include <pthread.h>
 #include <sys/time.h>
 #include <time.h>
 #include <dirent.h>
@@ -53,16 +55,10 @@
 typedef enum vidstate_e { vid_null, vid_none, vid_soft, vid_gl } vidstate_t;
 
 // SOUND RELATED VARIABLES
-SDL_AudioSpec audiospec;
 int SoundEnabled = 1;
 BYTE PrevStereoSound;
 DWORD PrevSoundQuality;
-Uint8 *Buffer = NULL;
-int Buffer_len = 0, Buffer_fill = 0;
-int Buffer_head = 0, Buffer_tail = 0;
 
-extern int DSPBuffer[];
-
 /* VIDEO VARIABLES */
 SDL_Surface *surface;
 int SurfaceLocking = 0;
@@ -726,63 +722,158 @@
   }
 }
 
+#define SAMPLE_NTSC_HI_SCALE 995ULL
+#define SAMPLE_NTSC_LO 59649ULL
+#define SAMPLE_PAL_HI_SCALE  1ULL
+#define SAMPLE_PAL_LO       50ULL
+static const int freqtab[7] = { 8000, 11025, 22050, 44100, 16000, 32000, 48000 };
+#define RATE freqtab[SoundQuality]
+
+struct
+{
+  unsigned long long hi;
+  unsigned long long lo;
+  unsigned long long balance;
+} sample_control;
+
+void init_sample_control()
+{
+  if (romispal)
+  {
+    sample_control.hi = SAMPLE_PAL_HI_SCALE*RATE;
+    sample_control.lo = SAMPLE_PAL_LO;
+  }
+  else
+  {
+    sample_control.hi = SAMPLE_NTSC_HI_SCALE*RATE;
+    sample_control.lo = SAMPLE_NTSC_LO;
+  }
+  sample_control.balance = sample_control.hi;
+}
+
+static ao_device *device;
+
+void WriteSamples(unsigned int samples)
+{
+  extern unsigned int BufferSizeB, BufferSizeW;
+  extern int DSPBuffer[1280];
+  void ProcessSoundBuffer();
+  short stemp[1280];
+
+  int *d = 0;
+  short *p = 0;
+
+  if (samples > 1280)
+  {
+    WriteSamples(1280);
+    samples -= 1280;
+  }
+
+  //printf("samples %d\n", samples);
+
+  BufferSizeB = samples;
+  BufferSizeW = samples<<1;
+
+  asm_call(ProcessSoundBuffer);
+
+  d = DSPBuffer;
+  p = stemp;
+
+  for (; d < DSPBuffer+samples; d++, p++)
+  {
+    if ((unsigned int)(*d + 0x8000) <= 0xFFFF) { *p = *d; continue; }
+    if (*d > 0x7FFF) { *p = 0x7FFF; }
+    else { *p = 0x8000; }
+  }
+
+  ao_play(device, (char *)stemp, samples*2);
+}
+
+static unsigned int samples_waiting = 0;
+
+void *AudioThread(void *useless)
+{
+  for (;;)
+  {
+    if (samples_waiting)
+    {
+      int samples = samples_waiting;
+      samples_waiting = 0;
+      WriteSamples(samples);
+    }
+    else
+    {
+      usleep(20);
+    }
+  }
+  return(0);
+}
+
+pthread_t audio_thread;
+
 int InitSound(void)
 {
-  SDL_AudioSpec wanted;
-  const int samptab[7] = { 1, 1, 2, 4, 2, 4, 4 };
-  const int freqtab[7] = { 8000, 11025, 22050, 44100, 16000, 32000, 48000 };
+  int driver_id;
 
-  SDL_CloseAudio();
-
   if (!SoundEnabled)
   {
     return FALSE;
   }
 
-  if (Buffer)
-    free(Buffer);
-  Buffer = NULL;
-  Buffer_len = 0;
-
   PrevSoundQuality = SoundQuality;
   PrevStereoSound = StereoSound;
 
-  if (SoundQuality > 6)
-    SoundQuality = 1;
-  wanted.freq = freqtab[SoundQuality];
+  driver_id = ao_driver_id(libAoDriver);
+  if (driver_id < 0) { driver_id = ao_default_driver_id(); }
 
-  if (StereoSound)
+  ao_sample_format driver_format;
+  driver_format.bits = 16;
+  driver_format.channels = StereoSound+1;
+  driver_format.rate = freqtab[SoundQuality = ((SoundQuality > 6) ? 1 : SoundQuality)];
+  driver_format.byte_format = AO_FMT_LITTLE;
+
+  if (device)
   {
-    wanted.channels = 2;
+    ao_close(device);
   }
   else
   {
-    wanted.channels = 1;
+    pthread_create(&audio_thread, 0, AudioThread, 0);
+    init_sample_control();
   }
 
-  wanted.samples = samptab[SoundQuality] * 128 * wanted.channels;
+  //ao_option driver_options = { "buf_size", "32768", 0 };
 
-  wanted.format = AUDIO_S16LSB;
-  wanted.userdata = NULL;
-  wanted.callback = UpdateSound;
-
-  if (SDL_OpenAudio(&wanted, &audiospec) < 0)
+  device = ao_open_live(driver_id, &driver_format, 0);
+  if (device)
   {
-    fprintf(stderr, "Sound init failed!\n");
-    fprintf(stderr, "freq: %d, channels: %d, samples: %d\n",
-      wanted.freq, wanted.channels, wanted.samples);
-    SoundEnabled = 0;
-    return FALSE;
+    ao_info *di = ao_driver_info(driver_id);
+    printf("\nAudio Opened.\nDriver: %s\nChannels: %u\nRate: %u\n\n", di->name, driver_format.channels, driver_format.rate);
   }
-  SDL_PauseAudio(0);
+  else
+  {
+    puts("Audio Open Failed");
+  }
 
-  Buffer_len = (audiospec.size * 2);
-  Buffer_len = (Buffer_len + 255) & ~255; /* Align to SPCSize */
-  Buffer = malloc(Buffer_len);
-
   return TRUE;
 }
 
+void WriteAudio()
+{
+  unsigned int samples = 0;
+  if (sample_control.lo)
+  {
+    samples = (unsigned int)((sample_control.balance/sample_control.lo) << StereoSound);
+    sample_control.balance %= sample_control.lo;
+    sample_control.balance += sample_control.hi;
+  }
+
+  if (!samples_waiting)
+  {
+    samples_waiting = samples;
+  }
+}
+
 int ReInitSound(void)
 {
   return InitSound();
@@ -1200,33 +1291,6 @@
   }
 }
 
-//Why in the world did someone make this use signed values??? -Nach
-void UpdateSound(void *userdata, Uint8 * stream, int len)
-{
-  int left = Buffer_len - Buffer_head;
-
-  if (left < 0)
-  {
-    return;
-  }
-
-  if (left <= len)
-  {
-    memcpy(stream, &Buffer[Buffer_head], left);
-    stream += left;
-    len -= left;
-    Buffer_head = 0;
-    Buffer_fill -= left;
-  }
-
-  if (len)
-  {
-    memcpy(stream, &Buffer[Buffer_head], len);
-    Buffer_head += len;
-    Buffer_fill -= len;
-  }
-}
-
 void sem_sleep(void)
 {
   end = update_ticks_pc - (sem_GetTicks() - start) - .2f;
@@ -1276,48 +1340,11 @@
 
 void UpdateVFrame(void)
 {
-  extern unsigned char DSPDisable;
-  extern unsigned int BufferSizeB, BufferSizeW;
-
   //Quick fix for GUI CPU usage
   if (GUIOn || GUIOn2 || EMUPause) { usleep(6000); }
 
   CheckTimers();
   Main_Proc();
-
-  /* Process sound */
-  BufferSizeB = 256;
-  BufferSizeW = BufferSizeB+BufferSizeB;
-
-  /* take care of the things we left behind last time */
-  SDL_LockAudio();
-  while (Buffer_fill < Buffer_len)
-  {
-    short *ptr = (short*)&Buffer[Buffer_tail];
-
-    if (soundon && !DSPDisable) { asm_call(ProcessSoundBuffer); }
-
-    if (T36HZEnabled)
-    {
-      memset(ptr, 0, BufferSizeW);
-    }
-    else
-    {
-      int *d = DSPBuffer;
-      int *end_d = DSPBuffer+BufferSizeB;
-      for (; d < end_d; d++, ptr++)
-      {
-        if ((unsigned) (*d + 0x8000) <= 0xFFFF) { *ptr = *d; continue; }
-        if (*d > 0x7FFF) { *ptr = 0x7FFF; }
-        else { *d = 0x8000; }
-      }
-    }
-
-    Buffer_fill += BufferSizeW;
-    Buffer_tail += BufferSizeW;
-    if (Buffer_tail >= Buffer_len) { Buffer_tail = 0; }
-  }
-  SDL_UnlockAudio();
 }
 
 void clearwin()
@@ -1336,6 +1363,8 @@
 
 void drawscreenwin(void)
 {
+  extern bool RawDumpInProgress;
+
   /* Just in case - DDOI */
   if (sdl_state == vid_none) return;
 
@@ -1345,12 +1374,19 @@
   else
 #endif
     sw_drawwin();
+
+  if (!GUIOn2 && !GUIOn && !EMUPause && !RawDumpInProgress)
+  {
+    WriteAudio();
+  }
+
 }
 
 void UnloadSDL()
 {
+  if (device) { ao_close(device); }
+  ao_shutdown();
   sem_sleep_die(); // Shutdown semaphore
-  if (Buffer) { free(Buffer); }
   if (sdl_state == vid_soft) { sw_end(); }
 #ifdef __OPENGL__
   else if (sdl_state == vid_gl) { gl_end(); }
I'd appreciate if those who knows pthreads well can contribute advice or perhaps code on how to sync the audio better. It works fine for me, but if I tax down my CPU, the audio gets crackly, probably since one thread no longer stays in sync with the other.
May 9 2007 - NSRT 3.4, now with lots of hashing and even more accurate information! Go download it.
_____________
Insane Coding
Aerdan
Winter Knight
Posts: 467
Joined: Mon Aug 16, 2004 10:16 pm
Contact:

Post by Aerdan »

Dunno what all you did, but loading savestates results in a permanent 0/60 fps with that patch. [It seems to be due to a state for a specific game., however; saving/loading a state in Chrono Trigger works fine.]
Last edited by Aerdan on Fri Jan 05, 2007 4:29 pm, edited 1 time in total.
juwb
New Member
Posts: 3
Joined: Sun Jun 18, 2006 7:21 pm

Post by juwb »

patch doesn't apply cleanly, but works in Linux
in fact I don't see (or hear) any difference
pagefault
ZSNES Developer
ZSNES Developer
Posts: 812
Joined: Tue Aug 17, 2004 5:24 am
Location: In your garden

Post by pagefault »

Patch against SVN, not 1.50.
janfsd
Rookie
Posts: 14
Joined: Fri Dec 22, 2006 10:05 pm

Post by janfsd »

Thanks for the patch, I was testing it, so far with chrono trigger, and I get the best results with oss, with alsa from time to time the sound becomes crappy but just temporarly.
With oss from time to time the sound gets cut, but not so often. It's just a small cut.
Another thing is that when I was using it with Alsa i got this message more than once:

Code: Select all

ALSA: underrun, at least 0ms.
Also I tested the patch with contra III and the problems I mentioned above don't appear at all, however using alsa I noticed a very small delay of sound, not so noticeable, but just comparing with oss...

And when using oss I always get this at the beginning:

Code: Select all

ALSA lib ../../../src/pcm/pcm_dmix.c:862:(snd_pcm_dmix_open) unable to open slave

Specs:
Ubuntu Edgy (AMD64)
CPU: Amd64 x2 3800+
1 GB RAM
Soundcard: Nvidia Ck804 chip: Realtek ACL850 rev 0
Using libao 0.8.6-4
Nach
ZSNES Developer
ZSNES Developer
Posts: 3904
Joined: Tue Jul 27, 2004 10:54 pm
Location: Solar powered park bench
Contact:

Post by Nach »

Oh, also mention if you think it sounds better than the SDL we were using.
May 9 2007 - NSRT 3.4, now with lots of hashing and even more accurate information! Go download it.
_____________
Insane Coding
janfsd
Rookie
Posts: 14
Joined: Fri Dec 22, 2006 10:05 pm

Post by janfsd »

Well, you can forget about what I said before, now I don't get that cut problems with oss or the crappy sound with alsa, (I think I installed some libraries today so now that problems don't happen).

The only thing that is still there, is that with alsa there is some delay time, by the way, I am using alsa 1.0.11

As for which one sounds better, if the old sdl or the new one, I cannot hear a difference, maybe you can tell me a better way to compare them, or some game that is good to compare with.

EDIT:
With Chrono Trigger I cannot hear a difference, but I just tried with Top Gear, and the music sounds really better with libao.
Nach
ZSNES Developer
ZSNES Developer
Posts: 3904
Joined: Tue Jul 27, 2004 10:54 pm
Location: Solar powered park bench
Contact:

Post by Nach »

Okay, forget using any patches, libao is now in SVN. It's usage is optional, you can still select SDL output.
I also fixed two bugs with the SDL sound code in the process.
May 9 2007 - NSRT 3.4, now with lots of hashing and even more accurate information! Go download it.
_____________
Insane Coding
12th&saturn
Rookie
Posts: 11
Joined: Tue Apr 25, 2006 4:30 am

Post by 12th&saturn »

Mednafen's solution, SexyAL, is a thin sound layer continued from Xod's work on FCE Ultra. It works very well, outputting to SDL, OSS, ALSA, and Jack with very low latencies.
CyberBotX
Lurker
Posts: 109
Joined: Sun Jan 30, 2005 10:06 pm
Location: Wouldn't you like to know?
Contact:

Post by CyberBotX »

Nach asked me to test the new libao options under FreeBSD (I'm using 6.2-RC1 right now, which probably isn't too different from 6.2-RELEASE), and I couldn't hear much difference between using oss or sdl. I could not test arts because I have that disabled (it takes over my entire sound card so nothing else can use it, so I don't use it). I only tested it with Chrono Trigger while inside one of the huts in 65,000,000 B.C., though, so I'm not sure how accurate the test would've been. If there's a better game or place in CT to test, I'll try that instead.
[url=http://www.cyberbotx.com/]SNES Sprite Animations[/url], made by an Insane Killer Robot.
I'm a computer programmer (in C++) and a future game designer.
Deathlike2
ZSNES Developer
ZSNES Developer
Posts: 6747
Joined: Tue Dec 28, 2004 6:47 am

Post by Deathlike2 »

The whole point of the libao testing is to see if you are able to rid of static or playback issues. It won't fix notable sound emulation problems of course, but certain things should play back better.
Continuing [url=http://slickproductions.org/forum/index.php?board=13.0]FF4[/url] Research...
CyberBotX
Lurker
Posts: 109
Joined: Sun Jan 30, 2005 10:06 pm
Location: Wouldn't you like to know?
Contact:

Post by CyberBotX »

Ah. Well in that case, I didn't detect any noticable difference in staticness between older versions and current ones with libao. As for playback, I've noticed it still depends on how overloaded my CPU is. Both before and after libao, the sound gets a bit choppy when it hits a CPU lag bubble. So I think for myself, it seems mostly the same.
[url=http://www.cyberbotx.com/]SNES Sprite Animations[/url], made by an Insane Killer Robot.
I'm a computer programmer (in C++) and a future game designer.
janfsd
Rookie
Posts: 14
Joined: Fri Dec 22, 2006 10:05 pm

Post by janfsd »

With the newest zsnes version, I still got the delay sound problem with libao and alsa output (it's very little delay). But with oss it's ok.
Nach
ZSNES Developer
ZSNES Developer
Posts: 3904
Joined: Tue Jul 27, 2004 10:54 pm
Location: Solar powered park bench
Contact:

Post by Nach »

ALSA sucks. If OSS works good for you than be happy. Fee free to try the other drivers too.
May 9 2007 - NSRT 3.4, now with lots of hashing and even more accurate information! Go download it.
_____________
Insane Coding
janfsd
Rookie
Posts: 14
Joined: Fri Dec 22, 2006 10:05 pm

Post by janfsd »

I know that, I am happy with oss. I was thinking that maybe you wanted to know about that problem. Anyway, is the alsa problem some kind of bug?
Deathlike2
ZSNES Developer
ZSNES Developer
Posts: 6747
Joined: Tue Dec 28, 2004 6:47 am

Post by Deathlike2 »

The ALSA issue is usually a result of either bad drivers, or just a bad sound API to work with.
Continuing [url=http://slickproductions.org/forum/index.php?board=13.0]FF4[/url] Research...
ssokolow
New Member
Posts: 2
Joined: Fri Oct 26, 2007 7:04 am
Location: Ontario, Canada
Contact:

Post by ssokolow »

A few things to keep in mind:

1. As I understand it, hardware mixing is actually worse than properly-done software mixing (dmix doesn't count) for modern systems because the system has to poll the buffers on the hardware mixer rather than just pushing in the completed stream. (And apparently the polling has a bigger performance penalty than just doing the mixing in the CPU)

3. Apparently OSS 4.x (GPLed and with an ALSA compatibility layer) does software mixing properly. (It has an in-kernel software mixer called vmix)

Oh, and dmix default settings will often still cause crackling when using ZSNES 1.51 with Intel HDA audio. I'll let you guys know when I figure out the proper /etc/asound.conf to fix it.
bsund

Post by bsund »

PulseAudio is the future..

i use fedora 8 and 1.51 works like a dream.. it has wrappers for most systems and works like a dream.. SDL is very nice and will be nice in the future.. so do stuff through SDL.. and PA will do the rest..

read this for a nice explanation about PulseAudio and see how it works and what it do.. it's a discussion about making PA the soundserver for gnome.. and a main developer comes in to explain what PA is about.. very nice read.. you dont need to read the rest of the mails..

http://mail.gnome.org/archives/desktop- ... 00136.html

i think it's best to keep it to SDL since it's very portable and only get's more and more accepted and the development in it is ace.. lots of commercial stuff use it.. (and it support pulseaudio directly if you want)
ssokolow
New Member
Posts: 2
Joined: Fri Oct 26, 2007 7:04 am
Location: Ontario, Canada
Contact:

Post by ssokolow »

bsund wrote:PulseAudio is the future..

i use fedora 8 and 1.51 works like a dream.. it has wrappers for most systems and works like a dream.. SDL is very nice and will be nice in the future.. so do stuff through SDL.. and PA will do the rest..

read this for a nice explanation about PulseAudio and see how it works and what it do.. it's a discussion about making PA the soundserver for gnome.. and a main developer comes in to explain what PA is about.. very nice read.. you dont need to read the rest of the mails..

http://mail.gnome.org/archives/desktop- ... 00136.html

i think it's best to keep it to SDL since it's very portable and only get's more and more accepted and the development in it is ace.. lots of commercial stuff use it.. (and it support pulseaudio directly if you want)
PulseAudio is a sound server. Many people (myself included) prefer to avoid sound servers.

As for using SDL audio, I believe the devs said that it was the main reason for the timing problems that I remember so well in earlier Linux ZSNES releases... but don't quote me on that.
Post Reply