This change rewrites and simplifies the copyright headers at the top of all source files: * Remove "Emacs style mode select" line; this line was included in the headers for the originally released source files and appears to be to set the file type for old versions of Emacs. I'm not sure entirely why it was required but I don't think it is any more. * Remove "You should have received a copy of..." text from copyright header. This refers to the old 59 Temple Place address where the FSF headquarters used to be located and is no longer correct. Rather than change to the new address, just remove the paragraph as it is superfluous anyway. This fixes #311. * Remove ---- separator lines so that the file headers are barer and more simplified.
320 lines
7 KiB
C
320 lines
7 KiB
C
//
|
|
// Copyright(C) 2005-2014 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.
|
|
//
|
|
// DESCRIPTION:
|
|
// PC speaker driver for [Open]BSD
|
|
// (Should be NetBSD as well, but untested).
|
|
//
|
|
|
|
#include "config.h"
|
|
|
|
// OpenBSD/NetBSD:
|
|
|
|
#ifdef HAVE_DEV_ISA_SPKRIO_H
|
|
#define HAVE_BSD_SPEAKER
|
|
#include <dev/isa/spkrio.h>
|
|
#endif
|
|
|
|
// FreeBSD
|
|
|
|
#ifdef HAVE_DEV_SPEAKER_SPEAKER_H
|
|
#define HAVE_BSD_SPEAKER
|
|
#include <dev/speaker/speaker.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_BSD_SPEAKER
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <signal.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(int speaker_handle)
|
|
{
|
|
tone_t tone;
|
|
int result;
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
// Start up the sound server. Returns non-zero if successful.
|
|
|
|
static int StartSoundServer(void)
|
|
{
|
|
int result;
|
|
int speaker_handle;
|
|
|
|
// 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, "StartSoundServer: Failed to open '%s': %s\n",
|
|
SPEAKER_DEVICE, strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
// Create a pipe for communications
|
|
|
|
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sound_server_pipe) < 0)
|
|
{
|
|
perror("socketpair");
|
|
close(speaker_handle);
|
|
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");
|
|
close(speaker_handle);
|
|
return 0;
|
|
}
|
|
else if (result == 0)
|
|
{
|
|
// This is the child (sound server)
|
|
|
|
SoundServer(speaker_handle);
|
|
close(speaker_handle);
|
|
|
|
exit(0);
|
|
}
|
|
else
|
|
{
|
|
// This is the parent
|
|
|
|
sound_server_pid = result;
|
|
close(speaker_handle);
|
|
}
|
|
|
|
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_BSD_SPEAKER */
|
|
|