2

Java で基本的なウェーブ ジェネレーターを使用していますが、ウェーブの振幅が急激に変化したときに発生するクリック音を除去するものが必要です。つまり、特にビープ音が鳴っている場合、ウェーブの再生を開始/停止するときです。

SOに関するPhrogzの回答は、本当に素晴らしくシンプルな機能を提供しましたが、それを正しく実装しているかどうかはわかりません。

最初に使用しようとしたときは動作しませんでしたが、非常にうまく動作したことを覚えているようです...それ以来、コードをいじってみましたが、動作していないようですとても元気です。

したがって、SSCCEに最も近いのは次のとおりです。

これを再生すると、フィルタリングがオン (filter = true) の場合、波がはるかに静かになり、クリック音がわずかに少なくなることがわかりますが、これは主に音量の減少によるものと思われます。各ビープ音には、私が望んでいない顕著な「ヒット」がまだあり、以前にそこにいたことを覚えていません...

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. 良ければ、長すぎる質問に対する短い回答にする必要があります。

4

1 に答える 1

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 エンベロープを使用して、新しい音声/サウンドが開始および停止するときの振幅の変化を滑らかにします。

于 2012-05-25T02:11:00.517 に答える