summaryrefslogtreecommitdiff
path: root/pcsound
diff options
context:
space:
mode:
Diffstat (limited to 'pcsound')
-rw-r--r--pcsound/Makefile.am10
-rw-r--r--pcsound/pcsound.c114
-rw-r--r--pcsound/pcsound.h45
-rw-r--r--pcsound/pcsound_sdl.c179
-rw-r--r--pcsound/pcsound_win32.c115
5 files changed, 463 insertions, 0 deletions
diff --git a/pcsound/Makefile.am b/pcsound/Makefile.am
new file mode 100644
index 00000000..77957d72
--- /dev/null
+++ b/pcsound/Makefile.am
@@ -0,0 +1,10 @@
+
+AM_CFLAGS= @SDL_CFLAGS@ @SDLMIXER_CFLAGS@
+
+noinst_LIBRARIES=libpcsound.a
+
+libpcsound_a_SOURCES = \
+ pcsound.c pcsound.h \
+ pcsound_sdl.c \
+ pcsound_win32.c
+
diff --git a/pcsound/pcsound.c b/pcsound/pcsound.c
new file mode 100644
index 00000000..9c18eb01
--- /dev/null
+++ b/pcsound/pcsound.c
@@ -0,0 +1,114 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2007 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.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// PC speaker interface.
+//
+//-----------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pcsound.h"
+
+#ifdef _WIN32
+extern pcsound_driver_t pcsound_win32_driver;
+#endif
+extern pcsound_driver_t pcsound_sdl_driver;
+
+static pcsound_driver_t *drivers[] =
+{
+#ifdef _WIN32
+ &pcsound_win32_driver,
+#endif
+ &pcsound_sdl_driver,
+ NULL,
+};
+
+static pcsound_driver_t *pcsound_driver = NULL;
+
+int PCSound_Init(pcsound_callback_func callback_func)
+{
+ char *driver_name;
+ int i;
+
+ if (pcsound_driver != NULL)
+ {
+ return 1;
+ }
+
+ // Check if the environment variable is set
+
+ driver_name = getenv("PCSOUND_DRIVER");
+
+ if (driver_name != NULL)
+ {
+ for (i=0; drivers[i] != NULL; ++i)
+ {
+ if (!strcasecmp(drivers[i]->name, driver_name))
+ {
+ // Found the driver!
+
+ if (drivers[i]->init_func(callback_func))
+ {
+ pcsound_driver = drivers[i];
+ }
+ else
+ {
+ printf("Failed to initialise PC sound driver: %s\n",
+ drivers[i]->name);
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ // Try all drivers until we find a working one
+
+ for (i=0; drivers[i] != NULL; ++i)
+ {
+ if (drivers[i]->init_func(callback_func))
+ {
+ pcsound_driver = drivers[i];
+ break;
+ }
+ }
+ }
+
+ if (pcsound_driver != NULL)
+ {
+ printf("Using PC sound driver: %s\n", pcsound_driver->name);
+ return 1;
+ }
+ else
+ {
+ printf("Failed to find a working PC sound driver.\n");
+ return 0;
+ }
+}
+
+void PCSound_Shutdown(void)
+{
+ pcsound_driver->shutdown_func();
+ pcsound_driver = NULL;
+}
+
diff --git a/pcsound/pcsound.h b/pcsound/pcsound.h
new file mode 100644
index 00000000..1adee416
--- /dev/null
+++ b/pcsound/pcsound.h
@@ -0,0 +1,45 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2007 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.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// PC speaker interface.
+//
+//-----------------------------------------------------------------------------
+
+#ifndef PCSOUND_H
+#define PCSOUND_H
+
+typedef struct pcsound_driver_s pcsound_driver_t;
+typedef void (*pcsound_callback_func)(int *duration, int *frequency);
+typedef int (*pcsound_init_func)(pcsound_callback_func callback);
+typedef void (*pcsound_shutdown_func)(void);
+
+struct pcsound_driver_s
+{
+ char *name;
+ pcsound_init_func init_func;
+ pcsound_shutdown_func shutdown_func;
+};
+
+int PCSound_Init(pcsound_callback_func callback_func);
+void PCSound_Shutdown(void);
+
+#endif /* #ifndef PCSOUND_H */
+
diff --git a/pcsound/pcsound_sdl.c b/pcsound/pcsound_sdl.c
new file mode 100644
index 00000000..50820394
--- /dev/null
+++ b/pcsound/pcsound_sdl.c
@@ -0,0 +1,179 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2007 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.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// PC speaker interface.
+//
+//-----------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "SDL.h"
+#include "SDL_mixer.h"
+
+#include "pcsound.h"
+
+#define SQUARE_WAVE_AMP 0x2000
+
+static pcsound_callback_func callback;
+
+// Output sound format
+
+static int mixing_freq;
+static Uint16 mixing_format;
+static int mixing_channels;
+
+// Currently playing sound
+// current_remaining is the number of remaining samples that must be played
+// before we invoke the callback to get the next frequency.
+
+static int current_remaining;
+static int current_freq;
+
+static int phase_offset = 0;
+
+// Mixer function that does the PC speaker emulation
+
+static void PCSound_Mix_Callback(void *udata, Uint8 *stream, int len)
+{
+ Sint16 *leftptr;
+ Sint16 *rightptr;
+ Sint16 this_value;
+ int oldfreq;
+ int i;
+ int nsamples;
+
+ // Number of samples is quadrupled, because of 16-bit and stereo
+
+ nsamples = len / 4;
+
+ leftptr = (Sint16 *) stream;
+ rightptr = ((Sint16 *) stream) + 1;
+
+ // Fill the output buffer
+
+ for (i=0; i<nsamples; ++i)
+ {
+ // Has this sound expired? If so, invoke the callback to get
+ // the next frequency.
+
+ while (current_remaining == 0)
+ {
+ oldfreq = current_freq;
+
+ // Get the next frequency to play
+
+ callback(&current_remaining, &current_freq);
+
+ if (current_freq != 0)
+ {
+ // Adjust phase to match to the new frequency.
+ // This gives us a smooth transition between different tones,
+ // with no impulse changes.
+
+ phase_offset = (phase_offset * oldfreq) / current_freq;
+ }
+
+ current_remaining = (current_remaining * mixing_freq) / 1000;
+ }
+
+ // Set the value for this sample.
+
+ if (current_freq == 0)
+ {
+ // Silence
+
+ this_value = 0;
+ }
+ else
+ {
+ int frac;
+
+ // Determine whether we are at a peak or trough in the current
+ // sound. Multiply by 2 so that frac % 2 will give 0 or 1
+ // depending on whether we are at a peak or trough.
+
+ frac = (phase_offset * current_freq * 2) / mixing_freq;
+
+ if ((frac % 2) == 0)
+ {
+ this_value = SQUARE_WAVE_AMP;
+ }
+ else
+ {
+ this_value = -SQUARE_WAVE_AMP;
+ }
+
+ ++phase_offset;
+ }
+
+ --current_remaining;
+
+ // Use the same value for the left and right channels.
+
+ *leftptr += this_value;
+ *rightptr += this_value;
+
+ leftptr += 2;
+ rightptr += 2;
+ }
+}
+
+static int PCSound_SDL_Init(pcsound_callback_func callback_func)
+{
+ // Check that SDL_mixer has been opened already
+ // If not, fail
+
+ if (!Mix_QuerySpec(&mixing_freq, &mixing_format, &mixing_channels))
+ {
+ return 0;
+ }
+
+ // Only supports AUDIO_S16SYS
+
+ if (mixing_format != AUDIO_S16SYS || mixing_channels != 2)
+ {
+ fprintf(stderr,
+ "PCSound_SDL only supports native signed 16-bit LSB, "
+ "stereo format!\n");
+ return 0;
+ }
+
+ callback = callback_func;
+ current_freq = 0;
+ current_remaining = 0;
+
+ Mix_SetPostMix(PCSound_Mix_Callback, NULL);
+
+ return 1;
+}
+
+static void PCSound_SDL_Shutdown(void)
+{
+}
+
+pcsound_driver_t pcsound_sdl_driver =
+{
+ "SDL",
+ PCSound_SDL_Init,
+ PCSound_SDL_Shutdown,
+};
+
diff --git a/pcsound/pcsound_win32.c b/pcsound/pcsound_win32.c
new file mode 100644
index 00000000..090c0bbd
--- /dev/null
+++ b/pcsound/pcsound_win32.c
@@ -0,0 +1,115 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2007 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.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// PC speaker interface.
+//
+//-----------------------------------------------------------------------------
+
+#ifdef _WIN32
+
+#include <SDL.h>
+#include <windows.h>
+
+#include "pcsound.h"
+
+static SDL_Thread *sound_thread_handle;
+static int sound_thread_running;
+static pcsound_callback_func callback;
+
+static int SoundThread(void *unused)
+{
+ int frequency;
+ int duration;
+
+ while (sound_thread_running)
+ {
+ callback(&duration, &frequency);
+
+ if (frequency != 0)
+ {
+ Beep(frequency, duration);
+ }
+ else
+ {
+ Sleep(duration);
+ }
+ }
+
+ return 0;
+}
+
+static int PCSound_Win32_Init(pcsound_callback_func callback_func)
+{
+ OSVERSIONINFO osvi;
+ BOOL result;
+
+ // Temporarily disabled - the Windows scheduler is strange and
+ // stupid.
+
+ return 0;
+
+ // Find the OS version
+
+ osvi.dwOSVersionInfoSize = sizeof(osvi);
+
+ result = GetVersionEx(&osvi);
+
+ if (!result)
+ {
+ return 0;
+ }
+
+ // Beep() ignores its arguments on win9x, so this driver will
+ // not work there.
+
+ if (osvi.dwPlatformId != VER_PLATFORM_WIN32_NT)
+ {
+ // TODO: Use _out() to write directly to the PC speaker on
+ // win9x: See PC/winsound.c in the Python standard library.
+
+ return 0;
+ }
+
+ // Start a thread to play sound.
+
+ callback = callback_func;
+ sound_thread_running = 1;
+
+ sound_thread_handle = SDL_CreateThread(SoundThread, NULL);
+
+ return 1;
+}
+
+static void PCSound_Win32_Shutdown(void)
+{
+ sound_thread_running = 0;
+ SDL_WaitThread(sound_thread_handle, NULL);
+}
+
+pcsound_driver_t pcsound_win32_driver =
+{
+ "Windows",
+ PCSound_Win32_Init,
+ PCSound_Win32_Shutdown,
+};
+
+#endif /* #ifdef _WIN32 */
+