/* * PCM-Recorder / 16bit signed Dateien auf Ice1712 Pro mit ALSA/Linux/x86 * Aufruf [Programm] [Dateiname] ("liest" die Datei mit mmap!) * * (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. * * 30.05.2000 */ #define WHICH_CHN 0 // Kanal der ausgelesen werden soll, 0..7.., normal 0. #define snd_HZ 22050 /* z.B. 22050 */ /* Ice1712pro/Linux kann nur S32_LE, aber 8-48kHz Samplingrate... */ /* ***************************************************************** */ #include #include #include #include // strncpy #include #include // ... #include // open #include // stat #include // malloc #include // usleep #include // iovec /* ***************************************************************** */ // #define DEBUG #define USLEEP #define RBUF 4096 /* ReadBufferSize in Samples */ #define nCHN 12 // Channels, muss 12 sein bei der Karte #define snd_inBITS 16 /* *** 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 // sehr witzig! // ausserdem geht im stream mode nur write/interleaved richtig!? /* ***************************************************************** */ void help(void) { printf("Usage: Give me a filename and I will write,\n" "signed %dbit " "mono" " PCM %dHz data to it.\n", snd_inBITS,snd_HZ); _exit(1); return; }; /* ***************************************************************** */ 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_CAPTURE))) { fprintf(stderr,"Error with snd_pcm_open: %s\n",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; // 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_CAPTURE; // 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_ROLLOVER; // ... 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) * 4 * nCHN; // muss ein Vielfaches von 4*nCHN sein, min 1/2k... // 1/16 Sekunde mit (snd_HZ >> 4) * 40 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 = 480; // not really used if fill!=...SILENCE /* * params.buf.block.frag_size=40960; * 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_capture_prepare(*handle))) { fprintf(stderr,"Error preparing capture: "); fprintf(stderr,"%s\n",snd_strerror(retval)); return 1; }; // je nach START setting fehlt nun noch snd_pcm_capture_go(), // ansonsten passiert der START automatisch. return 0; }; /* ***************************************************************** */ int read_sound(snd_pcm_t * handle, FILE * o_stream) { int left, written, cnt, cnt2, which_chn=WHICH_CHN; // struct iovec bufv[nCHN]; signed int mybuf[RBUF*nCHN]; // nCHN wg nCHN voices signed short int mywrbuf[RBUF]; // for (left=0;left0 */ 42==42) { left = snd_pcm_read(handle,mybuf, sizeof(mybuf)); // snd_pcm_readv(handle, bufv, nCHN); left = (left>0) ? left / (nCHN * sizeof(mybuf[0])) : left; if (left<=0) { fprintf(stderr,"Error with snd_pcm_read: %s\n", snd_strerror(left)); #ifdef DEBUG left = snd_pcm_capture_go(handle); fprintf(stderr,"Tried to restart capture: %s\n", snd_strerror(left)); #endif left = 0; fprintf(stderr,"Trying to continue\n"); }; if ((left<0) && (left!= -EAGAIN)) { fprintf(stderr,"Error with snd_pcm_read: %s\n", snd_strerror(left)); return 1; }; if (left>0) { /* SKIP IF BUFFER EMPTY */ for (cnt=0,cnt2=which_chn; cnt> snd_SHL; cnt2+=nCHN; }; written = fwrite((void *)mywrbuf, sizeof(mywrbuf[0]), left, o_stream); if (written < left) { fprintf(stderr,"Did not expect file write to block. Bad luck.\n" "%d of %d samples written.\n",written,left); // return 1; }; }; /* SKIPPED IF BUFFER EMPTY */ #ifdef DEBUG if (left>0) { fprintf(stderr,"%d samples fetched\n",left); }; #endif #ifdef USLEEP usleep(1000); #endif #ifdef DEBUG left = 1; // endlos lesen #endif }; return 0; }; /* ***************************************************************** */ int main(int argc, char ** argv) { int retval; snd_pcm_t * soundhandle=NULL; FILE * pcmdata; if ((argc!=2) || (!argv)) help(); if (!argv[1]) help(); (void)umask(022); if (! (pcmdata = fopen(argv[1],"a"))) { fprintf(stderr,"%s:",argv[1]); perror("Error opening file"); return 1; }; if (open_sound(&soundhandle)) return 1; if (read_sound(soundhandle,pcmdata)) { fprintf(stderr, "Error reading from PCM or writing to file\n"); }; // but hang on... fprintf(stderr,"Closing sound and file\n"); 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 (write error?)"); return 1; }; return 0; };