Hallo, ich habe als Schulprojekt die Aufgabe eine Frequenzanalyse in "Echtzeit" am Eingang der Soundkarte zu machen. Für das Projekt soll ich unter Linux die ALSA Lib. und die FFTW3 Lib. nehmen. Ich hab auch ein HowTo für den Soundkartenzugriff gefunden, nur leider hab ich jetzt keine Ahnung wie ich den "char * buffer" an die FFTW übergeben kann und wie ich diesen char* buffer in einen Realteil wandle.... Hier das HowTo mit "Read Interleaved" snd_pcm_readi() /* This example reads from the default PCM device and writes to standard output for 5 seconds of data. */ /* Use the newer ALSA API */ #define ALSA_PCM_NEW_HW_PARAMS_API #include <alsa/asoundlib.h> int main() { long loops; int rc; int size; snd_pcm_t *handle; snd_pcm_hw_params_t *params; unsigned int val; int dir; snd_pcm_uframes_t frames; char *buffer; /* Open PCM device for recording (capture). */ rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_CAPTURE, 0); if (rc < 0) { fprintf(stderr, "unable to open pcm device: %s\n", snd_strerror(rc)); exit(1); } /* Allocate a hardware parameters object. */ snd_pcm_hw_params_alloca(¶ms); /* Fill it in with default values. */ snd_pcm_hw_params_any(handle, params); /* Set the desired hardware parameters. */ /* Interleaved mode */ snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); /* Signed 16-bit little-endian format */ snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE); /* Two channels (stereo) */ snd_pcm_hw_params_set_channels(handle, params, 2); /* 44100 bits/second sampling rate (CD quality) */ val = 44100; snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir); /* Set period size to 32 frames. */ frames = 32; snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir); /* Write the parameters to the driver */ rc = snd_pcm_hw_params(handle, params); if (rc < 0) { fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc)); exit(1); } /* Use a buffer large enough to hold one period */ snd_pcm_hw_params_get_period_size(params, &frames, &dir); size = frames * 4; /* 2 bytes/sample, 2 channels */ buffer = (char *) malloc(size); /* We want to loop for 5 seconds */ snd_pcm_hw_params_get_period_time(params, &val, &dir); loops = 5000000 / val; while (loops > 0) { loops--; rc = snd_pcm_readi(handle, buffer, frames); //wie an FFT??? if (rc == -EPIPE) { /* EPIPE means overrun */ fprintf(stderr, "overrun occurred\n"); snd_pcm_prepare(handle); } else if (rc < 0) { fprintf(stderr, "error from read: %s\n", snd_strerror(rc)); } else if (rc != (int)frames) { fprintf(stderr, "short read, read %d frames\n", rc); } //rc = write(1, buffer, size); //kann man bei mir weglassen //if (rc != size) // fprintf(stderr, // "short write: wrote %d bytes\n", rc); } snd_pcm_drain(handle); snd_pcm_close(handle); free(buffer); return 0; } Gruß und Danke Markus
Markus schrieb: > Hallo, > > ich habe als Schulprojekt ?????????????? macht man das jetz schon in der Mittelschule? na wow
Neee, aber in der Techniker-Schule ;-)) Gruß Markus
FFTW3 liefert auch RFFTW, das macht alles mit Real, ohne Komplexanteil. Schau mal da, da ist ein Codebeispiel http://jgt.akpeters.com/papers/Stam01/ :-)
Hi, das mit der rfftw hab ich schon im Manual gefunden ,nur wie übergebe ich die Daten aus der "While-Schleife" an die FFT. rc = snd_pcm_readi(handle, buffer, frames); Ich muss doch den Inhalt jedes einzelnen Samples in eine double Zahl umwandeln um dann ein Array mit N=16384 Werten zu füllen.... Oder liege ich da falsch? Gruß Markus
Du liegst richtig. Kannst aber auch float nehmen, wenn ich mich nicht irre, aber das steht im Manual.
Nimm aber in-place-conversion. Den Teil mit Plan erzeugen hast du auch schon gefunden, denk' ich mal.
Leider nein, das mit dem Plan hab ich noch nicht verstanden, genauso wenig wie das mit der Wandlung von char* auf double genau funktioniert. Ich müsste ja bei einem char Zeiger (4Byte denk ich) irgendwie jeweils 16bit pro Sample in double wandeln... Gruß Markus
Erst mal C lernen wäre gut: http://www.amazon.de/Programming-Language-Prentice-Hall-Software/dp/0131103628 Und dann das Manual von FFTW reinziehen - oder den Beispielcode vom rfft Demo ...
Also, mir würde mit der Umwandlung von "snd_pcm_readi(handle, buffer, frames)" nach "double" schon geholfen :-) Gruß Markus
> das mit dem Plan hab ich noch nicht verstanden, genauso wenig wie das > mit der Wandlung von char* auf double genau funktioniert. Ich müsste ja > bei einem char Zeiger (4Byte denk ich) irgendwie jeweils 16bit pro > Sample in double wandeln... Wie groß ein Zeiger auf char ist, ist für diese Wandlung aber mal sowas von überhaupt nicht relevant... Du mußt nicht den Zeiger, sondern das, worauf er zeigt, in double wandeln. Allerdings zeigt er noch auf den falschen Typ. Um jetzt ein Array aus char, in dem aber 16-Bit-Werte stehen, als eben eins aus 16-Bit-Werten interpretieren zu können, ist es am einfachsten, den Zeiger erstmal in einen passenden Typ zu wandeln: int16_t* samples = (int16_t*)buffer; Nun können wir da einfach in einer Schleife den Zielpuffer befüllen: double* double_samples = malloc(sizeof(double) * sampleanzahl); for (int i = 0; i < sampleanzahl; i++) { double_samples[i] = samples[i] / 32768.0; } Ich nehme mal an, daß ein Wertebereich von +/- 1 erwartet wird, deshalb wird durch 32768.0 geteilt. Ich kenne FFTW nicht und weiß daher nicht, ob das so richtig ist.
Hallo, und danke für die Antwort. Meine Verständnissfrage mit dem Zeiger hat folgenden Hintergrund. Wenn ich 5 Sek. Stereo aufnehme, dann sind das laut ls -l im RAW-Format 882688 Bytes. Das sind in der While-Schleife 6896 Durchgänge mit einem Frame von 32 und 4Byte char* genau 882688 Bytes. Jetzt müsste ja alle 2Byte(16bit) der linke und dann der rechte Kanal im Speicher stehen....nur wie steht jetzt das Frame genau in Verbindung mit dem Buffer? Ich würde ja gerne immer nur ein Sample des linken Kanal mit 16bit aus der Schleife in das "double Array" schreiben. Gruß Markus
> Wenn ich 5 Sek. Stereo aufnehme, dann sind das laut ls -l im RAW-Format > 882688 Bytes. Das paßt von der Größe her. 882688 2 Kanäle 2 BytesProSample / 44100 SamplesProSekunde = 5,00390022676 Sekunden > Das sind in der While-Schleife 6896 Durchgänge mit einem Frame von 32 > und 4Byte char* genau 882688 Bytes. Was hast du hier schon wieder mit der Größe eines char*? Die 4 Bytes kommen zustande, weil du 2 Kanäle mit jeweils 2 Bytes pro Sample hast. > Jetzt müsste ja alle 2Byte(16bit) der linke und dann der rechte Kanal > im Speicher stehen....nur wie steht jetzt das Frame genau in Verbindung > mit dem Buffer? Da du > /* Interleaved mode */ eingestellt hast, kommt immer wechselweise jeweils ein Sample vom linken und eins vom rechten Kanal. > Ich würde ja gerne immer nur ein Sample des linken Kanal mit 16bit aus > der Schleife in das "double Array" schreiben. Warum nimmst du es dann überhaupt in Stereo auf? In Mono ergibt sich die Frage gar nicht erst.
Wenn ich set_channel() auf 1 stelle und nach get_period_size() frames*2 antsatt 4 nehme, kommt bei meiner While-Schleife aber folgendes zustande. Durchgänge:3444 frames:64 (warum?) buffer:4 Byte Ergebnis:3444*64*4=881664 Byte Das würde wieder einer Größe von Stereo entsprechen?? Aufgenommen wird aber nur die Hälfte 440960 Bytes und ich weiß nicht wo plötzlich die 64 bei "frames" herkommt (es werden ja auch mit write(1,buffer,size)nur 64Byte geschrieben).
Habs selbst gefunden , eine Periode hat ja dann nur ein 16er Frame.
Ok erster Versuch, ich habe folgendes in der While-Schleife ergänzt um zu sehen was im Speicher steht. rc= snd_pcm_readi(handle,buffer,frames); short *samples = (short*)buffer; for (int i=0;i < 128;i++) { cout << *samples << endl; samples++; } Es werden jetzt Werte zwischen -32768…+32767 ausgegeben. Ist das bis dahin richtig? Gruß Markus
> Es werden jetzt Werte zwischen -32768…+32767 ausgegeben. > Ist das bis dahin richtig? Ja.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.