Bilge Mirac Atici
Bilge Mirac Atici

Reputation: 31

Sine wave alternates distortion in Java

I'm trying to generate sine wave and add it to byte array. I searched and found. However, I always get distorted waveform like an attachment.

Please give me your opinion why it happens. Thanks.

My code is here

private byte[] getData(int freq) {   // taking pitch data
    double pha = Math.PI/2;          // defining phase
    final int LENGTH = 44100 * 10;   // defining length of sine wave, byte array
    final byte[] arr = new byte[LENGTH];
    for(int i = 0; i < arr.length; i++) {
        double angle = (2.0 * Math.PI * i*freq+pha) / (44100); 
        arr[i] = (byte) (Math.cos(angle) *127* 0.3);    // 0.3 is amplitude scale        
    }
    return arr;
}

Distort waveform example pic

enter image description here

Upvotes: 3

Views: 589

Answers (2)

Marko Topolnik
Marko Topolnik

Reputation: 200196

You can use this code to convince yourself that what you generate is appropriate at the level of Java semantics:

public static void main(String[] args) {
  for (byte b : getData(300)) System.out.println(sample(b));
}

static String sample(byte val) {
  final int len = (val-Byte.MIN_VALUE)/2;
  final StringBuilder b = new StringBuilder();
  for (int i = 0; i < len; i++) b.append(i < len-1? ' ' : '#');
  return b.toString();
}

It will print a nice vertical sine. Fix your code by producing unsigned bytes with this method:

static byte[] getData(int freq) {
  double pha = Math.PI/2;
  final int LENGTH = 44100 * 10;
  final byte[] arr = new byte[LENGTH];
  for(int i = 0; i < arr.length; i++) {
    double angle = (2.0 * Math.PI * i*freq+pha) / (44100);
    int unsignedSample = (int) (Math.cos(angle)*Byte.MAX_VALUE*0.3 - Byte.MIN_VALUE);
    arr[i] = (byte) (unsignedSample & 0xFF);
  }
  return arr;
}

If you print this, you'll see the same waveform which you saw in SonicVisualizer, but in that tool it will look the way you intended to.

Upvotes: 0

NPE
NPE

Reputation: 500663

The code looks fine. I suspect it's the visualiser interpreting the two's complement signed values as unsigned (-1 becoming 255, -2 becoming 254 and so on).

I write to a wav file and plot it with SonicVisualiser

According to WAVE PCM soundfile format:

8-bit samples are stored as unsigned bytes, ranging from 0 to 255. 16-bit samples are stored as 2's-complement signed integers, ranging from -32768 to 32767.

It looks like you either need to shift your sine wave up by 128 (so that it fits fully within the 0-255 range), or move to using 16-bit samples.

Upvotes: 5

Related Questions