2016-06-24 2 views
0

リアルタイムで一連の同時トーンを生成しようとしています。しかし、プログラムが生成するすべてのサウンドは、「あいまい」、または「静的」、またはバックグラウンドで「鳴らす」のようなサウンドさえあります。これは特に低音で顕著です。 は、ここでは、コードです:私はこのプログラムからの音が出力されるの記録を試みたSouceDataLineで再生された生成されたサウンドがぼやけています

static final long bufferLength = 44100; 
static final AudioFormat af = new AudioFormat(bufferLength, 8, 1, true, false); 
static boolean go = true; //to be changed somewhere else 

static void startSound(double[] hertz) { 
    if (hertz.length == 0) {return;} 
    try { 
     SourceDataLine sdl = AudioSystem.getSourceDataLine(af); 
     sdl.open(); 
     sdl.start(); 
     int i = 0; 
     //iterate as long as the sound must play 
     do { 
      //create a new buffer 
      double[] buf = new double[128]; //arbitrary number 
      final int startI = i; 
      //iterate through each of the tones 
      for (int k = 0; k < hertz.length; k++) { 
       i = startI; 
       //iterate through each of the samples for this buffer 
       for (int j = 0; j < buf.length; j++) { 
        double x = (double)i/bufferLength*hertz[k]*2*Math.PI; 
        double wave1 = Math.sin(x); 
        //decrease volume with increasing pitch 
        double volume = Math.min(Math.max(300 - hertz[k], 50d), 126d); 
        buf[j] += wave1*volume; 
        i++; 
        if (i == 9999999) { //prevent i from getting too big 
         i = 0; 
        } 
       } 
      } 

      final byte[] finalBuffer = new byte[buf.length]; 
      //copy the double buffer into byte buffer 
      for (int j = 0; j < buf.length; j++) { 
       //divide by hertz.length to prevent simultaneous sounds 
       // from becoming too loud 
       finalBuffer[j] = (byte)(buf[j]/hertz.length); 
      } 

      //play the sound 
      sdl.write(finalBuffer, 0, finalBuffer.length); 
     } while (go); 
     sdl.flush(); 
     sdl.stop(); 
    } catch (LineUnavailableException e) { 
     e.printStackTrace(); 
    } 
} 

//play some deep example tones 
startSound(new double[]{65.4064, 58.2705, 48.9995}); 

、波が少しギザギザのように見えるん。しかし、生成された波をプログラムから直接プリントアウトすると、完全に滑らかに見えます。私が生成する音は、スピーカーから出てくる音とマッチしないようです。誰かが私が間違っていることをキャッチすることはできますか

+0

私はコードを実行していませんが、量子化誤差(一種の歪みの一種)を聞いています。 16ビットのサンプルを使用してみてください。 (編集:私はちょうどバイト配列にそれらを配置するいくつかのビット操作を行う必要があります実現したが、私は[ここ](http://stackoverflow.com/a/26824664/2891664)と説明するコードがありますアイデア。) – Radiodef

答えて

1

私の意見では、8ビットのオーディオのためにquantization errorと聞いていると思いますが、16ビットに切り替える必要があります。量子化誤差は、ノイズとも呼ばれますが、正弦波の高調波歪みの一種で、聞こえる微妙な倍音の元です。

8ビットは、スピーチのようなものではノイズのように聞こえることがあります。純粋なトーンで歪みが目立ちます。

コードを大まかにMCVEに変更して、その違いを実証しました。

class SoundTest { 
    static final int bufferLength = 44100; 
    static final AudioFormat af8 = new AudioFormat(bufferLength, 8, 1, true, false); 
    static final AudioFormat af16 = new AudioFormat(bufferLength, 16, 1, true, false); 
    static volatile boolean go = true; //to be changed somewhere else 

    static void startSound8(double[] hertz) { 
     if (hertz.length == 0) {return;} 
     try { 
      SourceDataLine sdl = AudioSystem.getSourceDataLine(af8); 
      sdl.open(); 
      sdl.start(); 
      int i = 0; 
      //iterate as long as the sound must play 
      do { 
       //create a new buffer 
       double[] buf = new double[128]; //arbitrary number 
       final int startI = i; 
       //iterate through each of the tones 
       for (int k = 0; k < hertz.length; k++) { 
        i = startI; 
        //iterate through each of the samples for this buffer 
        for (int j = 0; j < buf.length; j++) { 
         double x = (double)i/bufferLength*hertz[k]*2*Math.PI; 
         double wave1 = Math.sin(x); 
         //decrease volume with increasing pitch 
//      double volume = Math.min(Math.max(300 - hertz[k], 50d), 126d); 
         double volume = 64; 
         buf[j] += wave1*volume; 
         i++; 
         if (i == 9999999) { //prevent i from getting too big 
          i = 0; 
         } 
        } 
       } 

       final byte[] finalBuffer = new byte[buf.length]; 
       //copy the double buffer into byte buffer 
       for (int j = 0; j < buf.length; j++) { 
        //divide by hertz.length to prevent simultaneous sounds 
        // from becoming too loud 
        finalBuffer[j] = (byte)(buf[j]/hertz.length); 
       } 

       //play the sound 
       sdl.write(finalBuffer, 0, finalBuffer.length); 
      } while (go); 
      sdl.flush(); 
      sdl.stop(); 
      synchronized (SoundTest.class) { 
       SoundTest.class.notifyAll(); 
      } 
     } catch (LineUnavailableException e) { 
      e.printStackTrace(); 
     } 
    } 

    static void startSound16(double[] hertz) { 
     if (hertz.length == 0) {return;} 
     try { 
      SourceDataLine sdl = AudioSystem.getSourceDataLine(af16); 
      sdl.open(); 
      sdl.start(); 
      int i = 0; 
      //iterate as long as the sound must play 
      do { 
       //create a new buffer 
       double[] buf = new double[128]; //arbitrary number 
       final int startI = i; 
       //iterate through each of the tones 
       for (int k = 0; k < hertz.length; k++) { 
        i = startI; 
        //iterate through each of the samples for this buffer 
        for (int j = 0; j < buf.length; j++) { 
         double x = (double)i/bufferLength*hertz[k]*2*Math.PI; 
         double wave1 = Math.sin(x); 
         //decrease volume with increasing pitch 
         // double volume = Math.min(Math.max(300 - hertz[k], 50d), 126d); 
         double volume = 16384; 
         buf[j] += wave1*volume; 
         i++; 
         if (i == 9999999) { //prevent i from getting too big 
          i = 0; 
         } 
        } 
       } 

       final byte[] finalBuffer = new byte[buf.length * 2]; 

       //copy the double buffer into byte buffer 
       for (int j = 0; j < buf.length; j++) { 
        //divide by hertz.length to prevent simultaneous sounds 
        // from becoming too loud 

        int a = (int) (buf[j]/hertz.length); 
        finalBuffer[j * 2] = (byte) a; 
        finalBuffer[(j * 2) + 1] = (byte) (a >>> 8); 
       } 

       //play the sound 
       sdl.write(finalBuffer, 0, finalBuffer.length); 
      } while (go); 
      sdl.flush(); 
      sdl.stop(); 
      synchronized (SoundTest.class) { 
       SoundTest.class.notifyAll(); 
      } 
     } catch (LineUnavailableException e) { 
      e.printStackTrace(); 
     } 
    } 

    static void playTone(final double hz, final boolean fewBits) { 
     go = true; 
     new Thread() { 
      @Override 
      public void run() { 
       if (fewBits) { 
        startSound8(new double[] {hz}); 
       } else { 
        startSound16(new double[] {hz}); 
       } 
      } 
     }.start(); 
     try { 
      Thread.sleep(5000); 
     } catch (InterruptedException x) { 
      x.printStackTrace(); 
     } finally { 
      go = false; 
      synchronized (SoundTest.class) { 
       try { 
        SoundTest.class.wait(); 
       } catch (InterruptedException x) { 
        x.printStackTrace(); 
       } 
      } 
     } 
    } 

    public static void main(String[] args) { 
     playTone(220, true); 
     playTone(220, false); 
    } 
} 

は、私は16ビットのバイト配列hereをパックするために使用されるビット操作のための概念について説明し、サンプルコードがあります。

プロフェッショナルアプリケーションで何らかの理由で8ビットを使用する必要がある場合は、純粋な量子化エラーよりも優れた音を量子化する前にditherが追加される可能性があります。 (16ビットでも同様ですが、16ビットでの量子化誤差は蓄積されていなければ聞き取れません)

+0

パーフェクト!これは問題を完全に解決します。面白いディザリングのアイデアも。 –

関連する問題