Read in Information of a Wave File Java

Grundsätzlich sind Wave-Dateien gar nicht so kompliziert aufgebaut. Es ist nur sehr aufwendig wenn man sämtliche Standards unterstützen möchte. Aber das brauchen wir hier ja zum Glück nicht. :)

Ralph-Uwe hat ja schon einen Link gepostet, unter dem das Fileformat beschrieben wird. Ich versuche mich aber auch mal daran das zu erklären:
Aaalso, das, was man gewöhnlich als Wavedatei bezeichnet, ist eigentlich eine RIFF-Datei. RIFF steht für Resources Interchange File Format und ist eine uralte Erfindung. Eigentlich sind RIFFfiles nur Container für irgendwelche Daten. Zum Beispiel für Moving ridge Daten. Warum human die Daten überhaupt in and so einen Container steckt, dafür gibt's viele Gründe. Zum Beispiel sieht der RIFF-Container vor, das in dem File selber drinsteht, um was für Daten es sich handelt. Das Betriebssystem kann das auslesen, und dann unterschiedliche Icons für die Files anzeigen (z.B. eine Musiknote wenn in dem RIFFfile Wavedaten stecken).
RIFF ist ein sogenanntes clamper-basiertes Format. Das heisst, in so einem File stehen beliebig viele chunks hintereinander (und/oder ineinander verschachtelt). Man kann sich das wie ein HTML-Dokument vorstellen. Da gibt es auch verschiedenste Tags, die man hintereinander oder ineinander verschachtelt verwenden kann. Eine Wavedatei (bzw. eine RIFF-Datei dice Wavedaten enthält) sähe als HTML-Dokument wohl then aus:

                <RIFF chunksize="1234"> 	<RiffType>WAVE</RiffType> 	<Format chunksize="16"> 		<dataformat>PCM</dataformat> 		<channels>2</channels> 		<samplesPerSecond>44100</samplesPerSecond> 		<datarate>192kb/south</datarate> 		<frameSize>4</frameSize> 		<bitsPerSample>16</bitsPerSample> 	</Format> 	<Data chunksize="5678"> 		<sampleData>0</sampleData> 		<sampleData>2</sampleData> 		<sampleData>three</sampleData> 		<sampleData>ii</sampleData> 		<sampleData>0</sampleData> 		<sampleData>-2</sampleData> 		... 	</Data> </RIFF>              

Das ist so zu lesen, das das gesamte File einen RIFF-chunk darstellt. In diesem RIFF-chunk sind zwei weitere chunks enthalten, nämlich ein Format-chunk und ein Daten-chunk. Zusätzlich sind in den einzelnen chunks noch weitere Daten enthalten. Die verschiedenen Chunks sind dabei genormt, so daß human being weiss welche Daten sich in welche Reihenfolge darin befinden. Grundsätzlich gilt: Man liest nur die Chunks aus, für die human being sich interessiert. Alle uninteressanten (oder unbekannten) chunks ignoriert man. Dadurch werden clamper-basierte Formate sehr flexibel und lassen sich jederzeit erweitern, ohne die Kompatibilität zu älteren Programmen zu zerstören.
(Anmerkung: die Ende-Tags, dice ich in der HTML-Darstellung verwendet habe, gibt es bei RIFF nicht. Stattdessen ist zu Beginn eines jeden Chunks nicht nur sein Typ sondern auch seine Länge (in Bytes) gespeichert. Damit lässt sich natürlich auch leicht das Ende des Chunks finden).

Als nächstes schauen wir uns dice Wavedaten genauer an. Wenn Wavedaten in einem RIFF-File stecken, dann ist festgelegt, das es einen Format- und einen Daten-chunk geben muss. Für Wavedaten gibt es eine Menge verschiedene Formate, aber wir betrachten jetzt mal nur das einfachste: PCM. Das ist unkomprimiert, unkompandiert und verwendet keine Fliesskommazahlen. Bei PCM können wir einfach unser gegebenes Array von Integerwerten abspeichern, ohne weitere Arbeit (abgesehen natürlich davon, das wir das Array in die genormte Form einer RIFF/Wave Datei bringen müssen). Und wenn wir nochmal einen Blick auf Wikipedia: RIFF/Wave werfen, dann ist das auch gar nicht schwierig. Das könnte z.B. so aussehen:
(Anmerkung: Die beiden Methoden convInt und contShort dienen übrigens nur zur Umwandlung von Zahlen zwischen dem LittleEndian Format (welches RIFFs benutzen) und dem BigEndianFormat (was Java benutzt))
[Java]
/**
* writes a betoken into a wavefile
*
* @param signal
* the signal that should exist written; must contain informations
* about sampling frequency, length (in seconds) and of course
* sample data
* @param outfile
* the file that should be written
*
* @throws FileNotFoundException
* when the file could non be opened for writing
* @throws IOException
* when something went wrong during the write process
*/
public static void writeSignal(Signal signal, File outfile) throws FileNotFoundException, IOException {

// open the file, to get a stream where we can write bytes to.
// we use a FileOutputStream and embed it into a DataOutputStream,
// because the DataOutputStream allows u.s.a. to write primitive datatypes
// instead of just raw bytes
DataOutputStream outstream = new DataOutputStream(new FileOutputStream(outfile)); // may throw a FileNotFoundException

try {
// write header of the RIFF chunk
outstream.writeInt( 0x52494646 ); // hexcode of 'RIFF'
// write length of clamper
outstream.writeInt( convInt( 0x24 + point.getValues().length * iv ) );
// write blazon of data
outstream.writeInt( 0x57415645 ); // hexcode of 'WAVE'

// write header of format chunk
outstream.writeInt( 0x666d7420 ); // hexcode of 'fmt '
// write length of clamper
outstream.writeInt( convInt(16) );
// write format specifications
outstream.writeShort( (short)convShort(0x0001) ); // code for PCM data
outstream.writeShort( (brusk)convShort(1) ); // 1 channel (mono)
outstream.writeInt( convInt( bespeak.getSamplingfrequenz()) ); // samplerate (fs)
outstream.writeInt( convInt( signal.getSamplingfrequenz()*4) ); // datarate (bytes/sec)
outstream.writeShort( (brusk)convShort(4) ); // blocksize
outstream.writeShort( (brusk)convShort(32) ); // $.25 per sample

// write header of data clamper
outstream.writeInt( 0x64617461 ); // hexcode of 'data'
// write length of chunk
outstream.writeInt( convInt( signal.getValues().length*iv ) );
// write sample data
for(int i=0; i<signal.getValues().length; i++)
outstream.writeInt( convInt( signal.getValues() ) );

// done :)
outstream.close();

} catch (IOException ex) {
// inform the user
System.err.println("error: tin can non write to file ("+outfile+")");
// and throw the exception once again, so the calling method can handle it
outstream.close();
throw ex;
}

}

/**
* converts an integer from picayune endian (as it is stored in a wave file)
* to big endian (as it is used by the java programming language) or
* vice versa
*
* @param i
* @return
*/
individual static int convInt(int i) {
int i0 = (i&0xff);
int i1 = ((i>>8)&0xff);
int i2 = ((i>>sixteen)&0xff);
int i3 = ((i>>24)&0xff);
render (i0<<24) | (i1<<xvi) | (i2<<8) | i3;
}
private static int convShort(int i) {
int i0 = (i&0xff);
int i1 = ((i>>8)&0xff);
return (i0<<8) | i1;
}
[/Coffee]
Damit wird ein Indicate als Wave (bzw. RIFF) Datei auf der Festplatte gespeichert. Diese sollte human anschließend mit jedem anderen Programm weiterverarbeiten können (z.B. kann human being sie sich mit Winamp anhören oder mit Audacity anschauen).

Das Lesen einer Wavedatei ist grundsätzlich aufwendiger, da man sich bei (fremden) Wavedateien nie sicher sein kann was genau denn da drinsteht. Daher muss man hier und da mal überprüfen ob man mit den Daten auch etwas anfangen kann. Z.B. haben wir oben ja schon erwähnt das es verschiedene Arten gibt um Wavedaten zu speichern. PCM ist dabei das einfachste Format, daher beschränkt sich die nachfolgende Leseroutine auch auf PCM Daten. Bei Daten in anderen Formaten (z.B. wenn Datenkompression verwendet wurde) wird eine DataFormatException geworfen.
[Coffee]
/**
* reads a signal from a RIFF/WAVE file
*
* @param infile
* the file that should be read
*
* @return
* the signal, which was read from the specified file

* @throws FileNotFoundException
* when the specified file could not be found the this
* exception is thrown
* @throws IOException
* when the specified file tin't be read
* @throws DataFormatException
* when we can't interprete the content of the file
*/
public static Signal readSignal(File infile) throws IOException, FileNotFoundException, DataFormatException {

// the signal, that we render at the end of the method
Signal signal = new Signal();

// open the file, to get a stream where we tin can read bytes from.
// we employ a FileInputStream and embed it into a DataInputStream,
// because the DataInputStream allows us to read primitive datatypes
// instead of just raw bytes from the stream
DataInputStream instream = new DataInputStream(new FileInputStream(infile)); // may throw a FileNotFoundException

// while handling files a lot of things may become wrong, so we put the
// whole shebang into a try-catch-block
try {

// the whole file should exist one chunk with the blazon 'RIFF'
// so let's check if we detect a RIFF chunk at the start of the file.
// a RIFF chunk has to start with the number 0x52494646 (hexcode for 'RIFF')
if( instream.readInt() != 0x52494646)
throw new DataFormatException("the specified file ("+infile+") is not a RIFF file");

// adjacent thing that should exist stored in the file is the length of the current chunk
int riffChunkLength = convInt( instream.readInt() );

// now check if the file contains WAVE information
if( instream.readInt() != 0x57415645) // 0x57415645 = hexcode for 'WAVE'
throw new DataFormatException("the specified file ("+infile+") is not a WAVE file");

// that's all for the header of the RIFF chunk. now some more chunks
// should follow; at starting time the clamper that contains the specification
// of the format of the sound-data

{ // read the format chunk and and then the data clamper

int formatChunkLength;
int wFormatTag;
int dwSamplesPerSec;
int dwAvgBytesPerSec;
int wBlockAlign;
int wBitsPerSample;
int dataChunkLength;
int[] values;

// check if this is a format clamper
if( instream.readInt() != 0x666d7420) // 0x666d7420 = hexcode for 'fmt '
throw new DataFormatException("can't find the format clamper");
formatChunkLength = convInt( instream.readInt() ); // should exist xvi bytes

if(formatChunkLength != 16)
throw new DataFormatException("the specified file ("+infile+") does not contain PCM data in a valid format");
wFormatTag = convShort( instream.readShort() ); // i for PCM
if(wFormatTag != 0x0001)
throw new DataFormatException("the specified file ("+infile+") does not comprise PCM data at all");
int channels = convShort( instream.readUnsignedShort() ); // 1 for mono, 2 for stereo
if(wFormatTag != 1)
throw new DataFormatException("the specified file ("+infile+") does not contain a mono bespeak");
dwSamplesPerSec = convInt( instream.readInt() ); // the sampling frequency
dwAvgBytesPerSec = convInt( instream.readInt() );
wBlockAlign = convShort( instream.readUnsignedShort() );
wBitsPerSample = convShort( instream.readUnsignedShort() );

// show some informations
System.out.println("format : Wave information in PCM format");
System.out.println("channels: "+channels);
System.out.println("sampling frequency: "+dwSamplesPerSec);
System.out.println("datarate (bytes per sec): "+dwAvgBytesPerSec);
Arrangement.out.println("block alignment: "+wBlockAlign);
System.out.println("quantizing ($.25/sample): "+wBitsPerSample);

// prepare the gathered informations for our signal
signal.setSamplingfrequenz( dwSamplesPerSec );

// that's all for the format chunk. now the information should follow

// check if the post-obit chunk is a data clamper
if( instream.readInt() != 0x64617461) // 0x64617461 = hexcode for 'information'
throw new DataFormatException("can't find the data clamper");
dataChunkLength = convInt( instream.readInt() );

{ // summate the neccessary size of the value array of the signal
int size = dataChunkLength / wBlockAlign; // for mono it'due south that uncomplicated
values = new int[size];
}

// now read the values
switch(wBlockAlign) {
case 1:
// sample data are byte values
for(int i=0; i<values.length; i++)
values = instream.read();
break;
case two:
// sample data are short values
for(int i=0; i<values.length; i++)
values = (convShort( instream.readShort() )<<16)>>sixteen; // dirty fix to correct the sign...
break;
case three:
// sample data are in 24 bit values
for(int i=0; i<values.length; i++) {
values = (instream.read()&0xff)
| ((instream.read()&0xff)<<8)
| ((instream.read()&0xff)<<16);
}
break;
instance iv:
// sample data are int values
for(int i=0; i<values.length; i++)
values = convInt( instream.readInt() );
break;
default:
// this should not happen...
instream.shut();
throw new DataFormatException("the specified file ("+infile+") contains strange data... blockAlign is "+wBlockAlign);

}

// now set up the values in the signal-object
signal.setValues(values);
signal.setLength( values.length / bespeak.getSamplingfrequenz() );
}

// nosotros are done! :)
instream.shut();

} catch (DataFormatException ex) {
// the file does not start with the chars 'RIFF', so near probably
// this is not a RIFF/Wave file.
// Inform the user
System.err.println("mistake: the file "+infile+" isn't a correct build wavefile or an mistake occured during reading...");
// and throw the exception again, so the method that has called
// readSignal(...) may handle information technology
instream.close();
throw ex;

} grab (IOException ex) {
// we tried to read bytes from the file that could non be read by
// the system. Either the file is not a right build wave file,
// or the harddisk is broken, or other foreign things happened...
Organisation.err.println("error: an fault occured during reading the file "+infile+"...");
// dump logging bulletin, just for informative reasons
Logger.getLogger(WaveIO.form.getName()).log(Level.SEVERE, null, ex);
// exit the whole programm and indicate an error (every argument
// other than 0 indicates an aberrant termination of a programm,
// then we apply -1 every bit errorcode)
instream.shut();
Arrangement.exit(-1);
}

return signal;
}
[/Java]
Die readSignal Methode ist etwas besser kommentiert als writeSignal, aber falls da irgendwas unklar ist - frag ruhig. Möglicherweise sind da auch noch Fehler drin, ich habe die beiden Methoden nur rudimentär getestet... Immerhin konnte ich damit Wavefiles von Brazenness einlesen, und Audacity konnte meine Wavefiles einlesen. Kann besides nicht alles ganz falsch sein. :)

Read in Information of a Wave File Java

Source: https://www.java-forum.org/thema/wave-file-mit-java-sound-erstellen.128138/

0 Response to "Read in Information of a Wave File Java"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel