/* * sound_oss.c - Open Sound System driver * * Copyright (C) 1995-1998 David Firth * Copyright (C) 1998-2015 Atari800 development team (see DOC/CREDITS) * * This file is part of the Atari800 emulator project which emulates * the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers. * * Atari800 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. * * Atari800 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 Atari800; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include /* XXX: #include */ #include "atari.h" #include "log.h" #include "platform.h" #include "sound.h" #define DEBUG 0 #if defined (__NetBSD__) || defined (__OpenBSD__) static const char * const dspname = "/dev/audio"; #else static const char * const dspname = "/dev/dsp"; #endif static int dsp_fd; /* When opening an OSS audio device, we don't limit number of sound fragments that OSS creates. To minimise latency resulting from too many fragments, we take advantage of the fact that OSS usually starts playback after fully filling 2 fragments, and ensure that we never have more filled fragments than MAX_FILLED_FRAGMENTS (which is <> 2 for some additional headroom). */ enum { MAX_FILLED_FRAGMENTS = 4 }; int PLATFORM_SoundSetup(Sound_setup_t *setup) { int format; int frag_size; int setfragment; if (Sound_enabled) close(dsp_fd); dsp_fd = open(dspname, O_WRONLY); if (dsp_fd == -1) { perror(dspname); return FALSE; } if (setup->buffer_frames == 0) /* Set buffer_frames automatically. */ frag_size = setup->freq / 50; else frag_size = setup->buffer_frames; frag_size *= setup->channels * setup->sample_size; /* By setting number of fragments to 0x7fff (ie. don't limit) we ensure that the obtained fragment size will be as close to the requested value as possible. */ setfragment = 0x7fff0000; { /* Compute the closest power of two. */ int pow_val = 1; while (pow_val <= frag_size) { pow_val <<= 1; ++setfragment; } } if (ioctl(dsp_fd, SNDCTL_DSP_SETFRAGMENT, &setfragment) == -1) { Log_print("%s: SNDCTL_DSP_SETFRAGMENT(%.8x) failed", dspname, setfragment); close(dsp_fd); return FALSE; } format = setup->sample_size == 2 ? AFMT_S16_NE : AFMT_U8; if (ioctl(dsp_fd, SNDCTL_DSP_SETFMT, &format) == -1) { Log_print("%s: SNDCTL_DSP_SETFMT(%i) failed", dspname, format); close(dsp_fd); return FALSE; } if (format == AFMT_S16_NE) setup->sample_size = 2; else if (format == AFMT_U8) setup->sample_size = 1; else { Log_print("%s: Obtained format %i not supported", dspname, format); close(dsp_fd); return FALSE; } if (ioctl(dsp_fd, SNDCTL_DSP_CHANNELS, &setup->channels) == -1) { Log_print("%s: SNDCTL_DSP_CHANNELS(%u) failed", dspname, setup->channels); close(dsp_fd); return FALSE; } if (ioctl(dsp_fd, SNDCTL_DSP_SPEED, &setup->freq) == -1) { Log_print("%s: SNDCTL_DSP_SPEED(%u) failed", dspname, setup->freq); close(dsp_fd); return FALSE; } if (ioctl(dsp_fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) == -1) { Log_print("%s: SNDCTL_DSP_GETBLKSIZE failed", dspname); close(dsp_fd); return FALSE; } setup->buffer_frames = frag_size / setup->channels / setup->sample_size; { audio_buf_info bi; if (ioctl(dsp_fd, SNDCTL_DSP_GETOSPACE, &bi) == -1) { Log_print("%s: cannot retrieve ospace", dspname); return 0; } #if DEBUG Log_print("fragments=%i, fragstotal=%i, fragsize=%i, bytes=%i", bi.fragments, bi.fragstotal, bi.fragsize, bi.bytes); Log_print("frag_size=%i, buf_Frames=%u", frag_size, setup->buffer_frames); #endif } return TRUE; } void PLATFORM_SoundExit(void) { close(dsp_fd); } void PLATFORM_SoundPause(void) { /* flush buffers */ ioctl(dsp_fd, SNDCTL_DSP_POST, NULL); } void PLATFORM_SoundContinue(void) { /* do nothing */ } unsigned int PLATFORM_SoundAvailable(void) { audio_buf_info bi; int filled_frags; enum { MAX_FILLED_FRAGMENTS = 4 }; if (ioctl(dsp_fd, SNDCTL_DSP_GETOSPACE, &bi) == -1) { Log_print("%s: cannot retrieve ospace", dspname); return 0; } #if DEBUG Log_print("fragments=%i, fragstotal=%i, fragsize=%i, bytes=%i", bi.fragments, bi.fragstotal, bi.fragsize, bi.bytes);*/ #endif /* Usually OSS playback starts when 2 fragments are fully filled. Take advantage of it: write audio only if at most MAX_FILLED_FRAGS fragments are filled, to minimize latency regardless of actual total number of fragments. */ filled_frags = bi.fragstotal - bi.fragments; if (filled_frags <= MAX_FILLED_FRAGMENTS) return bi.fragsize * (MAX_FILLED_FRAGMENTS - filled_frags); else if (bi.fragstotal <= MAX_FILLED_FRAGMENTS) return bi.bytes; else return 0; } void PLATFORM_SoundWrite(UBYTE const *buffer, unsigned int size) { int wsize = write(dsp_fd, buffer, size); if (wsize < size) { /* TODO: handle problem */ } }