WAVE-file mit Java Sound erstellen

Hallo Leute,

wir haben in der Schule derzeit ein Projekt am laufen, wo wir ein generiertes Signal (Sinus, Rechteck oder Sägezahn; Amplitude, Frequenz, Länge einstellbar) in ein WAVE file abspeichern und wiedergeben sollen. Weiter sollen wir später ein WAVE file einlesen und graphisch darstellen können, das ist jedoch nicht primär wichtig, sollte es aber eng mit dem vorherigen zusammenhängen, freue ich mich auch hier über tipps ;)

Das Problem dabei ist, dass wir keinerlei theoretischen, geschweige denn praktischen, Input zu diesem Thema (Aufbau WAVE file, Java Sound, etc.) bekommen haben.
Ich habe bereits versucht, mich in die Thematik einzulesen, da dieses Thema jedoch sehr breitgefächert ist, habe ich bis jetzt, trotz langer Recherche, praktisch noch keine sinnvollen Tutorials (welche genau diesen Teilbereich behandeln) o.ä. finden können, darum bitte ich nun hier um Hilfe.

Das Einstellen und Auslesen der Parameter via GUI funktioniert bereits problemlos, in meiner model-Klasse "Signal" wird das zu erstellende Signal wie folgt abgebildet:

Java:
public class Signal 
{
	private int fs;  //Samplingfrequenz
	private int length;  //Laenge des Signals (in sek)
	private int[] values;  //Die Werte des Signals
	
//folgen noch Getter/Setter und Constructor
}

Wie schaffe ich es nun, mit dieser Information ein WAVE file zu erstellen?

Danke im Vorraus für die Hilfe, LG
 
D

Dow Jones

Gast
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 Resource Interchange File Format und ist eine uralte Erfindung. Eigentlich sind RIFFfiles nur Container für irgendwelche Daten. Zum Beispiel für Wave Daten. Warum man die Daten überhaupt in 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 chunk-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 die Wavedaten enthält) sähe als HTML-Dokument wohl so aus:
Code:
<RIFF chunksize="1234">
	<RiffType>WAVE</RiffType>
	<Format chunksize="16">
		<dataformat>PCM</dataformat>
		<channels>2</channels>
		<samplesPerSecond>44100</samplesPerSecond>
		<datarate>192kb/s</datarate>
		<frameSize>4</frameSize>
		<bitsPerSample>16</bitsPerSample>
	</Format>
	<Data chunksize="5678">
		<sampleData>0</sampleData>
		<sampleData>2</sampleData>
		<sampleData>3</sampleData>
		<sampleData>2</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ß man weiss welche Daten sich in welche Reihenfolge darin befinden. Grundsätzlich gilt: Man liest nur die Chunks aus, für die man sich interessiert. Alle uninteressanten (oder unbekannten) chunks ignoriert man. Dadurch werden chunk-basierte Formate sehr flexibel und lassen sich jederzeit erweitern, ohne die Kompatibilität zu älteren Programmen zu zerstören.
(Anmerkung: die Ende-Tags, die 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 die 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 signal into a wavefile
*
* @param signal
* the signal that should be 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 not 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 us 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 chunk
outstream.writeInt( convInt( 0x24 + signal.getValues().length * 4 ) );
// write type of data
outstream.writeInt( 0x57415645 ); // hexcode of 'WAVE'

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

// write header of data chunk
outstream.writeInt( 0x64617461 ); // hexcode of 'data'
// write length of chunk
outstream.writeInt( convInt( signal.getValues().length*4 ) );
// 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: can not write to file ("+outfile+")");
// and throw the exception again, so the calling method can handle it
outstream.close();
throw ex;
}

}


/**
* converts an integer from little 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
*/
private static int convInt(int i) {
int i0 = (i&0xff);
int i1 = ((i>>8)&0xff);
int i2 = ((i>>16)&0xff);
int i3 = ((i>>24)&0xff);
return (i0<<24) | (i1<<16) | (i2<<8) | i3;
}
private static int convShort(int i) {
int i0 = (i&0xff);
int i1 = ((i>>8)&0xff);
return (i0<<8) | i1;
}
[/Java]
Damit wird ein Signal als Wave (bzw. RIFF) Datei auf der Festplatte gespeichert. Diese sollte man anschließend mit jedem anderen Programm weiterverarbeiten können (z.B. kann man 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.
[Java]
/**
* 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 can'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 return at the end of the method
Signal signal = new Signal();

// open the file, to get a stream where we can read bytes from.
// we use 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 go wrong, so we put the
// whole shebang into a try-catch-block
try {

// the whole file should be one chunk with the type 'RIFF'
// so let's check if we find a RIFF chunk at the beginning 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");

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

// now check if the file contains WAVE data
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 first the chunk that contains the specification
// of the format of the sound-data

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

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

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

if(formatChunkLength != 16)
throw new DataFormatException("the specified file ("+infile+") does not contain PCM data in a valid format");
wFormatTag = convShort( instream.readShort() ); // 1 for PCM
if(wFormatTag != 0x0001)
throw new DataFormatException("the specified file ("+infile+") does not contain 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 signal");
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 data in PCM format");
System.out.println("channels: "+channels);
System.out.println("sampling frequency: "+dwSamplesPerSec);
System.out.println("datarate (bytes per sec): "+dwAvgBytesPerSec);
System.out.println("block alignment: "+wBlockAlign);
System.out.println("quantizing (bits/sample): "+wBitsPerSample);

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

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

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

{ // calculate the neccessary size of the value array of the signal
int size = dataChunkLength / wBlockAlign; // for mono it's that simple
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 2:
// sample data are short values
for(int i=0; i<values.length; i++)
values = (convShort( instream.readShort() )<<16)>>16; // dirty fix to correct the sign...
break;
case 3:
// 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;
case 4:
// sample data are int values
for(int i=0; i<values.length; i++)
values = convInt( instream.readInt() );
break;
default:
// this should not happen...
instream.close();
throw new DataFormatException("the specified file ("+infile+") contains strange data... blockAlign is "+wBlockAlign);

}

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

// we are done! :)
instream.close();


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

} catch (IOException ex) {
// we tried to read bytes from the file that could not be read by
// the system. Either the file is not a correct build wave file,
// or the harddisk is broken, or other strange things happened...
System.err.println("error: an error occured during reading the file "+infile+"...");
// dump logging message, just for informative reasons
Logger.getLogger(WaveIO.class.getName()).log(Level.SEVERE, null, ex);
// exit the whole programm and indicate an error (every argument
// other than 0 indicates an abnormal termination of a programm,
// so we use -1 as errorcode)
instream.close();
System.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 Audacity einlesen, und Audacity konnte meine Wavefiles einlesen. Kann also nicht alles ganz falsch sein. :)
 
Vielen vielen Dank, so eine ausführliche Antwort mit Beispielen hatte ich mir zwar vielleicht erhofft, aber sicher nicht damit gerechnet ;)

Habe die beiden Beispiele die du mir gepostet hast adaptiert und implemetiert, es funktioniert perfekt, danke vielmals !! :)

Jetzt werde ich mich noch um die Wiedergabe kümmern, dann is das ganze schon fast geschafft ...

LG, DANKE!!
 
D

Dow Jones

Gast
Schön zu hören, das es dir weitergeholfen hat. :)
Zwei Dinge sollten aber besser noch erwähnt werden, bevor "unerklärliche Fehler" auftauchen...

1) Auch wenn das Dateiformat genormt ist heisst das natürlich nicht das sich auch jede Software der Welt daran hält. Beim Lesen der Wavefiles anderer Leute sollte man sich besser nicht darauf verlassen dass dort nur jeweils ein Format- und ein Daten-Chunk drinsteht. Möglicherweise findet man auch noch Chunks mit Kommentartexten, Playlisten oder was auch immer (siehe z.B. hier). Nicht einmal die richtige Reihenfolge (erst Format-, dann Daten-Chunk) ist immer gegeben.

2) Worüber ich am Anfang gestolpert bin: Da die Sampledaten eures Signals Integerwerte sind reicht deren Spannweite von -2.147.483.648 bis +2.147.483.647. Um beim Abspielen des Waves auch etwas hören zu können sollten die Werte am besten diese gesamte Spannweite ausnutzen.
Ich hatte zum Testen anfangs nur 16Bit Werte verwendet und da war natürlich nichts zu hören... :oops:
 
Hab jetzt doch ein Problem, dadurch dass ich erst jetzt damit begonnen habe, mir die wavefiles auch anzuhören (war zufrieden damit, dass ich sie fehlerlos erstellen kann und sie als wavefiles erkannt werden und abgespielt werden), habe ich realisiert, dass diese zwar problemlos erkannt und gelesen werden, jedoch kein sound ausgegeben wird ... woran könnte das denn liegen??

implemtiert habe ich das ganze wie folgt, seht ihr das problem?:


Es handelt sich hierbei um eine if-Anweisung in der actionPerformed Methode meiner GUI-Klasse:

Java:
if (event.equals("Signal als WAVE-file abspeichern"))
		{
			//create new signal with current settings
			sig = new Siggen(41000, (Integer)cStime.getValue(), (Integer)cSamplitudeSlider.getValue(), (Integer)cSfrequencySlider.getValue(), (String)cStype.getSelectedItem());  //hier wird ein neues signal auf basis der aktuell ausgewählten werte erzeugt
			saveWave = new SaveWave();  //deine implemtierung für das Schreiben eines WAVEfiles
			
			//fileChooser to save to file
			if (fc == null)
			{
			fc = new JFileChooser();
			}
			JFileChooser fc = new JFileChooser();
	        fc.setDialogTitle("Speicherort auswählen");
	        fc.setFileFilter(new FileFilter() {
	                public boolean accept(File f) {
	                    return f.getName().toLowerCase().endsWith(".wav") || f.isDirectory();
	                }
	                public String getDescription() {
	                    return "WAVE-files (*.wav)";
	                }
	            });
			//save dialog
			int returnVal = fc.showSaveDialog(StartMenue.this);
            if (returnVal == JFileChooser.APPROVE_OPTION) 
            {
                fileToSave = fc.getSelectedFile();
            }
            else
            {
            	System.out.println("Couldn't save to file.");
            }
			
            //write in file
			try 
			{
				saveWave.writeSignal(sig.getSig()/*signal to write*/, fileToSave);
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//reset file of fileChooser for next operation
			fc.setSelectedFile(null);
			
		}

danke im vorraus, lg, wenn etwas unklar ist oder noch gebraucht wird, bitte melden !! :)
 
D

Dow Jones

Gast
Verstehe ich das richtig, das die mit eurem Programm geschriebenen Wavefiles auch von anderen Programmen korrekt als Wavedateien gelesen werden können, das diese beim Abspielen dann aber nichts (hörbares) ausgeben?

Dafür würden mir auf Anhieb zwei mögliche Erklärungen einfallen:

1) Die Frequenz eures Signals ist zu niedrig/hoch, als das man es hören könnte. Versuch doch testweise mal eine Sinus/Rechteck/wasauchimmer Schwingung von 1000 Herz, bei einer Samplefrequenz von 8000 Herz.

2) Ihr nutzt die vorgegebene Spannweite der Integerwerte nicht aus. Dann kommt soetwas dabei heraus:
waveLUKBC.png

Beim oberen Wavefile standen im Values-Array Werte zwischen -30.000 und +30.000. Bezogen auf den Wertebereich von Int ist das freilich gar nichts, daher wird dieses Wavefile dermassen leise abgespielt das man nix hört. Beim unteren Signal lagen die Werte zwischen -2.000.000.000 und +2.000.000.000, und da war's kein Problem sich das anzuhören.

Ansonsten lade doch mal eines von euren Wavefiles hoch, dann schaue ich mir das genauer an.


PS: Im oben geposteten Schnipsel zum Lesen von Wavefiles ist mir noch ein Fehler aufgefallen: In der If-Abfrage in Zeile 72 muss es natürlich "channels" heissen, nicht "wFormat".
 
Verstehe ich das richtig, das die mit eurem Programm geschriebenen Wavefiles auch von anderen Programmen korrekt als Wavedateien gelesen werden können, das diese beim Abspielen dann aber nichts (hörbares) ausgeben?

genauso ist es leider

Dafür würden mir auf Anhieb zwei mögliche Erklärungen einfallen:

1) Die Frequenz eures Signals ist zu niedrig/hoch, als das man es hören könnte. Versuch doch testweise mal eine Sinus/Rechteck/wasauchimmer Schwingung von 1000 Herz, bei einer Samplefrequenz von 8000 Herz.

2) Ihr nutzt die vorgegebene Spannweite der Integerwerte nicht aus. Dann kommt soetwas dabei heraus:
Beim oberen Wavefile standen im Values-Array Werte zwischen -30.000 und +30.000. Bezogen auf den Wertebereich von Int ist das freilich gar nichts, daher wird dieses Wavefile dermassen leise abgespielt das man nix hört. Beim unteren Signal lagen die Werte zwischen -2.000.000.000 und +2.000.000.000, und da war's kein Problem sich das anzuhören.

Ansonsten lade doch mal eines von euren Wavefiles hoch, dann schaue ich mir das genauer an.

ich hätte die werte des signals nun einmal mit jeweils einem faktor von 500000 "verstärkt", getan hat sich leider noch nicht sehr viel ... ich hänge mal eine von mir generierte .wav datei an, wenn du mir (erneut) weiterhelfen könntest, würde mich das sehr freuen, danke !!

PS: Es wird vermutlich etwas mit 2. zu tun haben, mein generiertes file hat eine schwingung von 1700 herz und dauert 10sec, da dürfte das problem nicht liegen ...

LG
 
D

Dow Jones

Gast
Zuerst die gute Nachricht: And und für sich funktioniert das ganze prima. :)

Jetzt die weniger gute Nachricht: Du machst einen winzig kleinen Denkfehler. Und ja, es hat mit dem zweiten angesprochenen Punkt zu tun. Ich weiss jetzt nicht ob du dich mit der Skalierung deiner Werte oder mit der Darstellung negativer Zahlen vertan hast, daher versuche ich hier mal über beides zu referieren. Wichtig ist: Der Wertebereich von Integer in Java (und auch in Wavefiles) ist nicht 0 bis 2^32 sondern -2^31 bis +2^31. Und diesen Wertebereich musst du auch voll ausschöpfen um die maximale Lautstärke (= maximale Bewegung des Lautsprechermemberans) zu bekommen.


In diesem Hexdump ist der Datenbereich deines Wavefiles hervorgehoben:

testhexQY3Q5.png


So wie es aussieht kommen nur wenige verschiedene Werte vor (0x00000000, 0x0007a120, 0x000f4240, 0xfff85ee0 und 0xfff0bdc0). Man kann sich das nun so vorstellen, das diese Werte die momentane Amplitude des Signals - und somit den Ausschlag des Lautsprechermembrans - angeben. Das Membran kann maximal um 2.147.483.648 (= 2^31) Einheiten nach vorne und genausoviele Einheiten nach hinten ausschlagen. Wenn man das mal umrechnet kommt man zu folgenden Ergebnissen:

Wert 0 -> kein Ausschlag, Lautsprechermembran in Ruhe (Mittelstellung).
Wert 0x0007a120 -> Ausschlag = 500.000/2.147.483.648 = 0.000232831 ~> 0.02% vom möglichen Maximalausschlag
Wert 0x000f4240 -> Ausschlag = 1.000.000/2.147.483.648 = 0.000465661 ~> 0.05% vom möglichen Maximalausschlag

Mit anderen Worten: diese drei Werte bewirken nahezu überhaupt keine Membranbewegung. Man darf sie bezüglich der Hörbarkeit getrost alle als 0 betrachten.
Die beiden restlichen Werte sollten dagegen für den maximalen Membranausschlag sorgen. Tatsächlich tun sie das aber nicht. Und hier liegt dein oben angesprochenr Denkfehler: Auch wenn die Werte groß aussehen - sie sind es nicht. Das liegt daran wie Java Zahlen abspeichert (Stichwort Zweierkomplement). Das höchstwertige Bit wird als Vorzeichen interpretiert und die restlichen Bits zählen gewissermaßen aufwärts gegen null. Die betragsmäßig größte negative Zahl ist demnach 0x80000000, während 0xfff85ee0 zwar auch negativ, aber betragsmäßig klein ist.
Java:
        int a = 0xfff0bdc0;
        int b = 0x80000000;
        System.out.println("a = "+a);
        System.out.println("b = "+b);
liefert
Code:
a = -1000000
b = -2147483648


Wenn wir unsere oben begonnene Umrechnung fortsetzen erhalten wir diese Ergebnisse:
Wert 0xFFF85EE0 -> Ausschlag = -500.000/2.147.483.648 = -0.000232831 ~> 0.02% vom möglichen Maximalausschlag (in negativer Richtung)
Wert 0xfff0bdc0 -> Ausschlag = -1.000.000/2.147.483.648 = -0.000465661 ~> 0.05% vom möglichen Maximalausschlag (in negativer Richtung)
Tja, ein Lautsprecherausschlag im Bereich von -0.05% bis +0.05%, da muss man schon seeeehr genau hinhören... Oder einen wahnsinns Verstärker haben. :joke:


Unter Audacity schaut euer Wavefile so aus:

testwav9W39V.png


Man kann die +- 0.05% zumindest erahnen.
Wenn man nun auf "normalisieren" klickt dann werden die Werte auf den (beinahe) gesamten Wertebereich gestreckt, und dann schaut's so aus:

testwav2CMQY9.png


Und so sollte es vermutlich auch aussehen. Jedenfalls kann man sich das nun auch anhören ohne den Verstärker hochzukurbeln. :)


Also, die Quintessenz soll sein: Die Werte in eurem Datenarray müssen sich irgendwo zwischen -2.147.483.648 (maximaler Membranausschlag in negativer Richtung) und +2.147.483.648 (maximaler Membranausschlag in positiver Richtung) bewegen. Für maximale Lautstärke sollte man diesen Wertebreich auch vollständig ausnutzen. Bei kleineren Werten (z.B. -1.000.000 bis +1.000.000) bewegt sich das Membran kaum, weswegen man das Signal nicht hört. Es wird zwar korrekt abgespeichert, aber es ist halt unhörbar leise.

Schnelle Abhilfe: "Verstärkt" euer Signal nicht mit dem Faktor 500.000 sondern mit dem Faktor 1.000.000.000.
Schöne Abhilfe: Möglicherweise sind Integerwerte ja eher unpassend für eure Signale. In Wavefiles kann man die Daten auch als Words/Bytes/Floats etc ablegen, man müsste das halt nur im Format-Chunk entsprechend ändern. Wäre vielleicht eine Alternative, wenn ihr den vollen Wertebereich von Integer sowieso nicht benötigt. Und die Wavefiles verbrauchen dann weniger Platz auf der Platte. :)
 

PC94

Neues Mitglied
ist die Signal Klasse, die du verwendest, eine selbst programmierte oder eine fertige Bibliothek?
ich wollte nämlich auch ein Programm schreiben, welches mit Wave-Dateien arbeitet und wollte deine 2 Programme als Tutorials verwenden, aber bei mir findet es die Signal-Klasse nicht bzw. gibt es zwar eine von sun, aber die kennt die von dir verwendeten Methoden nicht
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
D Wave Datei mehrmals abspielen Java Basics - Anfänger-Themen 2
Timo12345 JNLP File mit Java öffnen Java Basics - Anfänger-Themen 2
I Bild richtig speichern / Hochkant im File Explorer, nach Upload vertikal Java Basics - Anfänger-Themen 9
R File.pathSeparator und File.separator Java Basics - Anfänger-Themen 46
KeinJavaFreak Erste Schritte Java "Executable Jar File" nicht vorhanden Java Basics - Anfänger-Themen 1
izoards Prunsrv - Windows Service - .bat file starten Java Basics - Anfänger-Themen 84
I File Uploader... Statusanzeige, Berechnung des Status etc. Java Basics - Anfänger-Themen 0
I H2 Datenbank starten / Daten in File speichern Java Basics - Anfänger-Themen 25
G jar file lässt sich nicht öffnen Java Basics - Anfänger-Themen 1
N Textdatei aus Resourcen-Ordner eines Projekts/ jar-file lesen Java Basics - Anfänger-Themen 4
J File length als Prüfwert für Download Java Basics - Anfänger-Themen 5
berserkerdq2 Überprüfen ob eine Schreibberechtigung auf ein file exisitert bzw. ob man dieses file löschen kann, wie? Java Basics - Anfänger-Themen 9
berserkerdq2 IOstreams, was unterscheidet file von z. B. BufferedWriter? Java Basics - Anfänger-Themen 11
J Datentypen String in File konvertieren funktioniert nicht Java Basics - Anfänger-Themen 4
A java jar-File Java Basics - Anfänger-Themen 1
E Executable jar file fehler Java Basics - Anfänger-Themen 9
I Upload File zu einem Webservice Java Basics - Anfänger-Themen 17
I ZIP File erstellen Java Basics - Anfänger-Themen 10
LeoDerKek Textdatei in JAR-File Java Basics - Anfänger-Themen 4
Tom/S File Java Basics - Anfänger-Themen 10
AleXusher Mehrdimensionales Array aus txt.file auslesen Java Basics - Anfänger-Themen 4
A Checkstyle - File contains tab characters Java Basics - Anfänger-Themen 2
H Java file nicht gefunden Java Basics - Anfänger-Themen 5
C File speichern Java Basics - Anfänger-Themen 5
C Runnable Jar-File erzeugen Java Basics - Anfänger-Themen 14
C Pfad zu Properties-File bei ResourceBundle Java Basics - Anfänger-Themen 7
F GSON file mit einer List erstellen Java Basics - Anfänger-Themen 2
rafi072001 Lesen aus einem Excel File Java Basics - Anfänger-Themen 10
C Bild in executable JAR File mitgeben Java Basics - Anfänger-Themen 5
K File (png) in Image laden Java Basics - Anfänger-Themen 3
B Inputstream in file schreiben? Java Basics - Anfänger-Themen 23
S Input/Output Reader/Writer finden file nicht Java Basics - Anfänger-Themen 3
T Buffered Stream leert Txt File Java Basics - Anfänger-Themen 3
A 2d Arrays aus txt.file einlesen Java Basics - Anfänger-Themen 16
Dimax In Java File (nicht in Java Projekt) mysql Driver importieren Java Basics - Anfänger-Themen 3
K File wird in der .Jar nicht gefunden Java Basics - Anfänger-Themen 3
E TXT FILE EINLESEN Java Basics - Anfänger-Themen 4
Trèfle EXCEL-File Laufwerkunabhängig einbinden. Java Basics - Anfänger-Themen 1
B HTML File einlesen inkl. Bilder? Java Basics - Anfänger-Themen 2
B Hilfe bei InputStream To File Java Basics - Anfänger-Themen 22
topi relativer Pfad in einem Runnable JAR file Java Basics - Anfänger-Themen 12
A jar File kleiner bekommen Java Basics - Anfänger-Themen 3
B File öffnen in src/main/webapp Java Basics - Anfänger-Themen 4
K File-Name Vergleich Java Basics - Anfänger-Themen 2
krgewb Runnable JAR File Export Java Basics - Anfänger-Themen 11
S Maxium aus einer File finden Java Basics - Anfänger-Themen 12
dapzoo Class File Version zu niedrig? Ausführen über Eingabeaufforderung nicht möglich Java Basics - Anfänger-Themen 14
D Input/Output File exists canRead canWrite Java Basics - Anfänger-Themen 11
B cal4j - Error at line 1:Unexpected end of file Java Basics - Anfänger-Themen 0
P jar file lässt sich nicht ausführen Java Basics - Anfänger-Themen 4
E Best Practice Jar-file mit zwei Klassen und externer Bibliothek über Konsole erzeugen Java Basics - Anfänger-Themen 13
M Erste Schritte CSV-File einlesen und Daten verarbeiten Java Basics - Anfänger-Themen 5
F File von Windowsfreigabe laden Java Basics - Anfänger-Themen 1
S java.nio.file.FileSystemException bei Dateizugriff vermeiden Java Basics - Anfänger-Themen 7
Vince42 NIO File Tree in XML umwandeln Java Basics - Anfänger-Themen 10
J Zweidimensionales Array in CSV File exportieren Java Basics - Anfänger-Themen 3
E War-File Problem mit Eclipse Java Basics - Anfänger-Themen 3
B Excel File einlesen und Überschrift prüfen Java Basics - Anfänger-Themen 8
M Email versenden Outlook, attached File, ohne Anmeldung Java Basics - Anfänger-Themen 4
M Input/Output Word File Kopieren Java Basics - Anfänger-Themen 12
K Runable Jar File erstellen Java Basics - Anfänger-Themen 17
T Java Executable jar file funktioniert nicht Java Basics - Anfänger-Themen 4
sourcecorn Werte aus einem File lesen Java Basics - Anfänger-Themen 6
Kopak'rraf Korruptes zip File. Java Basics - Anfänger-Themen 0
T File für Einstellungen wird nicht geladen Java Basics - Anfänger-Themen 1
S Sounddatei in Jar File integrieren Java Basics - Anfänger-Themen 2
S Index File bauen Java Basics - Anfänger-Themen 5
T Anzeige, wie lange es noch dauert bis ein File gesendet ist. Java Basics - Anfänger-Themen 2
T filereader , file aus programm einlesen Java Basics - Anfänger-Themen 12
Ghostman1711 Itext PDF print File gelockt by Java Java Basics - Anfänger-Themen 15
N Email mit Anhang - File not Found Java Basics - Anfänger-Themen 1
K Interface Kein Bild im .jar-File Java Basics - Anfänger-Themen 15
S Bild in Jar-File mit ImageIO Java Basics - Anfänger-Themen 50
Syncopated Pandemonium Compiler-Fehler The constructor MP3File(File) refers to the missing type NoMPEGFramesException Java Basics - Anfänger-Themen 7
S File mit canRead() testen Java Basics - Anfänger-Themen 4
A file.delete funktioniert nicht Java Basics - Anfänger-Themen 15
K Classpath Lesen von Property-File Java Basics - Anfänger-Themen 1
S jar file Java Basics - Anfänger-Themen 19
B Fragen zu ZIP-File Java Basics - Anfänger-Themen 9
B Dateityp von File bekommen Java Basics - Anfänger-Themen 2
I Automatisch Verzeichnis erstellen bei File erstellen Java Basics - Anfänger-Themen 5
E Große Datenmengen effizient in CSV File speichern Java Basics - Anfänger-Themen 4
I String zu File umwandeln Java Basics - Anfänger-Themen 2
N File virtuell ByteStream Java Basics - Anfänger-Themen 11
X Übergabeparameter Konstruktor (File) Java Basics - Anfänger-Themen 13
Anfänger2011 file.isDirectory() liefert falschen Wert!? Java Basics - Anfänger-Themen 1
ms_cikar Jar file mit resource erzuegen Java Basics - Anfänger-Themen 28
E lesen csv file column by column Java Basics - Anfänger-Themen 10
U Anhand von Ant ein War-File erstellen Java Basics - Anfänger-Themen 0
B Quellcode einelsen "line by line" (und abspeichern in file (txt) Java Basics - Anfänger-Themen 7
O Zip-File Inhalt einlesen Java Basics - Anfänger-Themen 3
V Erste Schritte Dateinamen aus einer FIle[] in eine List Java Basics - Anfänger-Themen 11
P Jar File Java Basics - Anfänger-Themen 3
B Probleme bei "Daten in CSV File schreiben". Java Basics - Anfänger-Themen 9
S CSV File - "Vergleichen und Sortieren" Java Basics - Anfänger-Themen 3
T Wieso kann ich das jar file nicht starten? Java Basics - Anfänger-Themen 5
S Resourcen-Dateien im Jar-File verfügbar machen (Intellij 14) Java Basics - Anfänger-Themen 14
A Wieso übergibt der nicht die bearbeitete txt file Java Basics - Anfänger-Themen 8
Ruvok Executable Jar File startet nicht Java Basics - Anfänger-Themen 3
R Ini File erstellen Java Basics - Anfänger-Themen 1

Ähnliche Java Themen

Neue Themen


Oben