2012-05-24 83 views
2

我在java中有一個基本的波形發生器,但我需要一些東西來消除當波的幅度急劇變化時我得到的點擊。也就是說,當我開始/停止播放一個波時,特別是如果我有嘟嘟聲。我是否正確地在Java中實現了這個基本的低通濾波器(Phrogz濾波器!)?

Phrogz's answer on SO給了一個非常好的和簡單的功能,但我不知道我執行它的權利。

當我第一次嘗試使用它,我無法得到它的工作,但我似乎記得它的工作非常好......因爲我已經擺弄了很多我的代碼,現在它不似乎再次運作良好。

因此,這裏是最接近我能得到一個SSCCE:

如果你玩這個,你會發現,當過濾是(過濾=真)浪更安靜和點擊稍顯不足,但這似乎主要是由於數量減少。還有每個嗶明顯的「打擊」,我不想要的,我不記得以前在那裏......

import javax.sound.sampled.*; 


public class Oscillator{ 

    private static int SAMPLE_RATE = 22050; 
    private static short MAX_AMPLITUDE = Short.MAX_VALUE; 
    private static AudioFormat af = null; 
    private static SourceDataLine line = null; 
    private int frequency = 440; //Hz 
    private int numLoops = 1000; 
    private int beep = 100; 

    // set to true to apply low-pass filter 
    private boolean filter = true; 
    // set the amount of "smoothing" here 
    private int smoothing = 100; 
    private double oldValue; 

    public Oscillator(){ 

     prepareLine(); 

    } 


    public static void main(String[] args) { 
     System.out.println("Playing oscillator"); 
     Oscillator osc = new Oscillator(); 
     osc.play(); 
    } 


    private void prepareLine(){ 


     af = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, SAMPLE_RATE, 16, 2, 4, SAMPLE_RATE, false); 

     try { 

      DataLine.Info info = new DataLine.Info(SourceDataLine.class, af); 

      if (!AudioSystem.isLineSupported(info)) { 
       System.out.println("Line does not support: " + af); 
       System.exit(0); 
      } 
      line = (SourceDataLine) AudioSystem.getLine(info); 
      line.open(af); 
     } 
     catch (Exception e) { 
      System.out.println(e.getMessage()); 
      System.exit(0); 
     } 
    } 

    private void play() { 

     System.out.println("play"); 

     int maxSize = (int) Math.round((SAMPLE_RATE * af.getFrameSize())/ frequency); 
     byte[] samples = new byte[maxSize]; 

     line.start(); 

     double volume = 1; 

     int count = 0; 
     for (int i = 0; i < numLoops; i ++){ 


      if (count == beep) { 
       if(volume==1) volume = 0; 
       else volume = 1; 
       count = 0; 
      } 

      count ++; 

      playWave(frequency, volume, samples); 


     } 

     line.drain(); 
     line.stop(); 
     line.close(); 
     System.exit(0); 
    } 

    private void playWave(int frequency, double volLevel, byte[] samples) { 

     double amplitude = volLevel * MAX_AMPLITUDE; 

     int numSamplesInWave = (int) Math.round(((double) SAMPLE_RATE)/frequency); 

     int index = 0; 

     for (int i = 0; i < numSamplesInWave; i++) { 

      double theta = (double)i/numSamplesInWave; 

      double wave = getWave(theta); 

      int sample = (int) (wave * amplitude); 


      if (filter) sample = applyLowPassFilter(sample); 



      // left sample 
      samples[index + 0] = (byte) (sample & 0xFF);   
      samples[index + 1] = (byte) ((sample >> 8) & 0xFF); 
      // right sample 
      samples[index + 2] = (byte) (sample & 0xFF); 
      samples[index + 3] = (byte) ((sample >> 8) & 0xFF); 
      index += 4; 
     } 

     int offset = 0; 

     while (offset < index){ 
      double increment =line.write(samples, offset, index-offset); 
      offset += increment; 
     } 
    } 

    private double getWave(double theta){ 

     double value = 0; 

     theta = theta * 2 * Math.PI; 

     value = getSin(theta); 
     //value = getSqr(theta); 

     return value; 

    } 

    private double getSin(double theta){ 
     return Math.sin(theta); 
    } 

    private int getSqr(double theta){ 
     if (theta <= Math.PI) return 1; 
     else return 0; 
    } 

    // implementation of basic low-pass filter 
    private int applyLowPassFilter(int sample){ 

     int newValue = sample; 
     double filteredValue = oldValue + (newValue - oldValue)/smoothing; 

     oldValue = filteredValue; 
     return (int) filteredValue; 
    } 
} 

相關的方法是在年底。如果有人測試這個,如果你有耳機請注意音量!

因此,要麼:

  1. 這是工作,我只是期望太高了這樣一個簡單的實現
  2. 我做錯了什麼,愚蠢和明顯的...

如果它只是1.我應該如何擺脫由於突然振幅變化而導致的惡劣節拍/擊中/點擊?

如果是2好,應該是太長的問題,一個V簡短的回答。

回答

2

低通濾波器不會刪除從突然的振幅變化的點擊。相反,你需要避免突然的幅度變化。

你可以使用低通濾波器來過濾你的幅度水平。

**Pseudo code** 

for i = 0 to numSamplesInWave-1 do 
begin 
    theta = i/numSamplesInWave; 
    wave = getWave(theta); 
    currentAmplitude = applyLowPassFilter(TargetAmplitude); 
    Sample[i] = wave * currentAmplitude; 
end; 

如上所述使用低通濾波器可以很好地平滑輸入值。例如,當用戶更改音量控制時。

在其他情況下,它可能更適合於建立某種形式的包絡。例如,合成器通常使用ADSR envelopes來平滑當新的音色/聲音開始和停止時的幅度變化。

+0

啊好的,謝謝。我只是覺得在做一個更好的工作之前,現在我的代碼似乎在減少音量。我會嘗試將其應用於振幅,並考慮實施ADSR信封。出於好奇,有沒有一個最大的幅度變化,你肯定會聽到點擊? – kiman

+0

@kiman:爲了避免點擊,振幅應該平穩改變。小的不連續幅度變化仍然可以產生點擊。 – Shannon

+1

@kiman:根據上下文,平滑幅度在1-10毫秒內變化可能足以避免明顯的點擊。 – Shannon