summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSimon Howard2007-03-07 19:08:27 +0000
committerSimon Howard2007-03-07 19:08:27 +0000
commite434c7c10278a47fc686024fae04d361a0282ab8 (patch)
tree04bbcbcfe2c2f0cae269e0c16911615ecf51bf6f /src
parentb96d0e706d15778c264f8e824343b050ea4a72d6 (diff)
downloadchocolate-doom-e434c7c10278a47fc686024fae04d361a0282ab8.tar.gz
chocolate-doom-e434c7c10278a47fc686024fae04d361a0282ab8.tar.bz2
chocolate-doom-e434c7c10278a47fc686024fae04d361a0282ab8.zip
Use native endianness for sound output, rather than always LSB. Add PC
speaker code! Subversion-branch: /trunk/chocolate-doom Subversion-revision: 844
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am5
-rw-r--r--src/i_pcsound.c238
-rw-r--r--src/i_pcsound.h40
-rw-r--r--src/i_sound.c111
-rw-r--r--src/pcsound/Makefile.am10
-rw-r--r--src/pcsound/pcsound.c114
-rw-r--r--src/pcsound/pcsound.h45
-rw-r--r--src/pcsound/pcsound_sdl.c179
-rw-r--r--src/pcsound/pcsound_win32.c113
9 files changed, 825 insertions, 30 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index d34a5b17..53ef775c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -22,6 +22,8 @@ z_native.c z_zone.h
chocolate_server_SOURCES=$(DEDSERV_FILES)
chocolate_server_LDADD = @LDFLAGS@ @SDL_LIBS@ @SDLNET_LIBS@
+SUBDIRS=pcsound
+
SOURCE_FILES=\
am_map.c am_map.h \
deh_ammo.c \
@@ -62,6 +64,7 @@ hu_stuff.c hu_stuff.h \
i_main.c \
info.c info.h \
i_scale.c i_scale.h \
+i_pcsound.c i_pcsound.h \
i_sound.c i_sound.h \
i_system.c i_system.h \
i_timer.c i_timer.h \
@@ -138,7 +141,7 @@ else
chocolate_doom_SOURCES=$(SOURCE_FILES)
endif
-chocolate_doom_LDADD = ../textscreen/libtextscreen.a @LDFLAGS@ @SDL_LIBS@ @SDLMIXER_LIBS@ @SDLNET_LIBS@
+chocolate_doom_LDADD = ../textscreen/libtextscreen.a pcsound/libpcsound.a @LDFLAGS@ @SDL_LIBS@ @SDLMIXER_LIBS@ @SDLNET_LIBS@
EXTRA_DIST = \
chocolate_doom_icon.c \
diff --git a/src/i_pcsound.c b/src/i_pcsound.c
new file mode 100644
index 00000000..926e5f1d
--- /dev/null
+++ b/src/i_pcsound.c
@@ -0,0 +1,238 @@
+// 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:
+// System interface for PC speaker sound.
+//
+//-----------------------------------------------------------------------------
+
+#include "SDL.h"
+
+#include "doomdef.h"
+#include "doomtype.h"
+
+#include "i_pcsound.h"
+#include "i_sound.h"
+#include "sounds.h"
+
+#include "w_wad.h"
+#include "z_zone.h"
+
+#include "pcsound/pcsound.h"
+
+static boolean pcs_initialised = false;
+
+static SDL_mutex *sound_lock;
+
+static uint8_t *current_sound_lump = NULL;
+static uint8_t *current_sound_pos = NULL;
+static unsigned int current_sound_remaining = 0;
+static int current_sound_handle = 0;
+
+static float frequencies[] = {
+ 0, 175.00, 180.02, 185.01, 190.02, 196.02, 202.02, 208.01, 214.02, 220.02,
+ 226.02, 233.04, 240.02, 247.03, 254.03, 262.00, 269.03, 277.03, 285.04,
+ 294.03, 302.07, 311.04, 320.05, 330.06, 339.06, 349.08, 359.06, 370.09,
+ 381.08, 392.10, 403.10, 415.01, 427.05, 440.12, 453.16, 466.08, 480.15,
+ 494.07, 508.16, 523.09, 539.16, 554.19, 571.17, 587.19, 604.14, 622.09,
+ 640.11, 659.21, 679.10, 698.17, 719.21, 740.18, 762.41, 784.47, 807.29,
+ 831.48, 855.32, 880.57, 906.67, 932.17, 960.69, 988.55, 1017.20, 1046.64,
+ 1077.85, 1109.93, 1141.79, 1175.54, 1210.12, 1244.19, 1281.61, 1318.43,
+ 1357.42, 1397.16, 1439.30, 1480.37, 1523.85, 1569.97, 1614.58, 1661.81,
+ 1711.87, 1762.45, 1813.34, 1864.34, 1921.38, 1975.46, 2036.14, 2093.29,
+ 2157.64, 2217.80, 2285.78, 2353.41, 2420.24, 2490.98, 2565.97, 2639.77,
+};
+
+#define NUM_FREQUENCIES (sizeof(frequencies) / sizeof(*frequencies))
+
+void PCSCallbackFunc(int *duration, int *freq)
+{
+ int tone;
+
+ *duration = 1000 / 140;
+
+ if (SDL_LockMutex(sound_lock) < 0)
+ {
+ *freq = 0;
+ return;
+ }
+
+ if (current_sound_lump != NULL && current_sound_remaining > 0)
+ {
+ // Read the next tone
+
+ tone = *current_sound_pos;
+
+ // Use the tone -> frequency lookup table. See pcspkr10.zip
+ // for a full discussion of this.
+ // Check we don't overflow the frequency table.
+
+ if (tone < NUM_FREQUENCIES)
+ {
+ *freq = (int) frequencies[tone];
+ }
+ else
+ {
+ *freq = 0;
+ }
+
+ ++current_sound_pos;
+ --current_sound_remaining;
+ }
+ else
+ {
+ *freq = 0;
+ }
+
+ SDL_UnlockMutex(sound_lock);
+}
+
+static boolean CachePCSLump(int sound_id)
+{
+ int lumplen;
+ int headerlen;
+
+ // Free the current sound lump back to the cache
+
+ if (current_sound_lump != NULL)
+ {
+ Z_ChangeTag(current_sound_lump, PU_CACHE);
+ current_sound_lump = NULL;
+ }
+
+ // Load from WAD
+
+ current_sound_lump = W_CacheLumpNum(S_sfx[sound_id].lumpnum, PU_STATIC);
+ lumplen = W_LumpLength(S_sfx[sound_id].lumpnum);
+
+ // Read header
+
+ if (current_sound_lump[0] != 0x00 || current_sound_lump[1] != 0x00)
+ {
+ return false;
+ }
+
+ headerlen = (current_sound_lump[3] << 8) | current_sound_lump[2];
+
+ if (headerlen > lumplen - 4)
+ {
+ return false;
+ }
+
+ // Header checks out ok
+
+ current_sound_remaining = headerlen;
+ current_sound_pos = current_sound_lump + 4;
+
+ return true;
+}
+
+int I_PCS_StartSound(int id,
+ int channel,
+ int vol,
+ int sep,
+ int pitch,
+ int priority)
+{
+ int result;
+
+ if (!pcs_initialised)
+ {
+ return -1;
+ }
+
+ // These PC speaker sounds are not played - this can be seen in the
+ // Heretic source code, where there are remnants of this left over
+ // from Doom.
+
+ if (id == sfx_posact || id == sfx_bgact || id == sfx_dmact
+ || id == sfx_dmpain || id == sfx_popain || id == sfx_sawidl)
+ {
+ return -1;
+ }
+
+ if (SDL_LockMutex(sound_lock) < 0)
+ {
+ return -1;
+ }
+
+ result = CachePCSLump(id);
+
+ if (result)
+ {
+ current_sound_handle = channel;
+ }
+
+ SDL_UnlockMutex(sound_lock);
+
+ if (result)
+ {
+ return channel;
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+void I_PCS_StopSound(int handle)
+{
+ if (!pcs_initialised)
+ {
+ return;
+ }
+
+ if (SDL_LockMutex(sound_lock) < 0)
+ {
+ return;
+ }
+
+ // If this is the channel currently playing, immediately end it.
+
+ if (current_sound_handle == handle)
+ {
+ current_sound_remaining = 0;
+ }
+
+ SDL_UnlockMutex(sound_lock);
+}
+
+int I_PCS_SoundIsPlaying(int handle)
+{
+ if (!pcs_initialised)
+ {
+ return false;
+ }
+
+ if (handle != current_sound_handle)
+ {
+ return false;
+ }
+
+ return current_sound_lump != NULL && current_sound_remaining > 0;
+}
+
+void I_PCS_InitSound(void)
+{
+ pcs_initialised = PCSound_Init(PCSCallbackFunc);
+
+ sound_lock = SDL_CreateMutex();
+}
+
diff --git a/src/i_pcsound.h b/src/i_pcsound.h
new file mode 100644
index 00000000..601f4ee5
--- /dev/null
+++ b/src/i_pcsound.h
@@ -0,0 +1,40 @@
+// 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:
+// System interface for PC speaker sound.
+//
+//-----------------------------------------------------------------------------
+
+#ifndef __I_PCSOUND_H__
+#define __I_PCSOUND_H__
+
+int I_PCS_StartSound(int id,
+ int channel,
+ int vol,
+ int sep,
+ int pitch,
+ int priority);
+void I_PCS_StopSound(int handle);
+int I_PCS_SoundIsPlaying(int handle);
+void I_PCS_InitSound(void);
+
+#endif /* #ifndef __I_PCSOUND_H__ */
+
diff --git a/src/i_sound.c b/src/i_sound.c
index 15ad27d6..16d8ce7f 100644
--- a/src/i_sound.c
+++ b/src/i_sound.c
@@ -39,6 +39,7 @@
#include "z_zone.h"
#include "i_system.h"
+#include "i_pcsound.h"
#include "i_sound.h"
#include "deh_main.h"
#include "m_argv.h"
@@ -122,7 +123,7 @@ void ReleaseSoundOnChannel(int channel)
static void ExpandSoundData(byte *data, int samplerate, int length,
Mix_Chunk *destination)
{
- byte *expanded = (byte *) destination->abuf;
+ Sint16 *expanded = (Sint16 *) destination->abuf;
int expanded_length;
int expand_ratio;
int i;
@@ -135,28 +136,25 @@ static void ExpandSoundData(byte *data, int samplerate, int length,
for (i=0; i<length; ++i)
{
- Uint16 sample;
+ Sint16 sample;
sample = data[i] | (data[i] << 8);
sample -= 32768;
- expanded[i * 8] = expanded[i * 8 + 2]
- = expanded[i * 8 + 4] = expanded[i * 8 + 6] = sample & 0xff;
- expanded[i * 8 + 1] = expanded[i * 8 + 3]
- = expanded[i * 8 + 5] = expanded[i * 8 + 7] = (sample >> 8) & 0xff;
+ expanded[i * 4] = expanded[i * 4 + 1]
+ = expanded[i * 4 + 2] = expanded[i * 4 + 3] = sample;
}
}
else if (samplerate == 22050)
{
for (i=0; i<length; ++i)
{
- Uint16 sample;
+ Sint16 sample;
sample = data[i] | (data[i] << 8);
sample -= 32768;
- expanded[i * 4] = expanded[i * 4 + 2] = sample & 0xff;
- expanded[i * 4 + 1] = expanded[i * 4 + 3] = (sample >> 8) & 0xff;
+ expanded[i * 2] = expanded[i * 2 + 1] = sample;
}
}
else
@@ -170,7 +168,7 @@ static void ExpandSoundData(byte *data, int samplerate, int length,
for (i=0; i<expanded_length; ++i)
{
- Uint16 sample;
+ Sint16 sample;
int src;
src = (i * expand_ratio) >> 8;
@@ -180,8 +178,7 @@ static void ExpandSoundData(byte *data, int samplerate, int length,
// expand 8->16 bits, mono->stereo
- expanded[i * 4] = expanded[i * 4 + 2] = sample & 0xff;
- expanded[i * 4 + 1] = expanded[i * 4 + 3] = (sample >> 8) & 0xff;
+ expanded[i * 2] = expanded[i * 2 + 1] = sample;
}
}
}
@@ -200,7 +197,7 @@ static boolean CacheSFX(int sound)
// need to load the sound
- lumpnum = I_GetSfxLumpNum(&S_sfx[sound]);
+ lumpnum = S_sfx[sound].lumpnum;
data = W_CacheLumpNum(lumpnum, PU_STATIC);
lumplen = W_LumpLength(lumpnum);
@@ -213,7 +210,7 @@ static boolean CacheSFX(int sound)
return false;
}
-
+
// 16 bit sample rate field, 32 bit length field
samplerate = (data[3] << 8) | data[2];
@@ -288,9 +285,24 @@ void I_SetSfxVolume(int volume)
int I_GetSfxLumpNum(sfxinfo_t* sfx)
{
char namebuf[9];
- sprintf(namebuf, "ds%s", DEH_String(sfx->name));
+ char *prefix;
+
+ // Different prefix for PC speaker sound effects.
+
+ if (snd_sfxdevice == SNDDEVICE_PCSPEAKER)
+ {
+ prefix = "dp";
+ }
+ else
+ {
+ prefix = "ds";
+ }
+
+ sprintf(namebuf, "%s%s", prefix, DEH_String(sfx->name));
+
return W_GetNumForName(namebuf);
}
+
//
// Starting a sound means adding it
// to the current list of active sounds
@@ -317,6 +329,11 @@ I_StartSound
if (!sound_initialised)
return 0;
+ if (snd_sfxdevice == SNDDEVICE_PCSPEAKER)
+ {
+ return I_PCS_StartSound(id, channel, vol, sep, pitch, priority);
+ }
+
// Release a sound effect if there is already one playing
// on this channel
@@ -349,6 +366,12 @@ void I_StopSound (int handle)
if (!sound_initialised)
return;
+ if (snd_sfxdevice == SNDDEVICE_PCSPEAKER)
+ {
+ I_PCS_StopSound(handle);
+ return;
+ }
+
Mix_HaltChannel(handle);
// Sound data is no longer needed; release the
@@ -366,7 +389,14 @@ int I_SoundIsPlaying(int handle)
if (handle < 0)
return false;
- return Mix_Playing(handle);
+ if (snd_sfxdevice == SNDDEVICE_PCSPEAKER)
+ {
+ return I_PCS_SoundIsPlaying(handle);
+ }
+ else
+ {
+ return Mix_Playing(handle);
+ }
}
@@ -383,6 +413,11 @@ void I_UpdateSound( void )
if (!sound_initialised)
return;
+ if (snd_sfxdevice == SNDDEVICE_PCSPEAKER)
+ {
+ return;
+ }
+
// Check all channels to see if a sound has finished
for (i=0; i<NUM_CHANNELS; ++i)
@@ -425,6 +460,11 @@ I_UpdateSoundParams
if (!sound_initialised)
return;
+ if (snd_sfxdevice == SNDDEVICE_PCSPEAKER)
+ {
+ return;
+ }
+
left = ((254 - sep) * vol) / 127;
right = ((sep) * vol) / 127;
@@ -472,18 +512,34 @@ I_InitSound()
{
nomusicparm = true;
}
-
+
//!
// Disable sound effects.
//
nosfxparm = M_CheckParm("-nosfx") > 0;
- if (snd_sfxdevice < SNDDEVICE_SB)
+ // If the SFX device is 0 (none), then disable sound effects,
+ // just like if we specified -nosfx. However, we still continue
+ // with initialising digital sound output even if we are using
+ // the PC speaker, because we might be using the SDL PC speaker
+ // emulation.
+
+ if (snd_sfxdevice == SNDDEVICE_NONE)
{
nosfxparm = true;
}
+ //!
+ // Disable sound effects and music.
+ //
+
+ if (M_CheckParm("-nosound") > 0)
+ {
+ nosfxparm = true;
+ nomusicparm = true;
+ }
+
// When trying to run with music enabled on OSX, display
// a warning message.
@@ -498,16 +554,6 @@ I_InitSound()
}
#endif
- //!
- // Disable sound effects and music.
- //
-
- if (M_CheckParm("-nosound") > 0)
- {
- nosfxparm = true;
- nomusicparm = true;
- }
-
// If music or sound is going to play, we need to at least
// initialise SDL
// No sound in screensaver mode.
@@ -521,7 +567,7 @@ I_InitSound()
return;
}
- if (Mix_OpenAudio(22050, AUDIO_S16LSB, 2, 1024) < 0)
+ if (Mix_OpenAudio(22050, AUDIO_S16SYS, 2, 1024) < 0)
{
fprintf(stderr, "Error initialising SDL_mixer: %s\n", Mix_GetError());
return;
@@ -531,6 +577,13 @@ I_InitSound()
SDL_PauseAudio(0);
+ // If we are using the PC speaker, we now need to initialise it.
+
+ if (snd_sfxdevice == SNDDEVICE_PCSPEAKER)
+ {
+ I_PCS_InitSound();
+ }
+
if (!nomusicparm)
music_initialised = true;
diff --git a/src/pcsound/Makefile.am b/src/pcsound/Makefile.am
new file mode 100644
index 00000000..77957d72
--- /dev/null
+++ b/src/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/src/pcsound/pcsound.c b/src/pcsound/pcsound.c
new file mode 100644
index 00000000..9c18eb01
--- /dev/null
+++ b/src/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/src/pcsound/pcsound.h b/src/pcsound/pcsound.h
new file mode 100644
index 00000000..1adee416
--- /dev/null
+++ b/src/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/src/pcsound/pcsound_sdl.c b/src/pcsound/pcsound_sdl.c
new file mode 100644
index 00000000..50820394
--- /dev/null
+++ b/src/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/src/pcsound/pcsound_win32.c b/src/pcsound/pcsound_win32.c
new file mode 100644
index 00000000..41b1e059
--- /dev/null
+++ b/src/pcsound/pcsound_win32.c
@@ -0,0 +1,113 @@
+// 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 void SoundThread(void *unused)
+{
+ int frequency;
+ int duration;
+
+ while (sound_thread_running)
+ {
+ callback(&duration, &frequency);
+
+ if (frequency != 0)
+ {
+ Beep(frequency, duration);
+ }
+ else
+ {
+ Sleep(duration);
+ }
+ }
+}
+
+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 */
+