# WAVE-file mit Java Sound erstellen



## Javaspast 3000 (2. Dez 2011)

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:


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


----------



## Ralph-Uwe (2. Dez 2011)

Hallo,

hier findes Du den Aufbau einer Wave-Datei.

Gruß


----------



## Dow Jones (2. Dez 2011)

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:

```
<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. _


----------



## Javaspast 3000 (4. Dez 2011)

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


----------



## Dow Jones (4. Dez 2011)

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


----------



## Javaspast 3000 (7. Dez 2011)

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:


```
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 !!


----------



## Dow Jones (7. Dez 2011)

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: 





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".


----------



## Javaspast 3000 (14. Dez 2011)

Dow Jones hat gesagt.:


> 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



Dow Jones hat gesagt.:


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



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


----------



## Dow Jones (14. Dez 2011)

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:






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. 

```
int a = 0xfff0bdc0;
        int b = 0x80000000;
        System.out.println("a = "+a);
        System.out.println("b = "+b);
```
liefert

```
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:






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:






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 (4. Okt 2013)

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


----------

