/* Audio duplex mmap test program * Stéphane Doyon * January 20th 2002 * Two tests are available (see main.c). Compile with -DECHO for second test. */ /* Newer versions of the emu10k1 driver use non-standard mmap semantics */ //#define EMU10K1_MMAP_SEMANTICS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void newLine() { fprintf(stderr,"\n"); } void terminate() { exit(1); } void errDie(char *fmt, ...) { va_list argp; va_start(argp, fmt); vfprintf(stderr, fmt, argp); fprintf(stderr, ": %s\n", strerror(errno)); va_end(argp); terminate(); } void dieMsg(char *fmt, ...) { va_list argp; va_start(argp, fmt); vfprintf(stderr, fmt, argp); fprintf(stderr,"\n"); va_end(argp); terminate(); } void dieOutMem() { dieMsg("Out of memory!"); } static struct timeval progStart; void getNow(struct timeval *ptr) { struct timezone tz; gettimeofday(ptr,&tz); } void initTime() { getNow(&progStart); } int diffTime(struct timeval *t1, struct timeval *t2) { return (t2->tv_sec - t1->tv_sec) * 1000 + (t2->tv_usec - t1->tv_usec) / 1000; } unsigned elapsedSinceProgStart(struct timeval *t) { return diffTime(&progStart, t); } unsigned elapsed(struct timeval *t) { struct timeval now; getNow(&now); return(diffTime(t, &now)); } unsigned timeStamp() { return elapsed(&progStart); } void printProgTime() { fprintf(stderr,"%u", timeStamp()); } void print(char *fmt, ...) { va_list argp; va_start(argp, fmt); printProgTime(); fprintf(stderr,": "); vfprintf(stderr,fmt, argp); fflush(stdout); va_end(argp); } #define TRACEPRINT(level, args...) \ ({ if(debug >= level) print(args); newLine(); }) #define TRACE(args...) \ ({ print(__FUNCTION__ ": "); fprintf(stderr, args); newLine(); \ }) typedef struct sample_struct { __s16 c1; __s16 c2; } __attribute((packed)) sample; #define BYTES_PER_SAMP 4 #define SAMPS2BYTES(len) ((len)*BYTES_PER_SAMP) #define BYTES2SAMPS(len) ((len)/BYTES_PER_SAMP) #define SPEED 22050 #define TIME2SAMPS(msec) \ ((int)( (double)(msec)/1000.0 * (double)(SPEED) ) ) #define SAMPS2TIME(s) \ ((int)( (double)(s) / (double)(SPEED) *1000.0 ) ) #define STEREO 1 /*stereo*/ #define FORMAT AFMT_S16_LE /* signed 16bits little-endian */ /* NB correct operation is not possible with only 2 frags, we need at least 4. */ #define FRAGSIZECODE 14 /* fragment size = 2^14=16384 = 0.1857secs at 22.05KHz 16bits stereo */ /* With some version of es1370, although the buffer is 128K I can't seem to mmap more than 64K. */ #if 0 #define MAXDMA (64*1024) /* Whether to enforce a limit on the number of fragments */ #define NFRAGS (MAXDMA/(1<=0) { close(audio_fd); TRACE("Closed soundcard"); } audio_fd=-1; } static void cardSetup() { int frag = (NFRAGS << 16) | FRAGSIZECODE; int caps = 0; int stereo = STEREO; int speed = SPEED; int format = FORMAT; TRACE("About to open sound device"); if((audio_fd = open("/dev/dsp",O_RDWR |O_NONBLOCK, 0)) <= 0) errDie("open"); TRACE("Opened sound device"); if(ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps)==-1) errDie("ioctl SNDCTL_DSP_GETCAPS"); TRACE("Obtained caps: 0x%x", caps); if(!(caps & DSP_CAP_DUPLEX)) dieMsg("Soundcard is not full-duplex"); if(!(caps & DSP_CAP_TRIGGER)) dieMsg("Soundcard does not support trigger"); if(!(caps & DSP_CAP_MMAP)) dieMsg("Soundcard does not support mmap"); if(!(caps & DSP_CAP_REALTIME)) dieMsg("Soundcard does not support realtime / precise position reporting"); TRACE("caps are OK"); if (ioctl(audio_fd, SNDCTL_DSP_SETDUPLEX, 0)==-1) errDie("ioctl SNDCTL_DSP_SETDUPLEX"); TRACE("set duplex"); if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag)==-1) errDie("ioctl SNDCTL_DSP_SETFRAGMENT"); if( (frag & 0xFFFF) != FRAGSIZECODE ) dieMsg("Fragment size of %d was refused, proposed size was %d", FRAGSIZECODE, (frag & 0xFFFF)); TRACE("set fragments %x", frag); if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format)==-1) errDie("ioctl SNDCTL_DSP_SETFMT"); if(format != AFMT_S16_LE) dieMsg("Soundcard does not support 16bits signed sample format.\n"); TRACE("set format 0x%x", format); if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo)==-1) errDie("ioctl SNDCTL_DSP_STEREO"); if(stereo != STEREO) dieMsg("Stereo setting was ignored!"); TRACE("set stereo %d", stereo); if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &speed)==-1) errDie("ioctl SNDCTL_DSP_SPEED"); if(speed != SPEED) dieMsg("Speed selection of %d was translated to speed %d!", SPEED, speed); TRACE("set speed %d", speed); } static void cardDoMmap() { int size = nFrags*fragSize; int sizeSamps = size / BYTES_PER_SAMP; #ifdef EMU10K1_MMAP_SEMANTICS if((obuf = mmap(NULL, 2*size, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, audio_fd, 0)) == (void *)-1) errDie("mmap (input)"); oend = obuf+sizeSamps; ibuf = oend; iend = ibuf+sizeSamps; TRACE("mmapp'ed play to %p, up to %p", obuf,oend); TRACE("mmapp'ed record to %p, up to %p", ibuf,iend); #else if((ibuf = mmap(NULL, size, PROT_READ, MAP_FILE|MAP_SHARED, audio_fd, 0)) == (void *)-1) errDie("mmap (input)"); iend = ibuf+sizeSamps; TRACE("mmapp'ed PROT_READ to %p, up to %p", ibuf,iend); if((obuf = mmap(NULL, size, PROT_WRITE, MAP_FILE|MAP_SHARED, audio_fd, 0)) == (void *)-1) errDie("mmap (output)"); oend = obuf +sizeSamps; TRACE("mmapp'ed PROT_WRITE to %p, up to %p", obuf, oend); #endif } void cardSilence() { memset(obuf, 0, totalSamps *BYTES_PER_SAMP); TRACE("memset'ed obuf to silence"); } void cardOpen() { audio_buf_info info; cardClose(); cardSetup(); if(ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info)==-1) errDie("ioctl SNDCTL_DSP_GETOSPACE"); fragSize = info.fragsize; if(fragSize != (1<=0) { t = &timeo; timeo.tv_sec = msec/1000; timeo.tv_usec = (msec%1000)*1000; }else t = NULL; FD_ZERO(&set); FD_SET(audio_fd, &set); while(1) { if((ret = select(audio_fd+1, NULL, &set, NULL, t)) <0) { if(errno == EINTR) continue; dieMsg("select"); } return ret; } /* GETIPTR and GETOPTR to clear before calling again! */ } void clearSelect() { cardGetOPtr(); cardGetIPtr(); } void noise() { sample *s = obuf; int i = 0; while(sc1 = 0x8000; s->c2 = 0x8000; }else{ s->c1 = 0x7FFF; s->c2 = 0x7FFF; } s++; if(++i > 64) i=-63; } TRACE("wrote noise to obuf"); } int main() { initTime(); cardOpen(); cardStart(); #ifndef ECHO /* the simple beep test */ noise(); { int i; for(i=0; i<20; i++) { cardWaitFrag(-1); cardGetIPtr(); cardGetOPtr(); } } #endif #ifdef ECHO /* simplified echo test */ { /* assumes we never skip an interrupt... good enough for testing! */ int i; sample *iptr, *optr; for(i=0; i<30; i++) { cardWaitFrag(-1); iptr = cardGetIPtr(); iptr = ibuf + ((iptr-ibuf)/sampsPerFrag*sampsPerFrag); optr = cardGetOPtr(); optr = obuf + ((optr-obuf)/sampsPerFrag*sampsPerFrag); iptr -= sampsPerFrag; if(iptr= ibuf); assert(iptr < iend); } optr += sampsPerFrag; if(optr>=oend) { optr -= totalSamps; assert(optr >= obuf); assert(optr < oend); } assert( (sample *)(((char *)iptr) +fragSize) <= iend); assert( (sample *)(((char *)optr) +fragSize) <= oend); memcpy(optr, iptr, fragSize); } } #endif cardClose(); return 0; }