2016-06-24 1 views
0

J'essaie de générer un ensemble de sons simultanés en temps réel. Mais tous les sons produits par le programme sont "flous", ou "statiques", ou même "crissement" en arrière-plan. Cela est particulièrement visible dans les sons plus graves. Voici le code:Le son généré avec SouceDataLine est flou

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}); 

J'ai essayé enregistrer le son étant sortie de ce programme, et les vagues ne semblent un peu en dents de scie. Mais quand j'imprime les ondes générées directement à partir du programme, elles semblent parfaitement lisses. Le son que je génère ne semble pas correspondre au son provenant des enceintes. Quelqu'un peut-il attraper ce que je fais mal?

+0

Bien que je n'ai pas exécuté votre code, mon intuition est que c'est parce que vous utilisez 8- échantillons de bits et sont l'erreur de quantification d'audience (un type de distorsion carrée). Essayez d'utiliser des échantillons 16 bits. (edit: que je viens de réaliser vous obligerait à faire quelques opérations pour les mettre dans le tableau des octets, mais j'ai du code pour le faire [ici] (http://stackoverflow.com/a/26824664/2891664) et expliquer l'idée.) – Radiodef

Répondre

1

Selon mon commentaire, je pense que vous entendez quantization error en raison de l'audio 8 bits et vous devriez passer en 16 bits. L'erreur de quantification est parfois appelée bruit, mais elle est un type de distorsion harmonique carrée et est la source des harmoniques subtiles que vous entendez. 8 bits est parfois acceptable pour des choses comme la parole où cela ressemblera plus à du bruit. La distorsion est plus perceptible avec des sons purs.

J'ai transformé votre code en MCVE pour démontrer la différence.

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); 
    } 
} 

Je discute des concepts pour les opérations de bits j'ai utilisé pour emballer le tableau d'octets 16 bits here et il y a des exemples de code.

Il convient également de mentionner que si une application professionnelle devait utiliser 8 bits pour une raison quelconque, elle ajouterait probablement dither avant la quantification, ce qui sonne mieux qu'une simple erreur de quantification. (Même chose pour 16 bits, d'ailleurs, mais l'erreur de quantification à 16 bits est inaudible sauf si elle a été accumulée.)

+0

Parfait! Cela résout le problème à fond. Idée intéressante de tramage aussi. –