Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include <unistd.h>
+
+// OSS module, used by Linux and FreeBSD
+
+#include "quakedef.h"
+
+#include <errno.h>
#include <fcntl.h>
-#include <stdlib.h>
-#include <sys/types.h>
#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/shm.h>
-#include <sys/wait.h>
#include <sys/soundcard.h>
-#include <stdio.h>
-#include "quakedef.h"
+#include <unistd.h>
-int audio_fd;
-int snd_inited;
+#include "snd_main.h"
-static int tryrates[] = {44100, 22050, 11025, 8000};
-qboolean SNDDMA_Init(void)
-{
- int rc;
- int fmt;
- int tmp;
- int i;
- char *s;
- struct audio_buf_info info;
- int caps;
- int format16bit;
-
-#if BYTE_ORDER == BIG_ENDIAN
- format16bit = AFMT_S16_BE;
-#else
- format16bit = AFMT_S16_LE;
-#endif
- snd_inited = 0;
-
- // open /dev/dsp, confirm capability to mmap, and get size of dma buffer
- audio_fd = open("/dev/dsp", O_RDWR);
- if (audio_fd < 0)
- {
- perror("/dev/dsp");
- Con_Print("Could not open /dev/dsp\n");
- return 0;
- }
+#define NB_FRAGMENTS 4
- if (ioctl(audio_fd, SNDCTL_DSP_RESET, 0) < 0)
- {
- perror("/dev/dsp");
- Con_Print("Could not reset /dev/dsp\n");
- close(audio_fd);
- return 0;
- }
+static int audio_fd = -1;
+static int old_osstime = 0;
+static unsigned int osssoundtime;
- if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps)==-1)
- {
- perror("/dev/dsp");
- Con_Print("Sound driver too old\n");
- close(audio_fd);
- return 0;
- }
- if (!(caps & DSP_CAP_TRIGGER) || !(caps & DSP_CAP_MMAP))
- {
- Con_Print("Sorry but your soundcard can't do this\n");
- close(audio_fd);
- return 0;
- }
+/*
+====================
+SndSys_Init
- if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info)==-1)
- {
- perror("GETOSPACE");
- Con_Print("Um, can't do GETOSPACE?\n");
- close(audio_fd);
- return 0;
- }
+Create "snd_renderbuffer" with the proper sound format if the call is successful
+May return a suggested format if the requested format isn't available
+====================
+*/
+qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
+{
+ int flags, ioctl_param, prev_value;
+ unsigned int fragmentsize;
- // set sample bits & speed
- s = getenv("QUAKE_SOUND_SAMPLEBITS");
- if (s)
- shm->format.width = atoi(s) / 8;
-// COMMANDLINEOPTION: -sndbits <bits> chooses 8 bit or 16 bit sound output
- else if ((i = COM_CheckParm("-sndbits")) != 0)
- shm->format.width = atoi(com_argv[i+1]) / 8;
+ Con_DPrint("SndSys_Init: using the OSS module\n");
- if (shm->format.width != 2 && shm->format.width != 1)
+ // Check the requested sound format
+ if (requested->width < 1 || requested->width > 2)
{
- ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &fmt);
- if (fmt & format16bit)
- shm->format.width = 2;
- else if (fmt & AFMT_U8)
- shm->format.width = 1;
- }
+ Con_Printf("SndSys_Init: invalid sound width (%hu)\n",
+ requested->width);
- s = getenv("QUAKE_SOUND_SPEED");
- if (s)
- shm->format.speed = atoi(s);
-// COMMANDLINEOPTION: -sndspeed <hz> chooses 44100 hz, 22100 hz, or 11025 hz sound output rate
- else if ((i = COM_CheckParm("-sndspeed")) != 0)
- shm->format.speed = atoi(com_argv[i+1]);
- else
- {
- for (i = 0;i < (int) sizeof(tryrates) / 4;i++)
- if (!ioctl(audio_fd, SNDCTL_DSP_SPEED, &tryrates[i]))
- break;
+ if (suggested != NULL)
+ {
+ memcpy(suggested, requested, sizeof(*suggested));
- shm->format.speed = tryrates[i];
+ if (requested->width < 1)
+ suggested->width = 1;
+ else
+ suggested->width = 2;
+ }
+
+ return false;
}
- s = getenv("QUAKE_SOUND_CHANNELS");
- if (s)
- shm->format.channels = atoi(s);
-// COMMANDLINEOPTION: -sndmono sets sound output to mono
- else if ((i = COM_CheckParm("-sndmono")) != 0)
- shm->format.channels = 1;
-// COMMANDLINEOPTION: -sndstereo sets sound output to stereo
- else if ((i = COM_CheckParm("-sndstereo")) != 0)
- shm->format.channels = 2;
- else
- shm->format.channels = 2;
-
- shm->samples = info.fragstotal * info.fragsize / shm->format.width;
-
- // memory map the dma buffer
- shm->bufferlength = info.fragstotal * info.fragsize;
- shm->buffer = (unsigned char *) mmap(NULL, shm->bufferlength, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, audio_fd, 0);
- if (!shm->buffer || shm->buffer == (unsigned char *)-1)
+ // Open /dev/dsp
+ audio_fd = open("/dev/dsp", O_WRONLY);
+ if (audio_fd < 0)
{
perror("/dev/dsp");
- Con_Print("Could not mmap /dev/dsp\n");
- close(audio_fd);
- return 0;
+ Con_Print("SndSys_Init: could not open /dev/dsp\n");
+ return false;
}
-
- tmp = 0;
- if (shm->format.channels == 2)
- tmp = 1;
-
- rc = ioctl(audio_fd, SNDCTL_DSP_STEREO, &tmp);
- if (rc < 0)
+
+ // Use non-blocking IOs if possible
+ flags = fcntl(audio_fd, F_GETFL);
+ if (flags != -1)
{
- perror("/dev/dsp");
- Con_Printf("Could not set /dev/dsp to stereo=%d\n", shm->format.channels);
- close(audio_fd);
- return 0;
+ if (fcntl(audio_fd, F_SETFL, flags | O_NONBLOCK) == -1)
+ Con_Print("SndSys_Init : fcntl(F_SETFL, O_NONBLOCK) failed!\n");
}
- if (tmp)
- shm->format.channels = 2;
else
- shm->format.channels = 1;
-
- rc = ioctl(audio_fd, SNDCTL_DSP_SPEED, &shm->format.speed);
- if (rc < 0)
+ Con_Print("SndSys_Init: fcntl(F_GETFL) failed!\n");
+
+ // Set the fragment size (up to "NB_FRAGMENTS" fragments of "fragmentsize" bytes)
+ fragmentsize = requested->speed * requested->channels * requested->width / 10;
+ fragmentsize = (unsigned int)ceilf((float)fragmentsize / (float)NB_FRAGMENTS);
+ fragmentsize = CeilPowerOf2(fragmentsize);
+ ioctl_param = (NB_FRAGMENTS << 16) | log2i(fragmentsize);
+ if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &ioctl_param) == -1)
{
- perror("/dev/dsp");
- Con_Printf("Could not set /dev/dsp speed to %d\n", shm->format.speed);
- close(audio_fd);
- return 0;
+ Con_Print ("SndSys_Init: could not set the fragment size\n");
+ SndSys_Shutdown ();
+ return false;
}
+ Con_Printf ("SndSys_Init: using %u fragments of %u bytes\n",
+ ioctl_param >> 16, 1 << (ioctl_param & 0xFFFF));
- if (shm->format.width == 2)
+ // Set the sound width
+ if (requested->width == 1)
+ ioctl_param = AFMT_U8;
+ else
+ ioctl_param = AFMT_S16_NE;
+ prev_value = ioctl_param;
+ if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &ioctl_param) == -1 ||
+ ioctl_param != prev_value)
{
- rc = format16bit;
- rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc);
- if (rc < 0)
+ if (ioctl_param != prev_value && suggested != NULL)
{
- perror("/dev/dsp");
- Con_Print("Could not support 16-bit data. Try 8-bit.\n");
- close(audio_fd);
- return 0;
+ memcpy(suggested, requested, sizeof(*suggested));
+
+ if (ioctl_param == AFMT_S16_NE)
+ suggested->width = 2;
+ else
+ suggested->width = 1;
}
+
+ Con_Printf("SndSys_Init: could not set the sound width to %hu\n",
+ requested->width);
+ SndSys_Shutdown();
+ return false;
}
- else if (shm->format.width == 1)
+
+ // Set the sound channels
+ ioctl_param = requested->channels;
+ if (ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &ioctl_param) == -1 ||
+ ioctl_param != requested->channels)
{
- rc = AFMT_U8;
- rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc);
- if (rc < 0)
+ if (ioctl_param != requested->channels && suggested != NULL)
{
- perror("/dev/dsp");
- Con_Print("Could not support 8-bit data.\n");
- close(audio_fd);
- return 0;
+ memcpy(suggested, requested, sizeof(*suggested));
+ suggested->channels = ioctl_param;
}
+
+ Con_Printf("SndSys_Init: could not set the number of channels to %hu\n",
+ requested->channels);
+ SndSys_Shutdown();
+ return false;
}
- else
+
+ // Set the sound speed
+ ioctl_param = requested->speed;
+ if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &ioctl_param) == -1 ||
+ (unsigned int)ioctl_param != requested->speed)
{
- perror("/dev/dsp");
- Con_Printf("%d-bit sound not supported.\n", shm->format.width * 8);
- close(audio_fd);
- return 0;
+ if ((unsigned int)ioctl_param != requested->speed && suggested != NULL)
+ {
+ memcpy(suggested, requested, sizeof(*suggested));
+ suggested->speed = ioctl_param;
+ }
+
+ Con_Printf("SndSys_Init: could not set the sound speed to %u\n",
+ requested->speed);
+ SndSys_Shutdown();
+ return false;
}
- // toggle the trigger & start her up
- tmp = 0;
- rc = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp);
- if (rc < 0)
+ // TOCHECK: I'm not sure which channel layout OSS uses for 5.1 and 7.1
+ if (snd_channellayout.integer == SND_CHANNELLAYOUT_AUTO)
+ Cvar_SetValueQuick (&snd_channellayout, SND_CHANNELLAYOUT_ALSA);
+
+ old_osstime = 0;
+ osssoundtime = 0;
+ snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL);
+ return true;
+}
+
+
+/*
+====================
+SndSys_Shutdown
+
+Stop the sound card, delete "snd_renderbuffer" and free its other resources
+====================
+*/
+void SndSys_Shutdown (void)
+{
+ // Stop the sound and close the device
+ if (audio_fd >= 0)
{
- perror("/dev/dsp");
- Con_Print("Could not toggle.\n");
+ ioctl(audio_fd, SNDCTL_DSP_RESET, NULL);
close(audio_fd);
- return 0;
+ audio_fd = -1;
}
- tmp = PCM_ENABLE_OUTPUT;
- rc = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp);
- if (rc < 0)
+
+ if (snd_renderbuffer != NULL)
{
- perror("/dev/dsp");
- Con_Print("Could not toggle.\n");
- close(audio_fd);
- return 0;
+ Mem_Free(snd_renderbuffer->ring);
+ Mem_Free(snd_renderbuffer);
+ snd_renderbuffer = NULL;
}
-
- shm->samplepos = 0;
-
- snd_inited = 1;
- return 1;
}
-int SNDDMA_GetDMAPos(void)
+
+/*
+====================
+SndSys_Write
+====================
+*/
+static int SndSys_Write (const unsigned char* buffer, unsigned int nb_bytes)
{
+ int written;
+ unsigned int factor;
- struct count_info count;
+ written = write (audio_fd, buffer, nb_bytes);
+ if (written < 0)
+ {
+ if (errno != EAGAIN)
+ Con_Printf ("SndSys_Write: audio write returned %d! (errno= %d)\n",
+ written, errno);
+ return written;
+ }
+
+ factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
+ if (written % factor != 0)
+ Sys_Error ("SndSys_Write: nb of bytes written (%d) isn't aligned to a frame sample!\n",
+ written);
- if (!snd_inited) return 0;
+ snd_renderbuffer->startframe += written / factor;
- if (ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &count)==-1)
+ if ((unsigned int)written < nb_bytes)
{
- perror("/dev/dsp");
- Con_Print("Uh, sound dead.\n");
- close(audio_fd);
- snd_inited = 0;
- return 0;
+ Con_DPrintf("SndSys_Submit: audio can't keep up! (%u < %u)\n",
+ written, nb_bytes);
}
- shm->samplepos = count.ptr / shm->format.width;
- return shm->samplepos;
+ return written;
}
-void SNDDMA_Shutdown(void)
+
+/*
+====================
+SndSys_Submit
+
+Submit the contents of "snd_renderbuffer" to the sound card
+====================
+*/
+void SndSys_Submit (void)
{
- int tmp;
- if (snd_inited)
+ unsigned int startoffset, factor, limit, nbframes;
+ int written;
+
+ if (audio_fd < 0 ||
+ snd_renderbuffer->startframe == snd_renderbuffer->endframe)
+ return;
+
+ startoffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes;
+ factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
+ limit = snd_renderbuffer->maxframes - startoffset;
+ nbframes = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
+ if (nbframes > limit)
{
- // unmap the memory
- munmap(shm->buffer, shm->bufferlength);
- // stop the sound
- tmp = 0;
- ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp);
- ioctl(audio_fd, SNDCTL_DSP_RESET, 0);
- // close the device
- close(audio_fd);
- audio_fd = -1;
- snd_inited = 0;
+ written = SndSys_Write (&snd_renderbuffer->ring[startoffset * factor], limit * factor);
+ if (written < 0 || (unsigned int)written < limit * factor)
+ return;
+
+ nbframes -= limit;
+ startoffset = 0;
}
+
+ SndSys_Write (&snd_renderbuffer->ring[startoffset * factor], nbframes * factor);
}
+
/*
-==============
-SNDDMA_Submit
+====================
+SndSys_GetSoundTime
-Send sound to device if buffer isn't really the dma buffer
-===============
+Returns the number of sample frames consumed since the sound started
+====================
*/
-void SNDDMA_Submit(void)
+unsigned int SndSys_GetSoundTime (void)
{
+ struct count_info count;
+ int new_osstime;
+ unsigned int timediff;
+
+ if (ioctl (audio_fd, SNDCTL_DSP_GETOPTR, &count) == -1)
+ {
+ Con_Print ("SndSys_GetSoundTimeDiff: can't ioctl (SNDCTL_DSP_GETOPTR)\n");
+ return 0;
+ }
+ new_osstime = count.bytes / (snd_renderbuffer->format.width * snd_renderbuffer->format.channels);
+
+ if (new_osstime >= old_osstime)
+ timediff = new_osstime - old_osstime;
+ else
+ {
+ Con_Print ("SndSys_GetSoundTime: osstime wrapped\n");
+ timediff = 0;
+ }
+
+ old_osstime = new_osstime;
+ osssoundtime += timediff;
+ return osssoundtime;
}
-void *S_LockBuffer(void)
+
+/*
+====================
+SndSys_LockRenderBuffer
+
+Get the exclusive lock on "snd_renderbuffer"
+====================
+*/
+qboolean SndSys_LockRenderBuffer (void)
{
- return shm->buffer;
+ // Nothing to do
+ return true;
}
-void S_UnlockBuffer(void)
+
+/*
+====================
+SndSys_UnlockRenderBuffer
+
+Release the exclusive lock on "snd_renderbuffer"
+====================
+*/
+void SndSys_UnlockRenderBuffer (void)
{
+ // Nothing to do
}
+/*
+====================
+SndSys_SendKeyEvents
+
+Send keyboard events originating from the sound system (e.g. MIDI)
+====================
+*/
+void SndSys_SendKeyEvents(void)
+{
+ // not supported
+}