2009-04-11 10 views
5

Ich bekomme eine leichte Verzerrung (klingt wie Summen) im Hintergrund, wenn ich den folgenden Code ausführen. Wegen seiner subtilen Natur glaubt es, dass beim Byte-Casting eine Art Aliasing stattfindet.Rauschen im Hintergrund beim Generieren von Sinuswelle in Java

Audioformat = PCM_SIGNED 44100,0 Hz, 16-Bit, Stereo, 4 Bytes/Rahmen, big-endian

Hinweis: Code annimmt (bis jetzt), dass die Daten im Big-Endian ist.

public static void playFreq(AudioFormat audioFormat, double frequency, SourceDataLine sourceDataLine) 
{ 
    System.out.println(audioFormat); 
    double sampleRate = audioFormat.getSampleRate(); 
    int sampleSizeInBytes = audioFormat.getSampleSizeInBits()/8; 
    int channels = audioFormat.getChannels(); 

    byte audioBuffer[] = new byte[(int)Math.pow(2.0, 19.0) * channels * sampleSizeInBytes]; 

    for (int i = 0; i < audioBuffer.length; i+=sampleSizeInBytes*channels) 
    { 
     int wave = (int) (127.0 * Math.sin(2.0 * Math.PI * frequency * i/(sampleRate * sampleSizeInBytes * channels)) ); 

     //wave = (wave > 0 ? 127 : -127); 

     if (channels == 1) 
     { 
      if (sampleSizeInBytes == 1) 
      { 
       audioBuffer[i] = (byte) (wave); 
      } 

      else if (sampleSizeInBytes == 2) 
      { 
       audioBuffer[i] = (byte) (wave); 
       audioBuffer[i+1] = (byte)(wave >>> 8); 
      } 
     } 

     else if (channels == 2) 
     { 
      if (sampleSizeInBytes == 1) 
      { 
       audioBuffer[i] = (byte) (wave); 
       audioBuffer[i+1] = (byte) (wave); 
      } 

      else if (sampleSizeInBytes == 2) 
      { 
       audioBuffer[i] = (byte) (wave); 
       audioBuffer[i+1] = (byte)(wave >>> 8); 

       audioBuffer[i+2] = (byte) (wave); 
       audioBuffer[i+3] = (byte)(wave >>> 8); 
      } 
     } 
    } 

    sourceDataLine.write(audioBuffer, 0, audioBuffer.length); 
} 

Antwort

7

Ihre Kommentare sagen, dass der Code Big-Endian annimmt.

Technisch sind Sie eigentlich in Little-Endian ausgeben, aber es ist nicht, weil durch eine glückliche Marotte zu Materie scheint Ihr höherwertiges Byte ist immer 0.

EDIT: die weiter zu erklären - wenn Ihr Wert ist bei seinem Maximalwert von 127, Sie sollten schreiben (0x00, 0x7f), aber die tatsächliche Ausgabe von Ihrem Code ist (0x7f, 0x00) was 32512 ist. Dies geschieht in der Nähe der richtigen 16 Bit Maximalwert von 32767, aber mit den unteren 8 Bits alle Null. Es wäre besser, immer 32767 als Maximalwert zu verwenden und dann die unteren 8 Bits zu verwerfen, falls erforderlich.

Dies bedeutet, dass obwohl Sie 16-Bit-Daten ausgeben, die effektive Auflösung nur 8 Bit beträgt. Dies scheint für die mangelnde Klangqualität verantwortlich zu sein.

Ich habe eine Version Ihres Codes gemacht, die nur die Rohdaten in eine Datei ablegt, und nichts anderes falsch sehen kann, wenn sich das Bit verschiebt. Es gibt keine unerwarteten Änderungen des Vorzeichens oder der fehlenden Bits, aber es gibt ein Summen, das mit der 8-Bit-Sample-Qualität konsistent ist.

Auch für das, was es wert ist Ihre Mathe leichter sein wird, wenn Sie die Wellengleichung zu berechnen, basierend auf Probe zählt, und dann über Byteversätze separat Sorge:

int samples = 2 << 19; 
byte audioBuffer[] = new byte[samples * channels * sampleSizeInBytes]; 

for (int i = 0, j = 0; i < samples; ++i) 
{ 
    int wave = (int)(32767.0 * Math.sin(2.0 * Math.PI * frequency * i/sampleRate)); 
    byte msb = (byte)(wave >>> 8); 
    byte lsb = (byte) wave; 

    for (int c = 0; c < channels; ++c) { 
     audioBuffer[j++] = msb; 
     if (sampleSizeInBytes > 1) { 
      audioBuffer[j++] = lsb; 
     } 
    } 
} 
+0

Ah! Sehen Sie den Fehler jetzt, nachdem Sie den Amplitudenteil behoben haben, wurde der Fehler sehr offensichtlich, aber Ihr Code ist auch effizienter. Vielen Dank! – yxk

+0

Gut gemacht Analyse! –

2

Ich nehme an, dass Sie diesen Code wiederholt aufrufen, um einen langen Ton zu spielen.

Gibt es eine Chance, dass die Welle, die Sie generieren, keine vollständige Periode vor dem Schreiben erreicht?

Wenn die Welle "abgeschnitten" wird, bevor sie eine volle Periode abschließt und dann die nächste Welle in den Ausgang geschrieben wird, werden Sie bestimmt etwas Merkwürdiges hören und ich nehme an, dass dies das Brummen verursacht.

Zum Beispiel:

 /-------\    /-------\    /-------\ 
    -----/   \  -----/   \  -----/   \ 
        \      \      \ 
        \-----     \-----     \----- 

Beachten Sie die Trennung zwischen Teilen dieser Welle. Das könnte das Summen verursachen.

+0

Ja, es ist nicht das Problem ist, weil der Puffer groß genug für den Sound durch viele Zyklen. Es berücksichtigt das Klicken, aber nicht das konstante Brummen. – yxk

+0

Dies ist nicht die richtige Antwort - der Code erzeugt deutlich viele Samples und es gibt keine Trunkierung am Ende jedes Zyklus – Alnitak

+0

und ich habe den Code getestet - es ist ein unbeabsichtigter 8-Bit Quantisierungsfehler, der das Summen verursacht. – Alnitak