X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=snd_oss.c;h=9926b800642fce99d9e6c569a34668b969e8945e;hp=9e1711af33f816f2661c7ea7ddbda6e4677f21f1;hb=c9b9e2d6bd061e255c0418b44f8ec3e251642c16;hpb=9c8545528c5bc812c83d01ff96b19d13bc053010 diff --git a/snd_oss.c b/snd_oss.c index 9e1711af..9926b800 100644 --- a/snd_oss.c +++ b/snd_oss.c @@ -20,262 +20,325 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // OSS module, used by Linux and FreeBSD -#include +#include "quakedef.h" + +#include #include -#include -#include #include -#include -#include -#include #include -#include -#include "quakedef.h" +#include + #include "snd_main.h" -int audio_fd; -static int tryrates[] = {44100, 22050, 11025, 8000}; +#define NB_FRAGMENTS 4 -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 - - // open /dev/dsp, confirm capability to mmap, and get size of dma buffer - audio_fd = open("/dev/dsp", O_RDWR); // we have to open it O_RDWR for mmap - if (audio_fd < 0) - { - perror("/dev/dsp"); - Con_Print("Could not open /dev/dsp\n"); - return 0; - } - - 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: Linux OSS Sound: -sndbits 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: Linux OSS Sound: -sndspeed 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) / (int) sizeof(tryrates[0]);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: Linux OSS Sound: -sndmono sets sound output to mono - else if ((i = COM_CheckParm("-sndmono")) != 0) - shm->format.channels = 1; -// COMMANDLINEOPTION: Linux OSS Sound: -sndstereo sets sound output to stereo - else // if ((i = COM_CheckParm("-sndstereo")) != 0) - shm->format.channels = 2; - - tmp = (shm->format.channels == 2); - rc = ioctl(audio_fd, SNDCTL_DSP_STEREO, &tmp); - if (rc < 0) + // Open /dev/dsp + audio_fd = open("/dev/dsp", O_WRONLY); + if (audio_fd < 0) { perror("/dev/dsp"); - Con_Printf("Could not set /dev/dsp to stereo=%d\n", tmp); - close(audio_fd); - return 0; + Con_Print("SndSys_Init: could not open /dev/dsp\n"); + return false; } - - rc = ioctl(audio_fd, SNDCTL_DSP_SPEED, &shm->format.speed); - 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 speed to %d\n", shm->format.speed); - 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"); + } + else + 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) + { + 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; } - shm->sampleframes = info.fragstotal * info.fragsize / shm->format.width / shm->format.channels; - shm->samples = shm->sampleframes * shm->format.channels; + // 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 - // memory map the dma buffer - shm->bufferlength = info.fragstotal * info.fragsize; - shm->buffer = (unsigned char *) mmap(NULL, shm->bufferlength, PROT_WRITE, MAP_FILE|MAP_SHARED, audio_fd, 0); - if (!shm->buffer || shm->buffer == (unsigned char *)-1) +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 mmap /dev/dsp\n"); + ioctl(audio_fd, SNDCTL_DSP_RESET, NULL); close(audio_fd); - return 0; + audio_fd = -1; } - // toggle the trigger & start her up - tmp = 0; - 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; } - tmp = PCM_ENABLE_OUTPUT; - rc = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp); - if (rc < 0) +} + + +/* +==================== +SndSys_Write +==================== +*/ +static int SndSys_Write (const unsigned char* buffer, unsigned int nb_bytes) +{ + int written; + unsigned int factor; + + written = write (audio_fd, buffer, nb_bytes); + if (written < 0) { - perror("/dev/dsp"); - Con_Print("Could not toggle.\n"); - close(audio_fd); - return 0; + if (errno != EAGAIN) + Con_Printf ("SndSys_Write: audio write returned %d! (errno= %d)\n", + written, errno); + return written; } - shm->samplepos = 0; + 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); + + snd_renderbuffer->startframe += written / factor; + + if ((unsigned int)written < nb_bytes) + { + Con_DPrintf("SndSys_Submit: audio can't keep up! (%u < %u)\n", + written, nb_bytes); + } - return 1; + return written; } -int SNDDMA_GetDMAPos(void) + +/* +==================== +SndSys_Submit + +Submit the contents of "snd_renderbuffer" to the sound card +==================== +*/ +void SndSys_Submit (void) { + 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) + { + written = SndSys_Write (&snd_renderbuffer->ring[startoffset * factor], limit * factor); + if (written < 0 || (unsigned int)written < limit * factor) + return; + + nbframes -= limit; + startoffset = 0; + } - struct count_info count; + SndSys_Write (&snd_renderbuffer->ring[startoffset * factor], nbframes * factor); +} - if (!shm) return 0; - if (ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &count)==-1) +/* +==================== +SndSys_GetSoundTime + +Returns the number of sample frames consumed since the sound started +==================== +*/ +unsigned int SndSys_GetSoundTime (void) +{ + struct count_info count; + int new_osstime; + unsigned int timediff; + + if (ioctl (audio_fd, SNDCTL_DSP_GETOPTR, &count) == -1) { - perror("/dev/dsp"); - Con_Print("Uh, sound dead.\n"); - S_Shutdown(); + Con_Print ("SndSys_GetSoundTimeDiff: can't ioctl (SNDCTL_DSP_GETOPTR)\n"); return 0; } - shm->samplepos = count.ptr / shm->format.width; + new_osstime = count.bytes / (snd_renderbuffer->format.width * snd_renderbuffer->format.channels); - return shm->samplepos; -} + if (new_osstime >= old_osstime) + timediff = new_osstime - old_osstime; + else + { + Con_Print ("SndSys_GetSoundTime: osstime wrapped\n"); + timediff = 0; + } -void SNDDMA_Shutdown(void) -{ - int tmp; - // 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; + old_osstime = new_osstime; + osssoundtime += timediff; + return osssoundtime; } + /* -============== -SNDDMA_Submit +==================== +SndSys_LockRenderBuffer -Send sound to device if buffer isn't really the dma buffer -=============== +Get the exclusive lock on "snd_renderbuffer" +==================== */ -void SNDDMA_Submit(void) +qboolean SndSys_LockRenderBuffer (void) { + // Nothing to do + return true; } -void *S_LockBuffer(void) + +/* +==================== +SndSys_UnlockRenderBuffer + +Release the exclusive lock on "snd_renderbuffer" +==================== +*/ +void SndSys_UnlockRenderBuffer (void) { - return shm->buffer; + // Nothing to do } -void S_UnlockBuffer(void) +/* +==================== +SndSys_SendKeyEvents + +Send keyboard events originating from the sound system (e.g. MIDI) +==================== +*/ +void SndSys_SendKeyEvents(void) { + // not supported } -