/* * PCM-Player fuer 16bit signed Dateien auf Ice1712 Pro mit ALSA/Linux/x86 * Aufruf [Programm] [Dateiname] * * (Compilieren fuer Mono oder Stereo und auf eine feste Samplingrate zw. * 8000 und 48000Hz, hat bisher keine eigene Erkennung fuer WAV-Header!) * Benoetigte Library: asound * * Frei kopierbarer haesslicher "proof of concept" Code ohne jegliche * Garantien, nicht mal die, die oben versprochene Funktion zu erfuellen. * * Geschrieben von Eric Auer, Tipps von Dan Hollis und Christian Dressler * Achtung: Der ALSA Treiber fuer diese Karte erlaubt nur, ALLE Kanaele * gleichzeitig zu oeffnen, daher funktioniert dafuer auch keine OSS-Emu. * Fuer Ausgabe sind das exakt 10, fuer Eingabe exakt 12 Monokanaele. * * Ausserdem ist 32bit signed little endian das einzig moegliche Format, * wobei nur die 24 MSB vom Wandler verarbeitet werden. Das ALSA PCM * Plugin System bietet aber bei Bedarf Formatanpassungen per Software. * * 22.05.2000 */ #define FORCE_REGULAR 0 // 0 = auch Pipes etc. erlaubt // #define snd_inSTEREO /* Wenn define, wird die Datei von Stereo auf Mono runtergemischt */ #define snd_HZ 22050 /* z.B. 22050 */ /* Ice1712pro/Linux kann nur S32_LE, aber 8-48kHz Samplingrate... */ #define iCARD signed int #define iFILE signed short int #define nCHN 10 /* ***************************************************************** */ #include #include // #include // Kartentypen // #include // Sequencer #include #include // strncpy #include #include // ... #include // open #include // stat #include // malloc #include // usleep #include // iovec /* ***************************************************************** */ // #define DEBUG #define USLEEP #define WBUF 4096 /* WriteBufferSize in Samples */ #define snd_inBITS (8*sizeof(iFILE)) /* *** Achtung, zur Zeit werden nur 16 Bit signed verwendet!!! *** */ #define snd_SHL (31-snd_inBITS) /* 24 MSB of 32 used bei ice1712 */ // Ich habe ein MSB weggelassen, besser analog auf volles Volume bringen... // 386: __BYTE_ORDER == __LITTLE_ENDIAN // ice1712pro kann nur genau 10 voices playback und 12 voices capture // ausserdem geht im stream mode nur write/interleaved richtig!? /* ***************************************************************** */ void help(void) { printf("Usage: Give me a filename and I will play it,\n" "assuming the data is signed %dbit " #ifndef snd_inSTEREO "monaural" #else "stereo" #endif " PCM %dHz.\n", snd_inBITS,snd_HZ); _exit(1); return; }; /* auf files gibts z.B. getc() und feof() und int getw() * coz EOF is a valid int, ferror() should be used to detect getw() errors. * interessant bei sun: man -s 5 intro -> Tipps fuer Manpages aus Sektion 5, * z.B. regex, math, pam_unix, socket, signal/siginfo, ... flockfile fread */ /* ***************************************************************** */ int file_size(char * filenam, int force_regular) { struct stat status; int fd,fsize; fd = open(filenam,O_RDONLY /* | 0400000 */ ); // 04... ist O_NOFOLLOW if (!fd) { perror("could not open sound file"); return 0; }; if (fstat(fd,&status)) { perror("could not stat file"); close(fd); return 0; }; close(fd); fsize = status.st_size / sizeof(iFILE); #ifdef snd_inSTEREO fsize >>= 1; #endif if (force_regular) { if (!S_ISREG(status.st_mode)) { fprintf(stderr,"not a regular file\n"); return 0; }; } else { if (!S_ISREG(status.st_mode)) { fprintf(stderr,"not a regular file, assuming pipe\n"); return 12*3600*snd_HZ; }; }; return (fsize>0) ? fsize : 0; }; /* ***************************************************************** */ int open_sound(snd_pcm_t ** handle /* ptr auf ptr */) { int retval; snd_pcm_channel_params_t params; if ((retval = snd_pcm_open(handle, 0 /* card */, 0 /* device */, SND_PCM_OPEN_PLAYBACK))) { fprintf(stderr,"Error opening sound "); perror(snd_strerror(retval)); return 1; }; // auch mgl.: snd_pcm_open_subdevice(h,c,d, s ,m) ... // mode: | SND_PCM_OPEN_NONBLOCK ist mgl. params.channel=0; params.format.format=SND_PCM_SFMT_S32_LE; // je nach iCARD ! // Ice1712pro/Linux does only this, and without the use of plugins, // ALSA offers no conversion. // btw. the hardware only uses the 24 MSBs. params.format.interleave = 1; // interleaved vs. blocked data format // for vector based i/o, interleave sucks. params.format.rate = snd_HZ; params.format.voices = nCHN; // Ice1712pro/Linux driver supports only opening all voices at once! // for capture, voices *must* be 12, for playback, it *must* be 10. params.channel = SND_PCM_CHANNEL_PLAYBACK; // or ... CAPTURE params.mode = SND_PCM_MODE_STREAM; // or ... BLOCK // not set: params.digital.*, params.format.reserved, // params.reserved, params.format.special params.start_mode = SND_PCM_START_DATA; // or ... FULL or ... GO params.stop_mode = SND_PCM_STOP_STOP; // ... STOP or ... ERASE or ... ROLLOVER // STOP klappt, aber ROLLOVER muss nach einem Buffer // overrun/underrun nicht neu angeworfen werden. params.time = 0; // set to get gettimeofday time of beginning of transfer // -> sys/time.h ... struct timeval -> tv_sec ... params.ust_time = 0; // set to get time in UST format params.buf.stream.queue_size = (snd_HZ >> 1) * sizeof(iCARD) * nCHN; // muss ein Vielfaches von ... sein, min 1/2k... // 1/16 Sekunde mit (snd_HZ >> 4) * ... funktioniert z.B. ok. // Groessere Queue -> mehr Latency aber weniger kritisches // Timing bei Multitasking. Also sinnvoll einstellen! params.buf.stream.fill = SND_PCM_FILL_SILENCE_WHOLE; // or ... NONE or ... SILENCE params.buf.stream.max_fill = sizeof(iCARD) * nCHN * 10; // not really used if fill!=...SILENCE /* * params.buf.block.frag_size=4096 * nCHN; * params.buf.block.frags_min=1; * params.buf.block.frags_max=10; */ if ((retval = snd_pcm_channel_params(*handle,¶ms))) { fprintf(stderr,"Format not supported by soundcard: "); fprintf(stderr,"%s\n",snd_strerror(retval)); return 1; }; if ((retval = snd_pcm_playback_prepare(*handle))) { fprintf(stderr,"Error preparing playback: "); fprintf(stderr,"%s\n",snd_strerror(retval)); return 1; }; // je nach START setting fehlt nun noch snd_pcm_playback_go(), // ansonsten passiert der START automatisch. return 0; }; /* ***************************************************************** */ int write_sound(snd_pcm_t * handle, FILE * pcmdata, int samples) { int left, written; int cnt, cnt2, retval; int towrite, toread; // struct iovec bufv[nCHN]; iCARD mybuf[1+(WBUF*nCHN)]; iFILE filebuf[1+(WBUF*2)]; // *2 wg snd_inSTEREO iCARD * myrest; int restsize; int underrun=0; if (samples <= 0) return 1; left = samples; written = 0; #ifdef DEBUG fprintf(stderr,"About to play %d samples\n",samples); #endif while (left>0) { toread = (left>WBUF) ? WBUF : left; towrite = fread(&filebuf, #ifdef snd_inSTEREO 2 * #endif sizeof(filebuf[0]), toread, pcmdata); if (ferror(pcmdata)) { perror("fread PCM file failed"); return 1; }; if (towrite0) { written = snd_pcm_write(handle, myrest, restsize*sizeof(iCARD)*nCHN ); // snd_pcm_writev(handle, bufv, nCHN); if ((written<0) && (written != -EAGAIN)) { fprintf(stderr,"Error with snd_pcm_write, trying to recover: %s\n", snd_strerror(written)); #ifdef USLEEP usleep(1000); #endif // (void)snd_pcm_playback_drain(handle); // (void)snd_pcm_playback_flush(handle); // retval = snd_pcm_playback_go(handle); // fprintf(stderr,"Tried to flush and restart playback / %s\n", // snd_strerror(retval)); if ((retval = snd_pcm_close(handle))) { fprintf(stderr,"Error closing sound for re-open: %s\n", snd_strerror(retval)); return 1; }; if ((retval = open_sound(&handle))) { fprintf(stderr,"Error re-opening sound: %s\n", snd_strerror(retval)); return 1; }; }; written = (written>0) ? (written / (sizeof(iCARD) * nCHN)) : 0; left -= written; #ifdef DEBUG if (written!=restsize) { fprintf(stderr,"Only %d of %d (rest of %d) samples accepted\n", written,restsize,towrite); } else fprintf(stderr,"%d samples written\n",written); #endif restsize -= written; myrest += written * nCHN; #ifdef USLEEP if (left>0) usleep(1000); #endif }; // Ende while... write #ifdef USLEEP if (!towrite) usleep(1000); #endif }; // Ende while... read return 0; }; /* ***************************************************************** */ int main(int argc, char ** argv) { FILE * pcmdata; int retval, fsize; snd_pcm_t * soundhandle=NULL; if ((argc!=2) || (!argv)) help(); if (!argv[1]) help(); if (! (fsize = file_size(argv[1],FORCE_REGULAR))) { fprintf(stderr,"%s:",argv[1]); perror("Error checking file"); return 1; }; if (! (pcmdata = fopen(argv[1],"r"))) { fprintf(stderr,"%s:",argv[1]); perror("Error opening file"); return 1; }; if (open_sound(&soundhandle)) { fprintf(stderr,"Error opening ALSA"); return 1; }; if (write_sound(soundhandle, pcmdata, fsize-1)) /* fsize hat Einheit Samples */ { fprintf(stderr, "Error writing to PCM\n"); }; // but hang on... if ((retval = snd_pcm_close(soundhandle))) { fprintf(stderr,"Error closing sound: "); fprintf(stderr,"%s\n",snd_strerror(retval)); }; // hang on if an error occured, do not return 1. if (fclose(pcmdata)) { perror("Could not close file (!)"); return 1; }; return 0; };