# Audio-Eingabegerät auswählen (MIC oder LINE-IN)



## xysawq (1. Sep 2008)

Hallo, ich versuche momentan ein RTP-Kommunikationstool zu schreiben, momentan bin ich soweit, dass ich Audio aus Dateien einlesen, zu einer anderen Maschine senden, und dort speichern, bzw. abspielen kann.

Das Abspiele löse ich ganz einfach nur eine SourceDataLine, wie hier im Forum schon mehrfach beschrieben.

Jetzt möchte ich aber auch kommunizieren (sonst wäre es ja kein Kommunikationstool), und brauche dazu die Möglichkeit, von einem Mikrofon einlesen zu können.

Benutze ich eine TargetDataLine, dann Nehme ich aber von allem möglichen auf. Zudem möchte ich den Benutzer eine Auswahl treffen lassen, welchen Input er verwenden möchte (falls z.B. mehrere Geräte angeschlossen oder bereits verbaut sind, wie bei einem Notebook mit Built-In-Mic).

Ich habe mit zur Übersicht ein kleines Programm geschrieben, bei dem ich mir anzeigen lasse, was es alles an Geräten auf meinem Rechner gibt.


```
System.out.println("Available:");
Mixer mixer = null;
Mixer.Info[] mixerinfos = AudioSystem.getMixerInfo();
for(int i=0; i<mixerinfos.length; i++)
{
	mixer = AudioSystem.getMixer(mixerinfos[i]);
	System.out.println("\n " + mixerinfos[i].getName() + " (Name: " + mixerinfos[i].getName() +
		" / Vendor: " + mixerinfos[i].getVendor() + " / Version: " + mixerinfos[i].getVersion() + ")");
	
	Line.Info[] sLineInfos = mixer.getSourceLineInfo();
	System.out.println("  Sources (" + sLineInfos.length + "):");
	for(int k=0; k<sLineInfos.length; k++)
	{
		try
		{
			SourceDataLine sLine = (SourceDataLine)mixer.getLine(sLineInfos[k]);
			System.out.println("   Line: " + sLine.getLineInfo() + "\t(Name: " + sLine + ")");
		}
		catch (LineUnavailableException e)
		{
			e.printStackTrace();
		}
		catch(ClassCastException e)
		{
			try
			{
				Clip sClip = (Clip)mixer.getLine(sLineInfos[k]);
				System.out.println("   Clip: " + sClip.getLineInfo() + "\t(Name: " + sClip + ")");
			}
			catch (LineUnavailableException e1)
			{
				e1.printStackTrace();
			}
			catch(ClassCastException e1)
			{
				try
				{
					Port sPort = (Port)mixer.getLine(sLineInfos[k]);
					System.out.println("   Port: " + sPort.getLineInfo() + "\t(Name: " + sPort + ")");
				}
				catch (LineUnavailableException e2)
				{
					e2.printStackTrace();
				}
			}
		}
		
	}
	
	Line.Info[] tLineInfos = mixer.getTargetLineInfo();
	System.out.println("  Targets (" + tLineInfos.length + "):");
	for(int k=0; k<tLineInfos.length; k++)
	{
		try
		{
			TargetDataLine tLine = (TargetDataLine)mixer.getLine(tLineInfos[k]);
			System.out.println("   Line: " + tLine.getLineInfo() + "\t(Name: " + tLine + ")");
		}
		catch (LineUnavailableException e)
		{
			e.printStackTrace();
		}
		catch(ArrayIndexOutOfBoundsException e)
		{
			
		}
		catch(ClassCastException e)
		{
			try
			{
				Clip tClip = (Clip)mixer.getLine(tLineInfos[k]);
				System.out.println("   Clip: " + tClip.getLineInfo() + "\t(Name: " + tClip + ")");
			}
			catch (LineUnavailableException e1)
			{
				e1.printStackTrace();
			}
			catch(ClassCastException e1)
			{
				try
				{
					Port tPort = (Port)mixer.getLine(tLineInfos[k]);
					System.out.println("   Port: " + tPort.getLineInfo() + "\t(Name: " + tPort + ")");
				}
				catch (LineUnavailableException e2)
				{
					e2.printStackTrace();
				}
			}
		}
	}
}
```

Die letzten Zeilen dieser Ausgabe:

```
Port Realtek HD Audio output (Name: Port Realtek HD Audio output / Vendor: Unknown Vendor / Version: 5.10)
  Sources (0):
  Targets (1):
   Port: SPEAKER target port	(Name: com.sun.media.sound.PortMixer$PortMixerPort@6b7920)

Port Realtek HD Audio Input (Name: Port Realtek HD Audio Input / Vendor: Unknown Vendor / Version: 5.10)
  Sources (3):
   Port: MICROPHONE source port	(Name: com.sun.media.sound.PortMixer$PortMixerPort@5e3974)
   Port: LINE_IN source port	(Name: com.sun.media.sound.PortMixer$PortMixerPort@df503)
   Port: Stereo Mix source port	(Name: com.sun.media.sound.PortMixer$PortMixerPort@50d89c)
  Targets (0):
```

...sehen perfekt aus finde ich, nur leider kann ich mit Ports nichts anfangen.

Hat jemand eine Möglichkeit wie ich über ein vorher eingestelltes Gerät über nur einen Port (Audio)Daten beziehen kann?


Vielen Dank für eure Hilfe, XYSAWQ.


----------



## foobar (1. Sep 2008)

Genau das selbe habe ich auch mal gemacht und mußte festellen, daß die Latenz viel zu groß ist. Daher ist es ratsam direkt mit einer RTP Api wie Fmj zu arbeiten.


----------



## xysawq (2. Sep 2008)

Geht nicht... bin hier ein blöder Auszubildender im IT-Bereich von Siemens (Juhuu.. ich werde sowieso nicht übernommen! Und wenn doch, dann werde ich in 2 Jahren wieder entlassen.)

Deshalb soll ich schön alles selber schreiben und möglichst wenig Abhängigkeiten aufbauen, damit später mein Code auf allen Möglichen Systemen zum Einsatz kommen kann (ich mache ja auch die Arbeit von meinem Betreuer).

Und seit wann können RTP-APIs mit Mikrofonen umgehen? Ich dachte die sind nur zur Paketkontrolle.
Und wegen der Latenz... also ich habe Latenzen von ca. 14 bis 70 Millisekunden (je nach Anzahl und Verstreuung der Verbindungen) und schaffe 1000 RTP/RTCP-Sessions auf meinem kleinen Dualcore hier bei 70% CPU-Last.
Eigentlich ganz gut finde ich.


----------



## foobar (2. Sep 2008)

> Und seit wann können RTP-APIs mit Mikrofonen umgehen?


FMJ ist die opensource Variante von JMF, damit ist man nicht mehr auf die nervige Javasound-Api angewiesen. Ausserdem gibts da auch Libs für Rtp und ähnliches.

Wie überträgst du denn die Pakete? Nutzt du Datagrammsockets?



> Geht nicht... bin hier ein blöder Auszubildender im IT-Bereich von Siemens (Juhuu.. ich werde sowieso nicht übernommen! Und wenn doch, dann werde ich in 2 Jahren wieder entlassen.)


Erstmal abwarten und Tee trinken. Wer weiß was die Zukunft bringt.


----------



## xysawq (2. Sep 2008)

Jup... nehme DatagramSockets und DatagramPackets, funktioniert alles echt super, eben bis auf das mit den Mikros, es geht ja, wenn ich für den Input einfach eine TargetDataLine nehme und für den Output eine SourceDataLine, aber das ist mir zu allgemein, würde am Ende gerne Quelle und Ziel auswählen lassen können ohne, dass es Probleme gibt.

Hab auch daran gedacht so ne Art "Freisprechfunktion" einzubauen, dass man einstellen kann, dass der Sound von einem Headsetmikro aufgenommen und in seinen Kopfhörern ausgegeben wird, aber man während des Gesprächs den Sound auch auf die Speaker umleiten kann (wenn man z.B. ein USB-Headset benutz, was ja sozusagen eine eigene Soundkarte ist).


----------



## tuxedo (2. Sep 2008)

Verstehe das Problem nicht. Du kannst doch bestimmen von welchem Device du audio entgegen nimmst? Und ebenso kannst du das dann beliebig verteilen, versenden und/oder auf ein oder mehreren Devices wieder ausgeben. Dafür brauchst du aber hier und da einen Thread der den Stream anzapft und die bytes entsprechend verteilt und weiterreicht/umleitet.

Alles was du eigentlich brauchst ist http://www.jsresources.org/, Knowhow bzgl. Threads und Socketkommunikation mit UDP. 

Innerhalb Deutschland/Europa kommt man übrigens auch noch mit TCP zurecht (hat in meiner Diplomarbeit bestens geklappt). Allerdings sollte man die Audiopakete mit etwas passenden, z.B. JSpeex (google hilft), komprimieren.

- Alex


----------



## xysawq (2. Sep 2008)

Hust hust... 2 Sachen:

1. Auf der Seite habe ich mich schon dumm und dämlich gesucht und nichts gefunden, was mir in irgendeiner Weise zeigt, wie ich z.B. NUR vom Line-In-Eingang Sound aufnehme und an einem ganz bestimmt Ausgang wieder ausgebe.

Ich weiß wie ich die entsprechenden Ports (MICROPHONE, LINE_IN, StereoMix, SPEAKER etc.) auswähle, aber mit Ports kann man nichts machen, da sie anscheinend (noch) nicht unterstützt werden.


2. KnowHow bezüglich Threads und Socketkommunikation mit UDP habe ich bereits, sonst würde mein Programm garnicht lauffähig sein, das läuft wunderbar, ich kann mit anderen auf der ganzen Welt (auch mit anderen RTP-Programmen) Audio austauschen, bloß eben läuft das bei mir mit der Aufnahme und der Ausgabe der Töne nicht so wie ich das gerne hätte.

EDIT: Komprimieren darf ich aus meheren Gründen nicht, zum einen, weil ich mit allen möglichen anderen RTP-Programmen kompatibel sein muss und zum zweiten, weil mir das mein blöder Betreuer verboten hat!


----------



## tuxedo (2. Sep 2008)

Zu 1)

? Du kannst doch Target- und Sourcedataline frei einstellen? Und da hast du dann entsprechden Ein- und Ausgabestreams. Und da kannst du den Strom in Form von bytes einfach anzapfen?!


----------



## xysawq (3. Sep 2008)

Und genau das würde ich ja gerne, aber entweder mache ich grundlegend etwas falsch, oder es geht einfach nicht.

Ich hoffe ja, dass ich was falsch mache... deshalb bitte ich dich mir mal in Code-Form zu zeigen, wie du dir das vorgestellt hast.

Momentan mache ich es so:

```
TargetDataLine line = null;
try
{
	line = (TargetDataLine) AudioSystem.getLine(info);
	line.open(getAudioFormat());
}
catch (LineUnavailableException e)
{
	e.printStackTrace();
}

//...

private static AudioFormat getAudioFormat()
{
	AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED;

	float sampleRate = 41000.0F;
	int sampleSizeInBits = 16;
	int channels = 1;
	int frameSize = 2;
	int frameRate = 41000;
	boolean bigEndian = false;			
	
	return new AudioFormat(encoding,
					   sampleRate,
					   sampleSizeInBits,
					   channels,
					   frameSize,
					   frameRate,
					   bigEndian);
}
```

Ich könnte mir auch von einem Mixer die Line-Infos geben lassen und nachsehen was ich denn nehmen soll, aber wirklich auswählen was jetzt Line-In und was Mikrofon ist kann man nur, wenn man dazu Ports nimmt, und Ports sind wie ein Computer ohne Strom: nicht zu gebrauchen.


----------



## tuxedo (3. Sep 2008)

http://www.jsresources.org/faq_audio.html#capture_source


----------



## xysawq (3. Sep 2008)

Wow... also ich lese mich gerade rein, jetzt weiß ich endlich was Ports für nen Sinn haben.. danke.. werde dann später hier noch posten, was ich erreicht habe.

P.S.: Hab da irgendwie immer nur beim Code gesucht, aber da nicht.
Und ich hab noch nen guten Link: >klicke hier<
Der erklärt die Controls, das einzig sinnvolle an Ports!


----------



## xysawq (3. Sep 2008)

So... momentan habe ich das hier für meine "Auswahl" des Inputs, in diesem Fall für das Mikrofon, geschrieben, wenn man dann in die Volume-Control schaut ist das Mikrofon ausgewählt und hat volle Lautstärke und alle anderen sind nicht ausgewählt und haben die Lautstärke ganz unten:


```
import javax.sound.sampled.*;

public class VolumeTest
{
	public static void main(String[] args)
	{
		
		Mixer mixer = null;
		Mixer.Info[] mixerinfos = AudioSystem.getMixerInfo();
		boolean mixerFound = false;
		
		Port port = null;
		
		for(int i=0; i<mixerinfos.length && !mixerFound; i++)
		{
			mixer = AudioSystem.getMixer(mixerinfos[i]);
			
			try
			{
				port = (Port) mixer.getLine(Port.Info.MICROPHONE);
				port.open();
				
				CompoundControl pCompoundControl = (CompoundControl)port.getControls()[0];
				BooleanControl pSelect = (BooleanControl)pCompoundControl.getMemberControls()[0];
				FloatControl pVolume = (FloatControl)pCompoundControl.getMemberControls()[1];
				FloatControl pBalance = (FloatControl)pCompoundControl.getMemberControls()[2];
				
				pSelect.setValue(true);
				pVolume.setValue(pVolume.getMaximum());
				
				pBalance.setValue(0);
				
				Line.Info[] sli = mixer.getSourceLineInfo();
				Port[] otherPorts = new Port[sli.length-1];
				
				int skip = 0;
				for(int k=0; k<sli.length; k++)
				{
					Port tempPort = (Port) mixer.getLine(sli[k]);
					
					if(tempPort.equals(port))
					{
						skip = 1;
					}
					else
					{
						otherPorts[k-skip] = tempPort;
					}
				}
				
				for(int k=0; k<otherPorts.length; k++)
				{
					otherPorts[k].open();
					
					CompoundControl compoundControl = (CompoundControl)otherPorts[k].getControls()[0];
					BooleanControl select = (BooleanControl)compoundControl.getMemberControls()[0];
					FloatControl volume = (FloatControl)compoundControl.getMemberControls()[1];
					FloatControl balance = (FloatControl)compoundControl.getMemberControls()[2];
					
					select.setValue(false);
					volume.setValue(volume.getMinimum());
					
					balance.setValue(0);
					
					otherPorts[k].close();
				}
				
				System.out.println("\"" + mixerinfos[i].getName() + "\" is used with \"" + port.getLineInfo() + "\".");
				mixerFound = true;
			}
			catch (LineUnavailableException e)
			{
				mixer.close();
			}
			catch(IllegalArgumentException e)
			{
				mixer.close();
			}
		}
	}
}
```


----------

