# Schlechte Audioqualität (Recording)



## Blakh (25. Mai 2010)

Hallo,

weiss vllt. jemand Rat wie ich das haessliche knacken aus meinen Aufnahmen bekomme? Ich muss PCM aufnehmen und per RTP übertragen. Ich verwende Javasound zum Aufnehmen und habe ein beispiel gefunden daraus um eine CustomDatasource für JMF zu erstellen, damit ich JMF für die RTP-Übertragung nehmen kann. Hab schon eine Weile dran rumgespielt, aber der Groschen will nicht fallen. Bin auf dem Gebiet sehr frisch.

Ich post mal meinen Sourcecode.





```
public class LiveAudioStream implements PushBufferStream, Runnable {

	protected ContentDescriptor cd = new ContentDescriptor(
			ContentDescriptor.RAW);
	protected int maxDataLength;
	protected int vez = 0;
	protected AudioInputStream data;
	public AudioInputStream audioStream;
	protected byte[] audioBuffer;
	protected javax.media.format.AudioFormat audioFormat;
	protected boolean started;
	protected Thread thread;
	protected float frameRate = 20f;
	protected BufferTransferHandler transferHandler;
	protected Control[] controls = new Control[0];

	public LiveAudioStream(TargetDataLine targetDataLine) {

		audioStream = new AudioInputStream(targetDataLine);
		audioBuffer = new byte[targetDataLine.getBufferSize()];

		audioFormat = new AudioFormat(AudioFormat.LINEAR, 8000.0F, 8, 1,
				Format.NOT_SPECIFIED, AudioFormat.SIGNED, 8,
				Format.NOT_SPECIFIED, Format.byteArray);
		maxDataLength = targetDataLine.getBufferSize();
		thread = new Thread(this);
	}

	/***************************************************************************
	 * SourceStream
	 ***************************************************************************/

	public ContentDescriptor getContentDescriptor() {
		return cd;
	}

	public long getContentLength() {
		return LENGTH_UNKNOWN;
	}

	public boolean endOfStream() {
		return false;
	}

	/***************************************************************************
	 * PushBufferStream
	 ***************************************************************************/

	int seqNo = 0;
	double freq = 1.0;

	public Format getFormat() {
		return audioFormat;
	}

	public void read(Buffer buffer) throws IOException {
		synchronized (this) {

			Object outdata = buffer.getData();
			if (outdata == null || !(outdata.getClass() == Format.byteArray)
					|| ((byte[]) outdata).length < maxDataLength) {
				outdata = new byte[maxDataLength];

				buffer.setData(audioBuffer);

			}
			audioStream.read(audioBuffer);

			buffer.setFormat(audioFormat);
			buffer.setTimeStamp(1000000000 / 8);

		}

		buffer.setSequenceNumber(seqNo);
		buffer.setLength(maxDataLength);
		buffer.setFlags(0);
		buffer.setHeader(null);
		seqNo++;
	}

	public void setTransferHandler(BufferTransferHandler transferHandler) {
		synchronized (this) {
			this.transferHandler = transferHandler;
			notifyAll();
		}
	}

	void start(boolean started) {
		synchronized (this) {
			this.started = started;
			if (started && !thread.isAlive()) {
				thread = new Thread(this);
				thread.start();
			}
			notifyAll();
		}
	}

	/***************************************************************************
	 * Runnable
	 ***************************************************************************/

	public void run() {
		while (started) {
			synchronized (this) {
				while (transferHandler == null && started) {
					try {
						wait(1000);
					} catch (InterruptedException ie) {
					}
				} // while
			}

			if (started && transferHandler != null) {
				transferHandler.transferData(this);
				try {
					Thread.currentThread().sleep(10);
				} catch (InterruptedException ise) {
				}
			}
		} // while (started)
	} // run

	// Controls

	public Object[] getControls() {
		return controls;
	}

	public Object getControl(String controlType) {
		try {
			Class cls = Class.forName(controlType);
			Object cs[] = getControls();
			for (int i = 0; i < cs.length; i++) {
				if (cls.isInstance(cs[i]))
					return cs[i];
			}
			return null;

		} catch (Exception e) { // no such controlType or such control
			return null;
		}
	}
}
```



```
import javax.media.Time;
import javax.media.protocol.*;
import java.io.IOException;
import java.io.InputStream;
import javax.sound.sampled.AudioInputStream;
 
public class CustomDataSource extends PushBufferDataSource {
 
    protected Object [] controls = new Object[0];
    protected boolean started = false;
    protected String contentType = "raw";
    protected boolean connected = false;
    protected Time duration = DURATION_UNKNOWN;
    protected LiveAudioStream [] streams = null;
    protected LiveAudioStream stream = null;
   
    
    public CustomDataSource(LiveAudioStream ls) {
    	streams = new LiveAudioStream[1];
    	stream = streams[0]= ls;
    }
    
    public String getContentType() {
	if (!connected){
            System.err.println("Error: DataSource not connected");
            return null;
        }
	return contentType;
    }
    public byte[] getData() {
    	return stream.audioBuffer; 
    }
 
    public void connect() throws IOException {
	 if (connected)
            return;
	 connected = true;
    }
 
    public void disconnect() {
	try {
            if (started)
                stop();
        } catch (IOException e) {}
	connected = false;
    }
 
    public void start() throws IOException {
	// we need to throw error if connect() has not been called
        if (!connected)
            throw new java.lang.Error("DataSource must be connected before it can be started");
        if (started)
            return;
	started = true;
	stream.start(true);
    }
 
    public void stop() throws IOException {
	if ((!connected) || (!started))
	    return;
	started = false;
	stream.start(false);
    }
 
    public Object [] getControls() {
	return controls;
    }
 
    public Object getControl(String controlType) {
       try {
          Class  cls = Class.forName(controlType);
          Object cs[] = getControls();
          for (int i = 0; i < cs.length; i++) {
             if (cls.isInstance(cs[i]))
                return cs[i];
          }
          return null;
 
       } catch (Exception e) {   // no such controlType or such control
         return null;
       }
    }
 
    public Time getDuration() {
	return duration;
    }
 
    public PushBufferStream [] getStreams() {
 
	return streams;
    }
    
}
```

Und hier in einer Klasse wo ich den VoiceTransmitter erstelle ...


```
private String createProcessor() {
		if (locator == null) {
			return "Locator is null";
		}
		
		javax.sound.sampled.AudioFormat audioFormat = new javax.sound.sampled.AudioFormat(8000.0F,
				8,
				1,
				true,
				false);
		
		DataLine.Info dataLineInfo = new DataLine.Info( TargetDataLine.class, audioFormat);
		
		
		try {
			targetDataLine = (TargetDataLine)
			           AudioSystem.getLine(dataLineInfo);
			targetDataLine.open(audioFormat);
			 
			 
		} catch (LineUnavailableException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		targetDataLine.start();
		CustomDataSource dss = null;
		try {
		
		LiveAudioStream livestream = new LiveAudioStream(targetDataLine);
		dss = new CustomDataSource(livestream);
		
			dss.connect();
			dss.start();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		

		DataSource ds;

//		try {
//			ds = Manager.createDataSource(dss);
//		} catch (Exception e) {
//			return "Couldn't create DataSource";
//		}

		// Try to create a processor to handle the input media locator
		try {
			processor = Manager.createProcessor(dss);
		} catch (NoProcessorException npe) {
			return "Couldn't create processor";
		} catch (IOException ioe) {
			return "IOException creating processor";
		}
```

Wäre sehr dankbar, wenn mir wer irgendwelche Infos zu dem Thema liefern kann ;(


----------



## maki (25. Mai 2010)

Kenne mich nicht wirklich mit dem Thema aus, aber 8kHz sampling rate? 
Dann hast du ziemlich genau die Qualität von einem alten, analogen Telefon.


----------



## Noctarius (25. Mai 2010)

Jopp was maki meint wäre auch meine Vermutung gewesen (ohne tieferen Einblick in die Materie)


----------



## Hansdampf (25. Mai 2010)

Kommt das Knacken beim Starten/Enden von Sounds? Falls ja, dann "fade" die ersten paar Samples gegen 0.
etwa:

```
public void fadein(int[]data,int end) {
		for (int i = 0; i < end; i++)
			data[i] *= (float) i / end;
	}
```

Knacken kommt bei Javasound häufig von abrupten Start-Samples.


----------



## Guybrush Threepwood (25. Mai 2010)

Wie hört es sich denn an, wenn Du mit einem anderen Aufnahmeprogramm bei gleicher Hardware-Ausstattung aufnimmst? Ist da besser oder gleich?

Gute Audio-Aufnahmen sind nicht ganz einfach hinzubekommen. Just heute kam bei mir eine Lieferung von Hardware-Komponenten (ca. 200 Euro), um qualitativ hochwertige Sprachaufnahmen zu machen (Audio-Instruktionen in Programmen). Das ist also auch eine Frage der Aufnahmetechnik, nicht nur der Software.

Hier ein einfaches Beispielprogramm, mit dem ich gute Aufnahmen hinbekomme:


```
import java.io.File;
import java.io.IOException;

import javax.sound.sampled.AudioFileFormat;
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 Recorder
{
   public static void main(String[] args) throws IOException, InterruptedException
   {
      TargetDataLine line = null;
      DataLine.Info info = new DataLine.Info(TargetDataLine.class, getAudioFormat());
      
      try
      {
         line = (TargetDataLine) AudioSystem.getLine(info);
         line.open(getAudioFormat());
      }
      catch (LineUnavailableException e)
      {
         e.printStackTrace();
      }
      
      line.start();
      AudioInputStream stream = new AudioInputStream(line);

      
      Stopper stopper = new Stopper(line, stream);
      stopper.start();
      
      File file = new File("c:/temp/soundfile.wav");
      AudioSystem.write(stream, AudioFileFormat.Type.WAVE, file);
      
      System.out.println("Stopped...");
      System.in.read();
   }
   
   private static AudioFormat getAudioFormat()
   {
      AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_UNSIGNED;
      
      float sampleRate = 16000.0F;
      int sampleSizeInBits = 8;
      int channels = 1;
      int frameSize = 1;
      int frameRate = 16000;
      boolean bigEndian = false;         
      
      return new AudioFormat(encoding,
                        sampleRate,
                        sampleSizeInBits,
                        channels,
                        frameSize,
                        frameRate,
                        bigEndian);
   }
}
```


```
import java.io.IOException;
import javax.sound.sampled.*;

public class Stopper extends Thread
{
   TargetDataLine line = null;
   AudioInputStream stream = null;
   
   public Stopper(TargetDataLine line, AudioInputStream stream)
   {
      this.line = line;
      this.stream = stream;
   }
   
   public void run()
   {
      System.out.println("Press [RETURN] to stop capturing...");
      
      try
      {
         System.in.read();
      }
      catch (IOException e)
      {}
      
      line.stop();
      try
      {
         stream.close();
      }
      catch (IOException e)
      {}
   }
}
```

Für CD-Qualität müsstest Du beispielsweise die folgenden Einstellungen verwenden:

```
AudioFormat	audioFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100.0F, 16, 2, 4, 44100.0F, false);
```


----------



## Blakh (25. Mai 2010)

Danke fuer eure Antworten .. ich werde morgen auf Arbeit mal testen.

Die Qualitaet ist wirklich schlecht. Ein Rauschen geht in Ordnung, aber es knackt rythmisch und die Stimme kann man teilweise kaum hoeren. Das Problem ist wohl, dass ich nach der Aufnahme mit java.sound das ganze noch durch JMF/RTP jagen muss. Ich muss eigentlich auch darauf achten, dass die Verzoegerung gering bleibt, da es sich um einen Mobilfunkclient handelt. Alles neuland fuer mich und Java zickt leider rum. War kurz davor, aber dann hab ich festgestellt, dass ich jmf-bibliotheken nicht einfach ans Applet haengen kann, da dann eine Permission fehlt, selbst wenn alles signiert ist. Morgen auf Arbeit kann ich mehr dazu sagen.


----------



## guitarflow (25. Mai 2010)

Bin zwar in Java noch nicht so ein Guru, allerdings kenn ich mich mit Tontechnik aus und geb deswegen mal meinen Senf dazu...

Also wie meine Vorredner schon geschrieben haben, sind deine Einstellungen schon sehr grenzwertig!!
8 kHz Samplingrate is viel zu wenig. 
Noch dazu benutzt du dann auch noch nur 8 Bit Auflösung!!

Das is viel zu wenig. Sollten 16 sein. Damit erhöhst du deinen Signal-Rauschabstand schon deutlich und das Quantisierungsrauschen wird erheblich reduziert.

Das Knacken hängt schätzungsweise auch damit zusammen. Deine Soundkarte wird wahrscheinlich entweder mit 44.1kHz oder mit 48 kHz takten. Wenn du da mit 8.000 ankommst, dann bekommst du Probleme mit der Synchronisation. Also immer wenn dein Samplingtakt zu sehr abweicht, dann knacksts.

Kanns dir jetz nicht versprechen, aber ich würde jetz auch mal stark davon ausgehen, dass du bei geeigneten Einstellungen auch die entsprechenden Ergebnisse erzielen solltest.

mfg,

Flo


----------



## Blakh (26. Mai 2010)

Also es hängt eher nicht damit zusammen. Zumal ich davor auch mit den selben Einstellungen übertragen habe.

http://java.sun.com/javase/technologies/desktop/media/jmf/2.1.1/solutions/AVTransmit2.java

So hatte ich es und es lief gut, aber will das Applet halt loskoppeln von JMF. Es muss irgendwas mit dem Buffern zu tun haben *verzweifel* ;( ;(


----------



## Blakh (26. Mai 2010)

Ok hab weiter gesucht und gebastelt und hab eine abgewandelte Form von dem hier jetzt:

Java Media Framework - Custom JavaSound-capturing DataSource

Was wiederum auf dem hier basiert:

Generating Live Audio/Video Data

Das ist besser ... es knackt nicht mehr etc..

Danke euch trotzdem...


----------

