diff options
author | Simon Howard | 2007-09-03 00:30:51 +0000 |
---|---|---|
committer | Simon Howard | 2007-09-03 00:30:51 +0000 |
commit | 0517fad3f4e89304dc7a4ad18c5785e02173ca6c (patch) | |
tree | a63deca7e08c2a27c94a53e0bda21ffab9fcde94 | |
parent | 91eea3f8acd63d1641e03fc7088c833db7d0daf1 (diff) | |
download | chocolate-doom-0517fad3f4e89304dc7a4ad18c5785e02173ca6c.tar.gz chocolate-doom-0517fad3f4e89304dc7a4ad18c5785e02173ca6c.tar.bz2 chocolate-doom-0517fad3f4e89304dc7a4ad18c5785e02173ca6c.zip |
Add pcsound driver for OpenBSD.
Subversion-branch: /trunk/chocolate-doom
Subversion-revision: 965
-rw-r--r-- | configure.in | 2 | ||||
-rw-r--r-- | pcsound/Makefile.am | 1 | ||||
-rw-r--r-- | pcsound/pcsound.c | 7 | ||||
-rw-r--r-- | pcsound/pcsound_bsd.c | 315 |
4 files changed, 324 insertions, 1 deletions
diff --git a/configure.in b/configure.in index 9ed01939..3932fa3a 100644 --- a/configure.in +++ b/configure.in @@ -24,7 +24,7 @@ then CFLAGS="-O$OPT_LEVEL -g -Wall $orig_CFLAGS" fi -AC_CHECK_HEADERS([linux/kd.h]) +AC_CHECK_HEADERS([linux/kd.h dev/isa/spkrio.h]) AM_PATH_SDL(1.1.3) diff --git a/pcsound/Makefile.am b/pcsound/Makefile.am index 80ff01f8..63c76f77 100644 --- a/pcsound/Makefile.am +++ b/pcsound/Makefile.am @@ -5,6 +5,7 @@ noinst_LIBRARIES=libpcsound.a libpcsound_a_SOURCES = \ pcsound.c pcsound.h \ + pcsound_bsd.c \ pcsound_sdl.c \ pcsound_linux.c \ pcsound_win32.c \ diff --git a/pcsound/pcsound.c b/pcsound/pcsound.c index 8c739b35..3e6c3ae9 100644 --- a/pcsound/pcsound.c +++ b/pcsound/pcsound.c @@ -35,6 +35,10 @@ extern pcsound_driver_t pcsound_win32_driver; #endif +#ifdef HAVE_DEV_ISA_SPKRIO_H +extern pcsound_driver_t pcsound_bsd_driver; +#endif + #ifdef HAVE_LINUX_KD_H extern pcsound_driver_t pcsound_linux_driver; #endif @@ -46,6 +50,9 @@ static pcsound_driver_t *drivers[] = #ifdef HAVE_LINUX_KD_H &pcsound_linux_driver, #endif +#ifdef HAVE_DEV_ISA_SPKRIO_H + &pcsound_bsd_driver, +#endif #ifdef _WIN32 &pcsound_win32_driver, #endif diff --git a/pcsound/pcsound_bsd.c b/pcsound/pcsound_bsd.c new file mode 100644 index 00000000..00367d1f --- /dev/null +++ b/pcsound/pcsound_bsd.c @@ -0,0 +1,315 @@ +// 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 driver for [Open]BSD +// (Should be NetBSD as well, but untested). +// +//----------------------------------------------------------------------------- + +#include "config.h" + +#ifdef HAVE_DEV_ISA_SPKRIO_H + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <signal.h> +#include <dev/isa/spkrio.h> +#include <sys/ioctl.h> +#include <sys/signal.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <fcntl.h> + +#include "SDL.h" +#include "SDL_thread.h" + +#include "pcsound.h" +#include "pcsound_internal.h" + +#define SPEAKER_DEVICE "/dev/speaker" + +// +// This driver is far more complicated than it should be, because +// OpenBSD has sucky support for threads. Because multithreading +// is done in userspace, invoking the ioctl to make the speaker +// beep will lock all threads until the beep has completed. +// +// Thus, to get the beeping to occur in real-time, we must invoke +// the ioctl in a separate process. To do this, a separate +// sound server is forked that listens on a socket for tones to +// play. When a tone is received, a reply is sent back to the +// main process and the tone played. +// +// Meanwhile, back in the main process, there is a sound thread +// that runs, invoking the pcsound callback function to get +// more tones. This blocks on the sound server socket, waiting +// for replies. In this way, when the sound server finishes +// playing a tone, the next one is sent. +// +// This driver is a bit less accurate than the others, because +// we can only specify sound durations in 1/100ths of a second, +// as opposed to the normal millisecond durations. + +static pcsound_callback_func callback; +static int sound_server_pid; +static int sleep_adjust = 0; +static int sound_thread_running; +static SDL_Thread *sound_thread_handle; +static int sound_server_pipe[2]; + +// Play a sound, checking how long the system call takes to complete +// and autoadjusting for drift. + +static void AdjustedBeep(int speaker_handle, int ms, int freq) +{ + unsigned int start_time; + unsigned int end_time; + unsigned int actual_time; + tone_t tone; + + // Adjust based on previous error to keep the tempo right + + if (sleep_adjust > ms) + { + sleep_adjust -= ms; + return; + } + else + { + ms -= sleep_adjust; + } + + // Invoke the system call and time how long it takes + + start_time = SDL_GetTicks(); + + tone.duration = ms / 10; // in 100ths of a second + tone.frequency = freq; + + // Always a positive duration + + if (tone.duration < 1) + { + tone.duration = 1; + } + + if (ioctl(speaker_handle, SPKRTONE, &tone) != 0) + { + perror("ioctl"); + return; + } + + end_time = SDL_GetTicks(); + + if (end_time > start_time) + { + actual_time = end_time - start_time; + } + else + { + actual_time = ms; + } + + if (actual_time < ms) + { + actual_time = ms; + } + + // Save sleep_adjust for next time + + sleep_adjust = actual_time - ms; +} + +static void SoundServer(void) +{ + int speaker_handle; + tone_t tone; + int result; + + // Try to open the speaker device + + speaker_handle = open(SPEAKER_DEVICE, O_WRONLY); + + if (speaker_handle == -1) + { + // Don't have permissions for the console device? + + fprintf(stderr, "PCSound_BSD_Init: Failed to open '%s': %s\n", + SPEAKER_DEVICE, strerror(errno)); + return; + } + + // Run in a loop, invoking the callback + + for (;;) + { + result = read(sound_server_pipe[1], &tone, sizeof(tone_t)); + + if (result < 0) + { + perror("read"); + return; + } + + // Send back a response, so the main process knows to send another + + write(sound_server_pipe[1], &tone, sizeof(tone_t)); + + // Beep! (blocks until complete) + + AdjustedBeep(speaker_handle, tone.duration, tone.frequency); + } + + // Finished, close the handle + + close(speaker_handle); +} + +// Start up the sound server. Returns non-zero if successful. + +static int StartSoundServer(void) +{ + int result; + + // Create a pipe for communications + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sound_server_pipe) < 0) + { + perror("socketpair"); + return 0; + } + + // Start a separate process to generate PC speaker output + // We can't use the SDL threading functions because OpenBSD's + // threading sucks :-( + + result = fork(); + + if (result < 0) + { + fprintf(stderr, "Failed to fork sound server!\n"); + return 0; + } + else if (result == 0) + { + // This is the child (sound server) + + SoundServer(); + + exit(0); + } + else + { + // This is the parent + + sound_server_pid = result; + } + + return 1; +} + +static void StopSoundServer(void) +{ + int status; + + kill(sound_server_pid, SIGINT); + waitpid(sound_server_pid, &status, 0); +} + +static int SoundThread(void *unused) +{ + tone_t tone; + int duration; + int frequency; + + while (sound_thread_running) + { + // Get the next frequency to play + + callback(&duration, &frequency); + +//printf("dur: %i, freq: %i\n", duration, frequency); + + // Build up a tone structure and send to the sound server + + tone.frequency = frequency; + tone.duration = duration; + + if (write(sound_server_pipe[0], &tone, sizeof(tone_t)) < 0) + { + perror("write"); + break; + } + + // Wait until the sound server responds before sending another + + if (read(sound_server_pipe[0], &tone, sizeof(tone_t)) < 0) + { + perror("read"); + break; + } + } + + return 0; +} + +static int PCSound_BSD_Init(pcsound_callback_func callback_func) +{ + callback = callback_func; + + if (!StartSoundServer()) + { + fprintf(stderr, "PCSound_BSD_Init: Failed to start sound server.\n"); + return 0; + } + + sound_thread_running = 1; + sound_thread_handle = SDL_CreateThread(SoundThread, NULL); + + return 1; +} + +static void PCSound_BSD_Shutdown(void) +{ + // Stop the sound thread + + sound_thread_running = 0; + + SDL_WaitThread(sound_thread_handle, NULL); + + // Stop the sound server + + StopSoundServer(); +} + +pcsound_driver_t pcsound_bsd_driver = +{ + "BSD", + PCSound_BSD_Init, + PCSound_BSD_Shutdown, +}; + +#endif /* #ifdef HAVE_DEV_ISA_SPKRIO_H */ + |