2010-02-16 6 views
7

Ho una raccolta di file WAV brevi che vorrei elaborare in Java utilizzando vari algoritmi di elaborazione del segnale digitale. Ho bisogno di ottenere una matrice di campioni con valore int per questo scopo, codificata alla frequenza di fotogrammi di 11025 Hz.Conversione immediata della velocità di campionamento durante la lettura di un file WAV in un array di campioni con Java

I file di origine hanno diverse frequenze di campionamento, tra cui 11025 Hz e 44100 Hz. Ecco il codice che sto cercando di usare per leggerli:

// 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 

Il problema è, che il codice non gestisce diverse frequenze di campionamento in modo corretto. Quindi per il frame rate di 44100 Hz ottengo quattro volte il numero di campioni rispetto al frame rate di 11025 Hz. Vorrei che l'array campione risultante usasse la frequenza fotogrammi di 11025 Hz, indipendentemente dalla frequenza dei fotogrammi del file sorgente. Ho provato a forzare Java per convertire il frame rate per me quando la lettura del AudioInputStream, ma ottengo un'eccezione simile al seguente:

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) 

ho letto il tutorial di API Java Audio: http://java.sun.com/docs/books/tutorial/sound/converters.html. Sembra che l'API Java Sound non supporti questo tipo di conversione del mio sistema operativo (Windows 7). E vorrei evitare dipendenze da qualsiasi libreria esterna. C'è un modo per fare la conversione della frequenza di campionamento per conto mio?

risposta

6

Per frequenze di campionamento> 11025 Hz è necessario eseguire il downsampling, che è un processo a due fasi. Innanzitutto è necessario un filtro passa-basso per soddisfare il criterio di Nyquist e quindi è possibile decimare, ad es. per i dati della frequenza di campionamento a 44,1 kHz occorrerebbe un filtro passa-basso con una frequenza di taglio di 5,5 kHz e quindi è possibile eliminare 3 campioni su 4 per un rapporto di campionamento in scala 4: 1. Avrai bisogno di un filtro diverso per ogni rapporto di downsampling che vuoi supportare.

+0

Come faccio a calcolare la frequenza di taglio? E perché questo passaggio è necessario? – pako

+2

Il filtraggio è necessario a causa dell'effetto nyquist. In breve: se il SR è 11025 Hz e il tuo ingresso aveva un tono 5.572,5 hz in essa, che sarebbe stato riprodotto come tono di 60 Hz. involucro di Nyquist è totalmente non-armonica (traduzione: suona veramente brutto e cattivo). È necessario filtrare tutti gli input sopra la metà del nuovo sr per eliminare il rumore di nyquist. –

+2

e "filtra tutti gli input sopra la metà del tuo nuovo sr" Voglio dire assicurati che ci sia contenuto zero al di sopra di quella frequenza - e la quantità di filtri e dove li tagli può variare in base al tuo materiale sorgente - ascolta il risultato, sarà ovvio dal rumore aggiunto se il tuo filtro ha bisogno di essere più ripido o ha bisogno di una frequenza di taglio più bassa. –

5

Credo che la risposta accettato risponde un'altra domanda - risolve lo stesso problema (sottocampionamento l'audio), ma in un altro modo (manualmente al posto di usare il java suono API). Ho avuto la stessa cosa e ci sono riuscito.

Il modo corretto (o un modo java suono API) per fare questo è in effetti (come suggerito in http://docs.oracle.com/javase/tutorial/sound/converters.html)

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

problema è che Java Standard non viene fornito con ricampionamento (o conversione addirittura stereo-mono) codice (o almeno non in quella parte del codice - vedi http://www.jsresources.org/faq_audio.html#convert_sample_rate).

Le pagine di jsresources indicano anche le risposte: è sufficiente installare 2 plug-in. Più semplice è quello di installare questi plugin nella directory Extensions, su OSX Lion questo farà il trucco (purché si disponga di 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 

Dopo l'aggiunta di questi file 2 jar, tutto ha funzionato (solo un avvertimento in più: se si vuoi cambiare sia il numero di canali che la frequenza di campionamento, non è ancora supportato come un unico passaggio).

Problemi correlati