2010-02-16 10 views
7

Ich habe eine Sammlung kurzer WAV-Dateien, die ich in Java mit verschiedenen digitalen Signalverarbeitungsalgorithmen verarbeiten möchte. Ich muss zu diesem Zweck eine Reihe int-bewerteter Samples erhalten, die mit der Framerate von 11025 Hz kodiert sind.Konvertieren der Abtastrate während des Einlesens einer WAV-Datei in ein Samples-Array mit Java

Die Quelldateien haben verschiedene Abtastraten, einschließlich 11025 Hz und 44100 Hz. Hier ist der Code, den ich zu verwenden, ich versuche, sie zu lesen:

// read the WAV file 
FileInputStream fileInputStream = new FileInputStream(new File("test.wav")); 
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(fileInputStream); 

// copy the AudioInputStream to a byte array called buffer 
ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
byte[] data = new byte[4096]; 
int tempBytesRead = 0; 
int byteCounter = 0; 
while ((tempBytesRead = audioInputStream.read(data, 0, data.length)) != -1) { 
    bos.write(data, 0, tempBytesRead); 
      byteCounter += tempBytesRead; 
} 
bos.close(); 
byte[] buffer = bos.toByteArray(); 

AudioFileFormat audioFileFormat = new AudioFileFormat(AudioFileFormat.Type.WAVE, audioInputStream.getFormat(), (int)audioInputStream.getFrameLength()); 

// get the resulting sample array 
int[] samples = new int[audioFileFormat.getFrameLength()]; 
for (int i = 0; i < samples.length; i++) { 
    samples[i] = getSampleValue(i); // the getSampleValue method reads the sample values from the "buffer" array, handling different encoding types like PCM unsigned/signed, mono/stereo, 8 bit/16 bit 
} 

// RESULT: the "samples" array 

Das Problem ist, dass der Code nicht Raten unterschiedliche Probe richtig umgehen kann. Für die Framerate von 44100 Hz bekomme ich also viermal so viele Samples wie für die Framerate von 11025 Hz. Ich möchte, dass das resultierende Sample-Array die Framerate von 11025 Hz verwendet, unabhängig von der Framerate der Quelldatei. Ich habe versucht, Java zu zwingen, die Frame-Rate für mich zu konvertieren, wenn die AudioInputStream lesen, aber ich habe eine ähnliche Ausnahme der folgenden ein:

java.lang.IllegalArgumentException: Unsupported conversion: PCM_SIGNED 11025.0 Hz, 16 bit, mono, 2 bytes/frame, 44100.0 frames/second, little-endian from PCM_SIGNED 44100.0 Hz, 16 bit, mono, 2 bytes/frame, little-endian 
    at javax.sound.sampled.AudioSystem.getAudioInputStream(AudioSystem.java:955) 

ich die Java-Sound-API-Tutorial lesen: http://java.sun.com/docs/books/tutorial/sound/converters.html. Es scheint, dass die Java Sound API diese Art der Konvertierung meines Betriebssystems (Windows 7) nicht unterstützt. Und ich möchte Abhängigkeiten von irgendwelchen externen Bibliotheken vermeiden. Gibt es eine Möglichkeit, die Abtastratenkonversion selbst durchzuführen?

Antwort

6

Bei Abtastraten> 11025 Hz müssen Sie eine Downsample-Messung durchführen, was ein zweistufiger Prozess ist. Zuerst müssen Sie einen Tiefpassfilter verwenden, um das Nyquist-Kriterium zu erfüllen, und dann können Sie z. Für Sampleraten mit 44,1 kHz müssen Sie einen Tiefpassfilter mit einer Grenzfrequenz von 5,5 kHz verwenden und dann können Sie 3 von 4 Samples für ein 4: 1 Downsampling-Verhältnis wegwerfen. Sie benötigen für jedes Downsampling-Verhältnis, das Sie unterstützen möchten, einen anderen Filter.

+0

Wie berechne ich die Grenzfrequenz? Und warum ist dieser Schritt notwendig? – pako

+2

Die Filterung ist aufgrund des Nyquist-Effekts notwendig. Kurz gesagt: Wenn Ihr sr 11025 Hz ist und Ihr Eingang einen 5572,5 Hz-Ton hat, würde das als 60 Hz-Ton wiedergegeben werden. Nyquist Wrap ist völlig harmonisch (Übersetzung: klingt wirklich hässlich und schlecht). Sie müssen alle Eingaben oberhalb der Hälfte Ihres neuen sr filtern, um Nyquist-Rauschen zu eliminieren. –

+2

und durch "filter alle Eingabe über die Hälfte Ihrer neuen sr" Ich meine, stellen Sie sicher, dass es keine Inhalte über dieser Frequenz ist - und die Menge der Filterung und wo Sie es ausschneiden können basierend auf Ihrem Ausgangsmaterial variieren - hören Sie das Ergebnis, Aus dem hinzugefügten Geräusch wird es deutlich, wenn Ihr Filter steiler sein muss oder eine niedrigere Grenzfrequenz benötigt. –

5

Ich glaube, die angenommene Antwort beantwortet eine andere Frage - es löst das gleiche Problem (Downsampling der Audio), aber auf andere Weise (manuell anstelle der Java-Sound-API). Ich hatte das gleiche und grub mich hinein.

Der richtige Weg (oder Java-Sound-API Weg) dies in der Tat zu tun (wie in http://docs.oracle.com/javase/tutorial/sound/converters.html vorgeschlagen)

AudioFormat outDataFormat = new AudioFormat((float) 8000.0, (int) 8, (int) 1, true, false); 
AudioInputStream lowResAIS = AudioSystem.getAudioInputStream(outDataFormat, inFileAIS); 

Problem ist, dass Standard-Java nicht mit Resampling mitliefert (oder sogar Stereo-Mono-Konvertierung) Code (oder zumindest nicht in diesem Teil des Codes - siehe http://www.jsresources.org/faq_audio.html#convert_sample_rate).

Die jsresources-Seiten verweisen auch auf die Antworten: Die Installation von zwei Plugins macht den Trick. Einfachste ist es, diese Plugins im Extensions-Verzeichnis zu installieren, auf OSX Lion dies den Trick tun wird (vorausgesetzt, Sie haben wget):

wget http://www.tritonus.org/tritonus_share-0.3.6.jar -O /Library/Java/Extensions/tritonus_share-0.3.6.jar 
wget http://www.tritonus.org/tritonus_remaining-0.3.6.jar -O /Library/Java/Extensions/tritonus_remaining-0.3.6.jar 

Nachdem diese zwei JAR-Dateien hinzufügen, alles hat funktioniert (nur eine zusätzliche Warnung: Wenn Sie möchte sowohl die Anzahl der Kanäle als auch die Abtastrate ändern, wird jedoch nicht als ein Schritt unterstützt).