Frequenzen vom Mikrofon bestimmen

Also für die praktische Umsetzung würde ich so vorgehen:

1. mir eine Bibliothek etc. suchen, mit der man auf Audio-Eingänge zugreifen kann. Ein paar Anregungen:
Java Sound, Teil der Java SE API, http://docs.oracle.com/javase/tutorial/sound/TOC.html , http://openjdk.java.net/groups/sound/ , http://onjava.com/pub/a/onjava/excerpt/jenut3_ch17/index.html , http://www.developer.com/java/other...turing-Microphone-Data-into-an-Audio-File.htm , ich weiß nicht, ob das auch auf Mikros oder ähnliche Eingänge in allen Fällen zugreifen kann
Pulseaudio/ALSA/… (auf Linux/Unix) per JNI
Über die Samplelänge muss man sich etwas Gedanken machen: Um eine Schwingung noch sicher detektieren zu können, sollte man wenigstens 10 Perioden dieser Schwingung aufnehmen. Wenn wir also eine Untergrenze von 30Hz erreichen wollen, die wir noch sicher detektieren können, dann brauchen wir eine Samplelänge von 1/3 Sekunde. Um auf Nummer sicher zu gehen nehmen wir lieber 1 Sekunde, also 44.100 oder 48.000 Samples je nach Samplingrate (u.U. wären auch 192 kHz Abtastfrequenz möglich, aber nicht mit jeder Hardware und Software).

2. FFT auf die Sampledaten ausführen. Wäre praktisch, wenn man eine Bibliothek finden würde, die das kann. Die reale Implementierung hat nämlich einige Optimierungsmöglichkeiten, die auch unbedingt nötig sind – man will ja unter Umständen 10 mal pro Sekunde eine FFT auf Sampledaten von 1 Sekunde ausführen. Außerdem werden je nach Implementierung komplexe Zahlen verwendet.

3. FFT analysieren, d.h. auf dem Frequenzspektrum das Maximum suchen (einfache for-Schleife). Eventuell (je nach gewünschter Präzision) werden dazu auch mehrere aufeinander folgende Werte betrachtet, um zu vermeiden, dass ein Peak genau zwischen 2 Frequenzen fällt und somit die Intensität falsch eingeschätzt wird.

4. Frequenz berechnen, bei der das Maximum gefunden wurde. – da kann ich gerade gar nicht weiter helfen.
 
Zuletzt bearbeitet:
Vielen Dank schonmal an alle, die hier so tatrkäftig helfen!
Java:
import java.io.IOException;
import java.util.ArrayList;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.TargetDataLine;


public class AudioTest {
	AudioFormat format;
	TargetDataLine targetDataLine;
	int samplesize = 2;
	float samplerate = 44100f;
	public AudioTest() {

		format = new AudioFormat(samplerate, 8*samplesize, 1, true, false);

		DataLine.Info dataLineInfo =
				new DataLine.Info(
						TargetDataLine.class,
						format);
		try {
			targetDataLine = (TargetDataLine)AudioSystem.getLine(dataLineInfo);
			System.out.println("start");
			long time = System.currentTimeMillis();
			CaptureThread cp = new CaptureThread();
			cp.start();
			while (time + 2000 > System.currentTimeMillis())
				try {
					Thread.sleep(100);
				} catch (Exception e) {
					// TODO: handle exception
				}
			System.out.println("done rec");
			targetDataLine.stop();
			targetDataLine.close();
			cp.stop();
		} catch (LineUnavailableException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	class CaptureThread extends Thread{

		@Override
		public void run(){
			try {
				targetDataLine.open(format);
				targetDataLine.start();
				AudioInputStream ais = new AudioInputStream(targetDataLine);
				byte[] block = new byte[samplesize];
				ais.read(block, 0, samplesize);

				int value = block[0], oldValue=0;

				int messzeitraum = (int) samplerate; //samplerate 8000 = 1 sec
				boolean max = true; // true bei Maxima gesucht
				System.out.println("block länge: "+block.length);
				ArrayList<Frequenz> frequenzen = new ArrayList<Frequenz>();
				int sampleCount = 0;
				for (int lo=0; lo < messzeitraum; lo++) {

					while (ais.available() <= samplesize) {
						
					}
					ais.read(block,0,samplesize);
					value = block[0]+block[1];

					if (value > oldValue && max){
						sampleCount++;
					} else if (value <= oldValue && max){
						sampleCount++;
						max = false;
					} else if (value <= oldValue && !max)
						sampleCount++;
					else if (value > oldValue && !max){
						frequenzen.add(new Frequenz(sampleCount,samplerate));
						max = true;
						sampleCount = 1;
					}
					oldValue = value;
					System.out.println(oldValue + "\t - samples: "+sampleCount);
				}

				double b =0;
				
				// Testen ob Frequenz passt -> nein
				for (int i = 0; i < frequenzen.size(); i++) {
					b += frequenzen.get(i).frequenz;
					b /= 2;
					//System.out.println(b);
				}
			} catch (LineUnavailableException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	public static void main (String[] args){
		new AudioTest();
	}
}



_________________________



public class Frequenz {
	double frequenz = 0f;
	int sample = 0;
	double samplePerSec = 0;
	public Frequenz(int sample, double samplePerSec) {
		this.frequenz = 1/((double)sample/(samplePerSec));
		this.sample = sample;
		this.samplePerSec = samplePerSec;
	}
}

Ich habe hier ein kleines Testprogramm geschrieben (ohne Fourier Analyse) und würde jetzt gerne die Meinung hören. Ist das so korrekt und würde es den richtigen Wert liefern, wenn ich die Analyse durchführe?
Mein Ansatz war der von einigen schon vorgeschlagen wurde, nämlich die Minima zu bestimmen und die Samples dazu auszurechnen. Mich wundert jedoch, dass ich viele Frequenzen mit wenigen Samples bekomme (2-3). Liegt es an meiner Überlegung mit der Bestimmung der Samples oder hab ich sostwo einen Fehler?
Außerdem noch die Frage: Was müsste ich ändern, damit ich mit 16bit arbeiten kann? Einfach die beiden Bytes addieren würde sicher nicht funktionieren oder?
 
Zuletzt bearbeitet:
wenn du schon soweit bist :-)
lass dir doch erstmal bei deinem Testsignal die samples ausprinten

einfach um zu sehen ob überhaupt eine auswertung egal Max oder Null sinnvoll ist :-)

ich fürchte du kommst nicht an FFT vorbei ....

Code:
int messzeitraum = (int) samplerate / 100 ; //0.01sec

Code:
value = block[0];
System.out.println(value);

ev umstellen auf datei als eingang und dann das :-)
http://www.felusch.com/blog/?tag=sinus-1khz

ich habe hier ein 440Hz Generator rumliegen bin aber code mässig nicht soweit :-(

tja ich bekomme nur null werte ... zurück zum problem - wie selectiere ich dat micro ...
 
Zuletzt bearbeitet:
java sieht bei mir das (auszug)
Code:
CK8 [default] (Name: CK8 [default] / Vendor: ALSA (http://www.alsa-project.org) / Version: 1.0.23)
  Sources (2):
   Line: interface SourceDataLine supporting 84 audio formats, and buffers of at least 32 bytes        (Name: com.sun.media.sound.DirectAudioDevice$DirectSDL@1893efe)
   Clip: interface Clip supporting 84 audio formats, and buffers of at least 32 bytes        (Name: com.sun.media.sound.DirectAudioDevice$DirectClip@16b13c7)
  Targets (1):
   Line: interface TargetDataLine supporting 84 audio formats, and buffers of at least 32 bytes        (Name: com.sun.media.sound.DirectAudioDevice$DirectTDL@13d93f4)

ich harke mal Microfon ab - und beschäftige mich weiter mit meinem Problem - ich muss ja von file einlesen ...
 
Nach kleinen Anpassungen (Code wurde aktuallisiert) kommt folgendes Ablaufprotokoll raus. Der Sinus Signalton war 213Hz und auf laut eingestellt. Hier das Ergebnis:
Anhang anzeigen ablauf.txt
(erster Wert ist das, was in Value steht)
 
Zuletzt bearbeitet:
Leider kann ich es nicht mehr nachtesten da sich mein Programm immer noch weigert vom Mirco einzu lesen
aber...

Code:
 format = new AudioFormat(8800.0F, 16, 1, true, true);
also ein stellung Vorzeichen und BigEndian

dann sollte das Value bei 16 Bit wert so aussehen
Code:
int tmp = tl.read(block, 0, 2);
value = (block[0] << 8) | (block[1] & 0xFF);
höherwertiges Byte nach links geschoben und das unterere Byte dazu

wenn du wider dein Testsignal von 213 Hz und meine Einstellung SampleRate 8800 nimmst sollte also eine schwingung 41,3 :-) Samples haben

wenn also bei 100 Testsamples nix dergleichen an den Werten zu sehen ist - muss weiter nachgedacht werden ...
 
Zuletzt bearbeitet:
Hey danke, das hat echt geholfen! Jetzt verstehe ich jedoch nicht so ganz, warum ich das Doppelte der Frequenz herausbekomme? Ich bekomme bei einem Sinus von 201 einen Durschnittswert von 195 raus, wenn ich jede einzelne Frequenz Teile, ansonsten 395. (Ich habe einen gaaaaanz simplen Filter eingebaut, dass er mir die niedrigsten Samples rausschmeißt - alle unter 10 ;) ).
Übrigens bekomme ich unter meinem Mac auch des öfteren Fehlermeldungen aller:
Code:
java(6861,0x100501000) malloc: *** error for object 0x1ad: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
 
Malloc - ist eine Speichertregistrierung -
ev hilft ein flush() am ende des messvorgangs weiter
stop();
flush();
close();

Doppelte Werte was zählst du denn ? Z.B den Nulldurchgang den ich mal vorgeschlagen hatte der passiert beim Sinus genau 2 Mal pro Schwingung :-)

Gegenfrage - ich welchen Wertebereichen liegen denn deine Samples - ich hätte da noch ne Idee um Störungen auszufiltern . Stichword Smitt Trigger
 
Ach Gottchen bin ich blöd :D Hast natürlich Recht, eine Frequenz hat logischerweise 2 Extrema :$
Der Bereich ist meistens knapp unterhalb der eigentlichen Frequenz (2-5hz), ich werde wohl auch noch die TTF verwenden und schaue mal nach was du damit meinst :P
Der Fehler lies sich damit nicht beheben. Mein MacBook ist aus dem Jahre 2007. Es ist ein integriertes Micro mit 2 Kanälen (angeblich) und max. 96.000 Hz Aufnahmefrequenz. (Einstellungen laut Audio-Midi-Setup)
An meinem PC funktioniert es einwandfrei. Demnach würde ich schätzen (was man ja häufig liest), dass die Optimierung bei der Audio API das Problem ist.
Falls du noch andere Tips hast, immer her damit :D
 
Zurück