# Javasound - Kann Lautstärke nicht anpassen



## AlexDozer (25. Sep 2010)

Hi,

ich benutze in meinem neuen Projekt mp3 SPI. Dies basiert bzw. erweitert ja Javasound. Leider hab ich das Problem
das beim beenden des Sound es immer knackst. Dies wollte ich damit umgehen das ich vorher die Lautstärke auf 0 setze. 

Aus dem Internet hab ich folgenden Code:

```
FloatControl control = (FloatControl) _sourceDataLine.getControl(FloatControl.Type.VOLUME);
control.setValue(0);
```

Wenn ich diesen Code anwende erhalte ich folgende Exception:

```
Exception in thread "main" java.lang.IllegalArgumentException: Unsupported control type: Volume
	at com.sun.media.sound.AbstractLine.getControl(Unknown Source)
	at Sound.stopSound(Sound.java:86)
	at Main.main(Main.java:19)
```

Hier die ganze Klasse:

```
public class Sound extends Thread{
	
	private Boolean _playSound;
	private AudioFormat _audioFormat;
	private AudioInputStream _audioInputStream;
	private SourceDataLine _sourceDataLine;
	
	public Sound(String filename)
	{
	    try 
	    {
	    	File file = new File(filename);
	    	AudioInputStream in= AudioSystem.getAudioInputStream(file);
	    	_audioInputStream = null;
	    	AudioFormat baseFormat = in.getFormat();
	    	_audioFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
	                                       baseFormat.getSampleRate(),
	                                       16,
	                                       baseFormat.getChannels(),
	                                       baseFormat.getChannels() * 2,
	                                       baseFormat.getSampleRate(),
	                                       false);
	    	
	    	_audioInputStream = AudioSystem.getAudioInputStream(_audioFormat, in);
	    	_playSound = true;
	    } 
	    catch (Exception e)
	    {
		    e.printStackTrace();
	    }
	}
	
	public void run()
	{
		try
		{
			byte[] data = new byte[4096];
			
			_sourceDataLine = getLine(_audioFormat);
			if (_sourceDataLine != null)
			{
			    // Start
				_sourceDataLine.start();
			    int nBytesRead = 0;
			    while (nBytesRead != -1 && _playSound)
			    {
			        nBytesRead = _audioInputStream.read(data, 0, data.length);
			        if (nBytesRead != -1) 
			        	_sourceDataLine.write(data, 0, nBytesRead);
			    }
			    // Stop
			    _sourceDataLine.drain();
			    _sourceDataLine.stop();
			}
		}
		catch(Exception e)
		{
			
		}
	}

	public void playSound()                                                                                               
	{
		start();
	}
	
	public void stopSound() 
	{
		FloatControl control = (FloatControl) _sourceDataLine.getControl(FloatControl.Type.VOLUME);
		control.setValue(0);
		
		_sourceDataLine.stop();
	}

	private SourceDataLine getLine(AudioFormat audioFormat) throws LineUnavailableException
	{
	  SourceDataLine res = null;
	  DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
	  res = (SourceDataLine) AudioSystem.getLine(info);
	  res.open(audioFormat);
	  return res;
	}
}
```

Was läuft hier falsch? Bei dem Beispiel im Internet wird das auf genau den selben Typ, nämlich SourceInputLine, angwendet. Oder hat jemand eine andere Idee das Knacksen wegzukriegen?

Gruß Alex


----------



## Ralph-Uwe (26. Sep 2010)

Hi,

ersetze mal in der stopSound Methode "Volumen" durch Master_Gain.


```
public void stopSound() 
    {
        FloatControl control = (FloatControl) _sourceDataLine.getControl(FloatControl.Type.MASTER_GAIN);
        control.setValue(0);
        
        _sourceDataLine.stop();
    }
```


----------



## AlexDozer (26. Sep 2010)

Hallo Ralph-Uwe,

leider funktioniert dies auch nicht. Es wird zwar keine Exception geworfen, aber es haut auch keine Auswirkungen d.h. die Laustärke ändert sich dadurch nicht. 

Gruß Alex


----------



## Ralph-Uwe (26. Sep 2010)

Guten Morgen,

ich habe Deine Code genommen, die Änderung wie beschrieben durchgeführt und mit folgender main-Methode gestartet
und gestoppt:


```
private static Sound2 sound2 = new Sound2("f:/Audio/AudioTrack.mp3");

 public static void main(String args[]) throws MalformedURLException {
		 sound2.playSound();
		 try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		sound2.stopSound();
		 
	 }
```

sound starten und nach 5 sec wieder stoppen. Wobei die Wiedergabe nicht gestoppt wird sondern nur die Lautstärke auf
0 reduziert wird. Der Thread selber läuft weiter.


----------



## Ralph-Uwe (26. Sep 2010)

ich muss mich korrigieren,

wenn das Musikstück zu ende ist, stoppt der Thread.
Ich hatte im meinem Testprogramm noch andere implementationen die schuld waren, dass 
der Thread nicht gestoppt hat. :-(


----------



## AlexDozer (26. Sep 2010)

Hmm, komisch das das bei dir einwandfrei funktioniert. Ich hab jetzt eine andere Lösung gefunden das Knacksen zu umgehen:


```
public class Sound {
	
	private Boolean _playSound;
	private AudioFormat _audioFormat;
	private AudioInputStream _audioInputStream;
	private SourceDataLine _sourceDataLine;
	private Thread _thread;
	
	public Sound(String filename)
	{
	    try 
	    {
	    	File file = new File(filename);
	    	AudioInputStream in= AudioSystem.getAudioInputStream(file);
	    	_audioInputStream = null;
	    	AudioFormat baseFormat = in.getFormat();
	    	_audioFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
	                                       baseFormat.getSampleRate(),
	                                       16,
	                                       baseFormat.getChannels(),
	                                       baseFormat.getChannels() * 2,
	                                       baseFormat.getSampleRate(),
	                                       false);
	    	
	    	_audioInputStream = AudioSystem.getAudioInputStream(_audioFormat, in);
	    	_playSound = true;
	    } 
	    catch (Exception e)
	    {
		    e.printStackTrace();
	    }
	}
	
	public void playSound()                                                                                               
	{
		_playSound = true;
		_thread = new Thread() {
			
			public void run()
			{
				try
				{
					byte[] data = new byte[4096];
					
					_sourceDataLine = getLine(_audioFormat);
					if (_sourceDataLine != null)
					{
					    // Start
						_sourceDataLine.start();
					    int nBytesRead = 0;
					    while (nBytesRead != -1 && _playSound)
					    {
					        nBytesRead = _audioInputStream.read(data, 0, data.length);
					        if (nBytesRead != -1) 
					        	_sourceDataLine.write(data, 0, nBytesRead);
					    }
					    // Stop
					    _sourceDataLine.drain();
					    _sourceDataLine.stop();
					}
				}
				catch(Exception e)
				{
					
				}
			}
		
		};
		_thread.start();
	}
	
	public void stopSound() 
	{
		_playSound = false;
	}

	private SourceDataLine getLine(AudioFormat audioFormat) throws LineUnavailableException
	{
	  SourceDataLine res = null;
	  DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
	  res = (SourceDataLine) AudioSystem.getLine(info);
	  res.open(audioFormat);
	  return res;
	}
}
```

Ich halte die Schleife, welche die Daten in die SourceDataLine schaufelt, einfach an. Bei diesem Verfahren ist kein
Knachsen zu hören. 

Allerdings bleibt noch das Problem das wenn der Sound wieder gestartet wird, das er dann da anfängt wo er vorher gestoppt wurde. Aber das krieg ich auch noch hin.


----------



## Ralph-Uwe (26. Sep 2010)

Ich glaube ich war auch im falschen Märchen ;-) . Die Lautstärkeeinstellung funktionierte so auch nicht.

Das Einstellen der Lautstärke muss in die Schleife, in der die Daten von der Festplatte geladen werden und an die Soundkarte
weiter gereicht werden, da der Thread in der while-Scheife steckt.
Sonst wird auf die Lautstärke Änderung nicht reagiert.

Deine Sound Klasse würde so funktionieren:

```
public class Sound extends Thread{
    
    private Boolean _playSound;
    private AudioFormat _audioFormat;
    private AudioInputStream _audioInputStream;
    private SourceDataLine _sourceDataLine;
    private FloatControl control; //  neu FloatControl für den ganzen Thread zugänglich machen
    private int gainPercent = 90;  //gibt die Lautstärke in Prozent an.  (0% = -80dB und 100% = 6dB) //neu Anfangslautstärke
    
    public Sound(String filename)
    {
        try 
        {
            File file = new File(filename);
            AudioInputStream in= AudioSystem.getAudioInputStream(file);
            _audioInputStream = null;
            AudioFormat baseFormat = in.getFormat();
            _audioFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
                                           baseFormat.getSampleRate(),
                                           16,
                                           baseFormat.getChannels(),
                                           baseFormat.getChannels() * 2,
                                           baseFormat.getSampleRate(),
                                           false);
            
            _audioInputStream = AudioSystem.getAudioInputStream(_audioFormat, in);
            
            _playSound = true;
        } 
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
    
    public void run()
    {
        try
        {
            byte[] data = new byte[4096];
            
            _sourceDataLine = getLine(_audioFormat);
            control = (FloatControl) _sourceDataLine.getControl(FloatControl.Type.MASTER_GAIN); // neu hier das control besorgen
            if (_sourceDataLine != null)
            {
                // Start
                _sourceDataLine.start();
                int nBytesRead = 0;
                while (nBytesRead != -1 && _playSound)
                {
                	int gainLevel= (int) ((int)control.getMinimum()+((control.getMaximum()-control.getMinimum())/100*gainPercent)); // Berechnung der Lautstärke  (0% = -80dB und 100% = 6dB)
                	control.setValue(gainLevel); // neu Lautstärke in der While-Scheife
                	nBytesRead = _audioInputStream.read(data, 0, data.length);
                    if (nBytesRead != -1) 
                        _sourceDataLine.write(data, 0, nBytesRead);
                }
                // Stop
                _sourceDataLine.drain();
                _sourceDataLine.stop();
            }
        }
        catch(Exception e)
        {
            
        }
    }
 
    public void playSound()                                                                                               
    {
        start();
    }
    
	public void setVolumen(int volumen){
		if ((volumen <= 100) || (volumen >= 0)){
			gainPercent = volumen; // Setzen der Lautstärke zwischen 0 und 100 %
		}
		
	}
    
    
    
    public void stopSound() 
    {
         _sourceDataLine.stop();
    }
 
    private SourceDataLine getLine(AudioFormat audioFormat) throws LineUnavailableException
    {
      SourceDataLine res = null;
      DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
      res = (SourceDataLine) AudioSystem.getLine(info);
      res.open(audioFormat);
      return res;
    }
}
```

das Hauptprogramm so:

```
public static void main(String args[]) throws MalformedURLException {
		 sound2.playSound();
		 try {
			 Thread.sleep(5000);
			 sound2.setVolumen(90);
			 Thread.sleep(5000);
			 sound2.setVolumen(80);
			 Thread.sleep(5000);
			 sound2.setVolumen(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

	 }



}
```

Ich hoffe, dass ich Dir hiermit weiterhelfen konnte.


----------



## AlexDozer (26. Sep 2010)

Danke Ralph-Uwe,

das funktioniert einwandfrei! Jetzt kann ich mich endlich an den 
anderen Baustellen weiterarbeiten! 

Gruß Alex


----------



## Ralph-Uwe (26. Sep 2010)

kannst Du auch MP3 abspielen?


----------



## AlexDozer (26. Sep 2010)

Ja, habs bisher nur mit Mp3 probiert. Das ganze wird in einem Applet benutzt, von daher muss der Sound wenig Platz verbrauchen. Das schliesst Wave von vornherein aus.


----------

