/* $Id: audio_voxware.c,v 1.24 1999/11/17 13:16:49 daeron Exp $ */ #ifdef HAVE_SYS_SOUNDCARD_H # include #else # ifdef HAVE_SOUNDCARD_H # include # else # include # endif #endif Bool NO_AFMT_S8 = False; static int openAudioDevice(char *audiodev, int mode); static int closeAudioDevice(int audiofd); static int resetAudioDevice(int audiofd); static int initAudioDevice(int audiofd, SAudioFileInfo *afInfo); static int initWAVE(int audiofd, SAudioFileInfo *afInfo); static int initAIFF(int audiofd, SAudioFileInfo *afInfo); static int initAUSND(int audiofd, SAudioFileInfo *afInfo); static int initAFMT_U8(int audiofd, SAudioFileInfo *afInfo); static int initAFMT_S8(int audiofd, SAudioFileInfo *afInfo); static int initAFMT_S16_LE(int audiofd, SAudioFileInfo *afInfo); static int initAFMT_S16_BE(int audiofd, SAudioFileInfo *afInfo); static int initStereo(int audiofd, SAudioFileInfo *afInfo); static int initSampleRate(int audiofd, SAudioFileInfo *afInfo); static int writeAudioData(int audiofd, SAudioFileInfo *afInfo); static int write8bitAudioData(int audiofd, SAudioFileInfo *afInfo); static int write16bitAudioData(int audiofd, SAudioFileInfo *afInfo); int SPerformAudio(SAudioFileInfo *afInfo) { int audiofd; /* Open audio device */ audiofd = openAudioDevice(SGetStringForKey(S_DEVICE), O_WRONLY); if (audiofd == -1) return -1; /* Play with audio device */ if (initAudioDevice(audiofd, afInfo) == -1) { closeAudioDevice(audiofd); SDestroyAudioFileInfo(afInfo); return -1; } /* Clean up */ closeAudioDevice(audiofd); SDestroyAudioFileInfo(afInfo); return 0; } /* Open audio device */ static int openAudioDevice(char *audiodev, int mode) { int audiofd; assert(audiodev); audiofd = open(audiodev, mode, 0); if (audiofd == -1) { SErrorCode = SERR_DEVOPEN; return -1; } #ifdef DEBUG else fprintf(stderr, "-=> audio device opened\n"); #endif return audiofd; } /* Close audio device */ static int closeAudioDevice(int audiofd) { assert(audiofd > 0); if (close(audiofd) == -1) { SErrorCode = SERR_DEVCLOSE; return -1; } #ifdef DEBUG else fprintf(stderr, "<=- audio device closed\n"); #endif return 0; } /* Reset audio device */ static int resetAudioDevice(int audiofd) { assert(audiofd > 0); if (ioctl(audiofd, SNDCTL_DSP_RESET) == -1) { perror("SNDCTL_DSP_RESET"); SErrorCode = SERR_DEVRESET; return -1; } #ifdef DEBUG fprintf(stderr, " >> audio device resetted\n"); #endif return 0; } /* Initialize audio device for sound playback */ static int initAudioDevice(int audiofd, SAudioFileInfo *afInfo) { #ifdef DEBUG fprintf(stderr, " >> initializing audio device\n"); #endif if (resetAudioDevice(audiofd) == -1) { return -1; } switch (afInfo->FileFormat) { case AF_FILE_AIFF: return initAIFF(audiofd, afInfo); case AF_FILE_WAVE: return initWAVE(audiofd, afInfo); case AF_FILE_NEXTSND: return initAUSND(audiofd, afInfo); default: SErrorCode = SERR_BADFORMAT; return -1; } } /* ---------------------------------------------- */ static int initWAVE(int audiofd, SAudioFileInfo *afInfo) { int endianness; endianness = SGetEndianness(); if (afInfo->SampleWidth == 8) return (initAFMT_U8(audiofd, afInfo)); if ((afInfo->SampleWidth == 16) && (endianness == LITTLEENDIAN)) return (initAFMT_S16_LE(audiofd, afInfo)); if ((afInfo->SampleWidth == 16) && (endianness == BIGENDIAN)) return (initAFMT_S16_BE(audiofd, afInfo)); else { SErrorCode = SERR_BADFORMAT; return -1; } } static int initAIFF(int audiofd, SAudioFileInfo *afInfo) { int endianness; endianness = SGetEndianness(); if (afInfo->SampleWidth == 8) { return (initAFMT_U8(audiofd, afInfo)); } if ((afInfo->SampleWidth == 16) && (endianness == LITTLEENDIAN)) return (initAFMT_S16_LE(audiofd, afInfo)); if ((afInfo->SampleWidth == 16) && (endianness == BIGENDIAN)) return (initAFMT_S16_BE(audiofd, afInfo)); else { SErrorCode = SERR_BADFORMAT; return -1; } } static int initAUSND(int audiofd, SAudioFileInfo *afInfo) { int endianness; endianness = SGetEndianness(); if (afInfo->SampleWidth == 8) { return (initAFMT_U8(audiofd, afInfo)); } if ((afInfo->SampleWidth == 16) && (endianness == LITTLEENDIAN)) return (initAFMT_S16_LE(audiofd, afInfo)); if ((afInfo->SampleWidth == 16) && (endianness == BIGENDIAN)) return (initAFMT_S16_BE(audiofd, afInfo)); else { SErrorCode = SERR_BADFORMAT; return -1; } } static int initAFMT_U8(int audiofd, SAudioFileInfo *afInfo) { int format; format = AFMT_U8; if (ioctl(audiofd, SNDCTL_DSP_SETFMT, &format) == -1) { perror("SNDCTL_DSP_SETFMT"); SErrorCode = SERR_BADFORMAT; return -1; } if (format != AFMT_U8) { SErrorCode = SERR_DEVSUPPORT; return -1; } #ifdef DEBUG fprintf(stderr, " >> SNDCTL_DSP_SETFMT AFMT_U8\n"); #endif if ((initStereo(audiofd, afInfo) == -1) || (initSampleRate(audiofd, afInfo) == -1)) return -1; else return writeAudioData(audiofd, afInfo); } static int initAFMT_S8(int audiofd, SAudioFileInfo *afInfo) { int format; format = AFMT_S8; if (ioctl(audiofd, SNDCTL_DSP_SETFMT, &format) == -1) { perror("SNDCTL_DSP_SETFMT"); SErrorCode = SERR_BADFORMAT; return -1; } if (format != AFMT_S8) { NO_AFMT_S8 = True; if (format != AFMT_U8) { SErrorCode = SERR_DEVSUPPORT; return -1; } } #ifdef DEBUG if (format == AFMT_S8) fprintf(stderr, " >> SNDCTL_DSP_SETFMT AFMT_S8\n"); else fprintf(stderr, " >> SNDCTL_DSP_SETFMT AFMT_S8 unsupported ... using AFMT_U8\n"); #endif if ((initStereo(audiofd, afInfo) == -1) || (initSampleRate(audiofd, afInfo) == -1)) return -1; else return writeAudioData(audiofd, afInfo); } static int initAFMT_S16_LE(int audiofd, SAudioFileInfo *afInfo) { int format; format = AFMT_S16_LE; if (ioctl(audiofd, SNDCTL_DSP_SETFMT, &format) == -1) { perror("SNDCTL_DSP_SETFMT"); SErrorCode = SERR_BADFORMAT; return -1; } if (format != AFMT_S16_LE) { SErrorCode = SERR_DEVSUPPORT; return -1; } #ifdef DEBUG fprintf(stderr, " >> SNDCTL_DSP_SETFMT AFMT_S16_LE\n"); #endif if ((initStereo(audiofd, afInfo) == -1) || (initSampleRate(audiofd, afInfo) == -1)) return -1; else return writeAudioData(audiofd, afInfo); } static int initAFMT_S16_BE(int audiofd, SAudioFileInfo *afInfo) { int format; format = AFMT_S16_BE; if (ioctl(audiofd, SNDCTL_DSP_SETFMT, &format) == -1) { perror("SNDCTL_DSP_SETFMT"); SErrorCode = SERR_BADFORMAT; return -1; } if (format != AFMT_S16_BE) { SErrorCode = SERR_DEVSUPPORT; return -1; } #ifdef DEBUG fprintf(stderr, " >> SNDCTL_DSP_SETFMT AFMT_S16_BE\n"); #endif if ((initStereo(audiofd, afInfo) == -1) || (initSampleRate(audiofd, afInfo) == -1)) return -1; else return writeAudioData(audiofd, afInfo); } static int initStereo(int audiofd, SAudioFileInfo *afInfo) { int stereo = afInfo->Channels - 1; /* 0 == mono == 1 channel * 1 == stereo == 2 channels */ if (ioctl(audiofd, SNDCTL_DSP_STEREO, &stereo) == -1) { perror("SNDCTL_DSP_STEREO"); SErrorCode = SERR_DEVSTEREO; return -1; } if (stereo != (afInfo->Channels - 1)) { SErrorCode = SERR_DEVSTEREO; return -1; } #ifdef DEBUG if (afInfo->Channels == 2) fprintf(stderr, " >> SNDCTL_DSP_STEREO stereo\n"); else fprintf(stderr, " >> SNDCTL_DSP_STEREO mono\n"); #endif return 0; } static int initSampleRate(int audiofd, SAudioFileInfo *afInfo) { int speed = (int)afInfo->SampleRate; if (ioctl(audiofd, SNDCTL_DSP_SPEED, &speed) == -1) { perror("SNDCTL_DSP_SPEED"); SErrorCode = SERR_DEVSPEED; return -1; } if (0) { /* TODO: check for extreme difference between returned and requested speeds */ SErrorCode = SERR_DEVSPEED; return -1; } #ifdef DEBUG fprintf(stderr, " >> SNDCTL_DSP_SPEED %d Hz\n", speed); #endif return 0; } static int writeAudioData(int audiofd, SAudioFileInfo *afInfo) { if (afInfo->SampleWidth == 8) return (write8bitAudioData(audiofd, afInfo)); else return (write16bitAudioData(audiofd, afInfo)); } static int write8bitAudioData(int audiofd, SAudioFileInfo *afInfo) { u_int8_t *buffer; /* audio buffer */ long curFrame; /* current framecount */ long blkFrames; /* number of frames in current audio block */ int blockSize = 8192; /* Size of an audio block buffer in frames */ int i; u_int8_t fakebuffer[2] = {0, 0}; #ifdef DEBUG fprintf(stderr, " >> writing data\n"); #endif buffer = (u_int8_t *) malloc( blockSize * (afInfo->SampleWidth/8) * afInfo->Channels * sizeof(u_int8_t)); if (!buffer) { SErrorCode = SERR_NOMEMORY; return -1; } /* Following line is a simple workarround for buggy pcm driver, which seens need at least to write() calls for sound to be actually played */ write(audiofd, fakebuffer, 2); curFrame = 0; while (curFrame < afInfo->FrameCount) { if ((blkFrames = (afInfo->FrameCount - curFrame)) > blockSize) { blkFrames = blockSize; } if (afReadFrames(afInfo->FileHandle, AF_DEFAULT_TRACK, buffer, blkFrames) < 1) { #ifdef DEBUG fprintf(stderr, " >> frames written %ld\n", curFrame); #endif free(buffer); SErrorCode = SERR_READ; return -1; } /* XXX LibAudioFile internally translates everything to signed integer if your audio hardware doesn't support that (like my GUS MAX) we need to convert it back to unsigned UPDATE: Currently this is not the case and NO_AFMT_S8 will never be set. Leaving the code in here in case things change again. */ if (NO_AFMT_S8) { for (i = 0; i < (blkFrames*afInfo->Channels); i++) buffer[i] ^= 0x80; } /*TODO: * Make the volume a soundeffect * Change sound into struct with: * - sound file to play * - effects to apply * - number of effects to apply */ /* Adjust Volume */ if ((SGetVolume() < 1.0) /*TODO || (sound->effectCount > 0) */ ) { for (i = 0; i < (blkFrames*afInfo->Channels); i++) { int j; int x; /* The format for 8-bit unsigned audio is * different from 2-complement !!! */ x = buffer[i] - 128; x *= SGetVolume(); /* TODO (for sound effects) * * Functions needed available: * - SOpenStream -> Open an audio stream that will be mixed later on. * - SGetSampleRate -> Sample rate is required for timing. * - SCreatePool -> This way it is clear that the memory 'pool' needs mallocing. * - SDestroyPool -> ...and freeing. * for (j=0; j <= sound->effectCount; j++) { (SEffect8*)(sound->effects[j]->effect->e_8bit(buffer, i, sound->effects[j]->effectParameters), sound->effects[j]->pool); } */ buffer[i] = x + 128.5; } } if (write(audiofd, buffer, blkFrames * (afInfo->SampleWidth/8) * afInfo->Channels) == -1) { free(buffer); SErrorCode = SERR_DEVWRITE; return -1; } curFrame += blkFrames; } #ifdef DEBUG fprintf(stderr, " >> frames written %ld\n", curFrame); #endif free(buffer); return 0; } static int write16bitAudioData(int audiofd, SAudioFileInfo *afInfo) { u_int16_t *buffer; /* audio buffer */ long curFrame; /* current framecount */ long blkFrames; /* number of frames in current audio block */ int blockSize = 4096; /* Size of an audio block buffer in frames */ int i; u_int16_t fakebuffer[2] = {0, 0}; #ifdef DEBUG fprintf(stderr, " >> writing data\n"); #endif buffer = (u_int16_t *) malloc( blockSize * (afInfo->SampleWidth/8) * afInfo->Channels * sizeof(u_int16_t)); if (!buffer) { SErrorCode = SERR_NOMEMORY; return -1; } /* Following line is a simple workarround for buggy pcm driver, which seens need at least to write() calls for sound to be actually played */ write(audiofd, fakebuffer, 4); curFrame = 0; while (curFrame < afInfo->FrameCount) { if ((blkFrames = (afInfo->FrameCount - curFrame)) > blockSize) { blkFrames = blockSize; } if (afReadFrames(afInfo->FileHandle, AF_DEFAULT_TRACK, buffer, blkFrames) < 1) { #ifdef DEBUG fprintf(stderr, " >> frames written %ld\n", curFrame); #endif free(buffer); SErrorCode = SERR_READ; return -1; } /* Adjust Volume */ if (SGetVolume() < 1.0) { for (i = 0; i < (blkFrames*afInfo->Channels); i++) { buffer[i] = (int16_t)buffer[i] * SGetVolume() + 0.5; } } if (write(audiofd, buffer, blkFrames * (afInfo->SampleWidth/8) * afInfo->Channels) == -1) { free(buffer); SErrorCode = SERR_DEVWRITE; return -1; } curFrame += blkFrames; } #ifdef DEBUG fprintf(stderr, " >> frames written %ld\n", curFrame); #endif free(buffer); return 0; }