/* SoundIt library 0.04 Copyright 1994 Brad Pitzel pitzel@cs.sfu.ca Adapted to Solaris/SunOS and HPUX by Peter Ekberg. Feel free to use/distribute/modify as long as proper credits are included. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #if defined(HAVE_LINUX_SOUND) # include #elif defined(HAVE_SUN_SOUND) # if defined(HAVE_SYS_AUDIOIO_H) # include # elif defined(HAVE_SUN_AUDIOIO_H) # include # endif #elif defined(HAVE_HP_SOUND) # include #endif #include "soundIt.h" #include #include #include #include #include #include #include #include #include #include /*==========================================================================*/ /* the mix buff, where the channels are mixed into. The mix buffer is then dumped to the sound device (/dev/dsp). Samples are mixed in Vunclipbuf (buffer of ints), then the values in Vunclipbuf are clipped to values between 0 and 255, and stored into Vclippedbuf (buffer of unsigned chars). */ typedef struct { #if defined(HAVE_LINUX_SOUND) unsigned char *Vclippedbuf; #elif defined(HAVE_16BIT_SOUND) short *Vclippedbuf; #endif int *Vunclipbuf; int Vsize; } Mix; /*==========================================================================*/ typedef struct { unsigned char *Vstart, *Vcurrent; /* ptr's into a playing sample */ int Vlen; /* length of sample in bytes */ int Vleft; /* bytes left of sample to play */ int Vloop; /* loop=0 : play sample once */ /* loop=1 : loop sample */ } Channel; /*==========================================================================*/ /* variables prefixed with S_ are static */ /* 0 if mixer isn't initialized or init failed, 1 if mixer is good */ static int S_mixerStatus = 0; static const Sample *S_sounds = NULL; /* ptr to array of samples */ static int S_num_sounds = 0; /* size of 'sounds' array above */ static int S_fd_snddev = -1; /* file # for sound device once open */ static int S_fd_pipe[2] = { -1, -1 }; /* pipe to talk to child process */ static int S_son_pid = -1; /* process ID for the forked sound mixer */ static const char *S_snddev = NULL; /* char string for device, ie "/dev/dsp" */ static int S_num_channels = 6; /* number of channels to mix */ static int S_playback_freq = 0; /* playback frequency (in Hz) */ /*==========================================================================*/ /* non-public functions, used only within this file*/ int Snd_init_dev(void); int Snd_restore_dev(void); void Chan_reset(Channel *chan); /* init channel structure */ /* start a sample playing on a channel */ void Chan_assign(Channel *chan, const Sample *snd); /* mix all channels together into the 'mix' structure */ int Chan_mixAll(Mix *mix, Channel *ch); /* used by Chan_mixAll to mix the 1st channel */ int Chan_copyIn(Channel *chan, Mix *mix); /* used by Chan_mixAll to mix a loop in the 1st channel */ int Chan_copyIn_loop(Channel *chan, Mix *mix); /* used by Chan_mixAll to mix the middle channels */ int Chan_mixIn(Channel *chan, Mix *mix); /* used by Chan_mixAll to mix the last channel */ int Chan_finalMixIn(Channel *chan, Mix *mix); /* alloc mem for mix buffer, and deallocate function */ /* The sound channels are mixed together into the mix buffer */ /* then the mix buffer data is sent directly to the sound device */ void Mix_alloc(Mix *mix, int size); void Mix_dealloc(Mix *mix); /*==========================================================================*/ /* justing for testing, normally not called */ void dump_snd_list(void) { int i=0; for(i=0; i=S_num_sounds) return EXIT_FAILURE; /* make sure a valid channel # was passed in */ if(channel<0 || channel>=S_num_channels) return EXIT_FAILURE; if(S_sounds[sound_num].data != NULL) { write(S_fd_pipe[1], &sound_num, sizeof(sound_num)); write(S_fd_pipe[1], &channel, sizeof(channel)); } else { fprintf(stderr,"Referencing NULL sound entry\n"); return EXIT_FAILURE; } return EXIT_SUCCESS; } /*============================================================================*/ int Snd_init_dev(void) { int whoami; S_fd_snddev = -1; S_son_pid = 0; if(access(S_snddev,W_OK) != 0) { perror("no access to sound device"); return EXIT_FAILURE; } S_fd_snddev = open(S_snddev,O_WRONLY); if(S_fd_snddev < 0) { perror("cannot open sound device"); return EXIT_FAILURE; } close(S_fd_snddev); if(pipe(S_fd_pipe) < 0) { perror("cannot create pipe for sound control"); return EXIT_FAILURE; } /* now setup 2nd process for writing the data... */ if((whoami = fork()) < 0) { perror("cannot fork sound driver\n"); return EXIT_FAILURE; } if(whoami != 0) { /* successfully created son */ close(S_fd_pipe[0]); /* close end for reading */ S_son_pid = whoami; return EXIT_SUCCESS; } /* Here is the code for the son... */ { int sound_num,ch,i; struct timeval tval = {0L,0L}; fd_set readfds,dsp; Mix mix; int fragsize; Channel *chan; chan = (Channel *)malloc(sizeof(Channel) * S_num_channels); for(i=0; i 1050) printf("No good sample rate\n"); if(ioctl(S_fd_snddev, AUDIO_SET_SAMPLE_RATE, best_rate)==-1) perror("Sound driver ioctl: Audio Set Sample Rate"); /* AUDIO_OUT_LINE | AUDIO_OUT_INTERNAL | AUDIO_OUT_EXTERNAL */ if(ioctl(S_fd_snddev, AUDIO_SET_OUTPUT, AUDIO_OUT_EXTERNAL)==-1) perror("Sound driver ioctl: Audio Set Output"); again.cgain[0].transmit_gain = AUDIO_MAX_GAIN; again.cgain[0].monitor_gain = AUDIO_OFF_GAIN; again.cgain[1].transmit_gain = AUDIO_MAX_GAIN; again.cgain[1].monitor_gain = AUDIO_OFF_GAIN; again.channel_mask = AUDIO_CHANNEL_LEFT | AUDIO_CHANNEL_RIGHT; if(ioctl(S_fd_snddev, AUDIO_SET_GAINS, &again)==-1) perror("Sound driver ioctl: Audio Set Gain"); /* alimits.max_transmit_buffer_size = 512; alimits.max_receive_buffer_size = 512; if(ioctl(S_fd_snddev, AUDIO_SET_LIMITS, &alimits)==-1) perror("Sound driver ioctl: Audio Set Limits"); if(ioctl(S_fd_snddev, AUDIO_GET_LIMITS, &alimits)==-1) perror("Sound driver ioctl: Audio Get Limits"); fragsize=alimits.max_transmit_buffer_size; */ fragsize = 1024; } #endif /*printf("after: block size: %d \n", fragsize);*/ /* init mixer object*/ Mix_alloc(&mix, fragsize); close(S_fd_pipe[1]); /* close end for writing */ FD_ZERO(&dsp); FD_SET(S_fd_snddev, &dsp); FD_ZERO(&readfds); FD_SET(S_fd_pipe[0], &readfds); /* printf("soundIt library v"SOUNDIT_VERS" initialized.\n");*/ for(;;) { FD_SET(S_fd_pipe[0], &readfds); tval.tv_sec=0L; tval.tv_usec=0L; select(S_fd_pipe[0]+1, &readfds,NULL,NULL,&tval); if(FD_ISSET(S_fd_pipe[0], &readfds)) { if(read(S_fd_pipe[0], &sound_num, sizeof(int))==0) break; read(S_fd_pipe[0], &ch, sizeof(int)); /*printf("chan=%d snd=%d len=%d\n", ch, sound_num, S_sounds[sound_num].len);*/ Chan_assign(&(chan[ch]), &(S_sounds[sound_num])); } Chan_mixAll(&mix,chan); #if defined(HAVE_LINUX_SOUND) write(S_fd_snddev, mix.Vclippedbuf, fragsize); #elif defined(HAVE_SUN_SOUND) { int stat; audio_info_t ainfo; static unsigned int samplecount=0; do { stat = ioctl(S_fd_snddev, AUDIO_GETINFO, &ainfo); if(samplecount-ainfo.play.samplesVstart=NULL; chan->Vcurrent=NULL; chan->Vlen=0; chan->Vleft=0; chan->Vloop=0; } /*==========================================================================*/ void Chan_assign(Channel *chan, const Sample *snd) { chan->Vstart = snd->data; chan->Vcurrent= chan->Vstart; chan->Vlen = snd->len; chan->Vleft = snd->len; chan->Vloop = snd->loop; } /*==========================================================================*/ int Chan_copyIn(Channel *chan, Mix *mix) { int i, *p = mix->Vunclipbuf, result, min; result = (chan->Vleft>0) ? 1 : 0; min = (chan->Vleft < mix->Vsize) ? chan->Vleft : mix->Vsize; for(i=0; iVcurrent++; } chan->Vleft -= i; /* fill the remaining (if any) part of the mix buffer with silence */ while(iVsize) { *p++ = 128; i++; } return result; } /*==========================================================================*/ int Chan_copyIn_loop(Channel *chan, Mix *mix) { int i=0; int *p = mix->Vunclipbuf; int left_mix_vsize=mix->Vsize; if (chan->Vleft==0) chan->Vleft=chan->Vlen; if(left_mix_vsize > chan->Vleft) { /* fill the rest of the sample first */ for(i=0; iVleft; i++) *p++ = (int)*chan->Vcurrent++; left_mix_vsize -= chan->Vleft; chan->Vleft = chan->Vlen; /* loop the sample by writing it repeatedly */ while(left_mix_vsize > chan->Vlen) { chan->Vcurrent=chan->Vstart; for(i=0; iVleft; i++) *p++ = (int) *chan->Vcurrent++; left_mix_vsize-=chan->Vlen; } /* Return to the start of the Sample */ chan->Vcurrent=chan->Vstart; /* If there is some space left, then fill it up :-) */ for(i=0;iVcurrent++; chan->Vleft=chan->Vlen - left_mix_vsize; } else { /* fill in all that fits, no looping to be done ! */ for(i=0; iVsize; i++) *p++ = (int) *chan->Vcurrent++; chan->Vleft -= i; } return 1; } /*==========================================================================*/ int Chan_mixIn(Channel *chan, Mix *mix) { int i,*p = mix->Vunclipbuf, result, min; result = (chan->Vleft>0) ? 1 : 0; min = (chan->Vleft < mix->Vsize) ? chan->Vleft : mix->Vsize; for(i=0; iVcurrent++) - 128; chan->Vleft -= i; return result; } /*========================================================================*/ /* clip an int to a value between 0 and 255 */ #ifdef HAVE_16BIT_SOUND /* static inline short clip(int i) { return ((i<0) ? 0 : ( (i>255) ? 255 : i )) * 257 - 32768; } */ #define clip(i) ((short)((((i)<0) ? 0 : ( ((i)>255) ? 255 : (i) )) * 257 - 128*257)) #else static inline unsigned char clip(int i) { return (i<0) ? 0 : ( (i>255) ? 255 : i ); } #endif /*==========================================================================*/ int Chan_finalMixIn(Channel *chan, Mix *mix) { int i; int *p = mix->Vunclipbuf, result, min; #ifdef HAVE_16BIT_SOUND short *final = mix->Vclippedbuf; #else unsigned char *final = mix->Vclippedbuf; #endif result = (chan->Vleft>0) ? 1 : 0; min = (chan->Vleft < mix->Vsize) ? chan->Vleft : mix->Vsize; for(i=0; iVcurrent++) - 128; *final++ = clip(*p); p++; } chan->Vleft -= i; /* copy rest of Vunclipbuf over to Vclippedbuf */ while (iVsize) { *final++ = clip(*p); p++; i++; } return result; } /*==========================================================================*/ void Mix_alloc(Mix *mix, int size) { #ifdef HAVE_16BIT_SOUND mix->Vclippedbuf = (short *)calloc( sizeof(short), size); #else mix->Vclippedbuf = (unsigned char *)calloc( sizeof(char), size); #endif mix->Vunclipbuf = (int *)calloc( sizeof(int), size); mix->Vsize = size; if((mix->Vclippedbuf==NULL) || (mix->Vunclipbuf==NULL)) { printf("Unable to allocate memory for mixer buffer.\n"); exit(-1); } } /*==========================================================================*/ void Mix_dealloc(Mix *mix) { if(mix->Vclippedbuf) free(mix->Vclippedbuf); if(mix->Vunclipbuf) free(mix->Vunclipbuf); } /*==========================================================================*/ /* Mixes together the channels into one sound. Returns # of channels currently playing *any* sound Therefore, return 0 means to channels have a sample, therefore no sound is playing */ int Chan_mixAll(Mix *mix, Channel *chan) { int result=0, i=0; /* Loop in the first channel ? */ if (chan->Vloop==0) result = Chan_copyIn(chan, mix); else result = Chan_copyIn_loop(chan, mix); /* we want to loop for S_num_channels-2 */ for(i=2; idata = NULL; sample->len = 0; sample->loop = loop; fp = fopen(file,"r"); if(fp==NULL) return -1; /* get length of the file */ sample->len = lseek(fileno(fp), 0, SEEK_END); /* go back to beginning of file */ lseek(fileno(fp), 0, SEEK_SET); /* alloc memory for sample */ sample->data = (unsigned char *)malloc(sample->len); if(sample->data==NULL) { fclose(fp); return -2; } fread(sample->data, 1, sample->len, fp); fclose(fp); return 0; }