使用Java单独控制声道的增益

时间:2016-04-26 11:22:14

标签: java audio javasound sampling

我写过这种小信号生成方法。我的目标是在两个通道(左和右)之间产生一个小的时间延迟的蜂鸣声,或者通道之间的增益略有不同。 目前我通过为一个通道填充缓冲区来创建延迟,并为第二个通道填充值,并进一步向下交换通道之间的行为(如果您有任何提示或想法如何更好地执行此操作,我们将不胜感激。) 下一阶段是做与收益类似的事情。我已经看到Java通过FloatControl提供了内置的增益控制:

FloatControl gainControl = 
            (FloatControl) sdl.getControl(FloatControl.Type.MASTER_GAIN);

但我不确定如何分别控制每个通道的增益。有没有内置的方法来做到这一点? 我需要两个独立的流,每个通道一个?如果是这样,我如何同时播放它们? 我对声音编程很新,如果有更好的方法可以让我知道。很感谢任何形式的帮助。

到目前为止,这是我的代码:

    public static void generateTone(int delayR, int delayL, double gainRightDB, double gainLeftDB)
        throws LineUnavailableException, IOException {

    // in hz, number of samples in one second
    int sampleRate = 100000;    // let sample rate and frequency be the same

    // how much to add to each side:
    double gainLeft = 100;//Math.pow(10.0, gainLeftDB / 20.0);
    double gainRight = 100;// Math.pow(10.0, gainRightDB / 20.0);;

    // click duration = 40 us
    double duration = 0.08;
    double durationInSamples = Math.ceil(duration * sampleRate);

    // single delay window duration = 225 us
    double baseDelay = 0.000225;
    double samplesPerDelay = Math.ceil(baseDelay * sampleRate);

    AudioFormat af;

    byte buf[] = new byte[sampleRate * 4];                  // one second of audio in total
    af = new AudioFormat(sampleRate, 16, 2, true, true);    // 44100 Hz, 16 bit, 2 channels


    SourceDataLine sdl = AudioSystem.getSourceDataLine(af);

    sdl.open(af);

    sdl.start();

    // only one should be delayed at a time
    int delayRight = delayR;
    int delayLeft = delayL;

    int freq = 1000;

    /*
     * NOTE:
     * The buffer holds data in groups of 4. Every 4 bytes represent a single sample. The first 2 bytes
     * are for the left side, the other two are for the right. We take 2 each time because of a 16 bit rate.
     * 
     * 
     */
    for(int i = 0; i < sampleRate * 4; i++){
        double time = ((double)i/((double)sampleRate));

        // Left side:
        if (i >= delayLeft * samplesPerDelay * 4                // when the left side plays 
                && i % 4 < 2                                    // access first two bytes in sample
                && i <= (delayLeft * 4 * samplesPerDelay)
                + (4 * durationInSamples))                      // make sure to stop after your delay window

            buf[i] = (byte) ((1+gainLeft) * Math.sin(2*Math.PI*(freq)*time));                   // sound in left ear
        //Right side:
        else if (i >= delayRight * samplesPerDelay * 4          // time for right side
                && i % 4 >= 2                                   // use second 2 bytes
                && i <= (delayRight * 4 * samplesPerDelay)
                + (4 * durationInSamples))                      // stop after your delay window


            buf[i] = (byte) ((1+gainRight) * Math.sin(2*Math.PI*(freq)*time));                  // sound in right ear

    }

    for (byte b : buf)
        System.out.print(b + " ");
    System.out.println();

    sdl.write(buf,0,buf.length);
    sdl.drain();
    sdl.stop();


    sdl.close();
}

1 个答案:

答案 0 :(得分:1)

你想要多远才能发出哔哔声?我编写了一个程序,使正弦波发出的声音高达几百帧(44100 fps),然后用源代码here发布,欢迎您检查/复制/重写。

在如此低的分离水平下,声音在感知上保持融合,但可以开始移动到一只耳朵或另一只耳朵。我写这个是因为我想比较音量平移和基于延迟的平移。为了能够灵活地测试多个文件,代码比您开始时的模块稍微模块化。不过,我不会声称我写的内容更好。

一个类采用单声道PCM(范围是浮点数,-1到1)阵列,并将其转换为立体声阵列,在通道之间具有所需的帧延迟。同一类也可以将单声道文件分割成立体声文件,唯一的区别是音量,还有第三个选项,当您将单声道数据转换为立体声时,可以使用延迟和音量差异的组合。

Monofile:F1,F2,F3,...... Stereofile F1L,F1R,F2L,F2R,F3L,F3R,...

但如果你添加延迟,请说右边2帧:

Stereofile F1L,0,F2L,0,F3L,F1R,F4L,F2R,......

其中F是表示音频波的标准化浮点数(介于-1和1之间)。

制作蜂鸣声的第一个单声道阵列就像使用正弦函数一样。你可能会使边缘变得圆滑[&#39;通过在一些帧的过程中增加音量来最小化突然启动或停止的不连续点所产生的咔嗒声。

编写了另一个类,其唯一目的是通过SourceDataLine输出立体浮点数组。通过将音频输出乘以范围从0到1的因子来处理音量。将标准化值乘以32767以将它们转换为带符号的短路,并将短路分解为我使用的格式的字节(16位,44100 fps,立体声,小端)。

拥有一个阵列播放音频类有点整洁。它的数组很像Clips,但您可以直接访问数据。使用此类,您可以构建和重用许多声音数组。我想我有一些代码可以将wav文件加载到这个DIY剪辑中。

this thread at Java-Gaming.org上有更多关于此代码的讨论。

我最终使用了我在这里学到的一些东西来制作简化的实时3D音响系统。最好的&#34;但是,建立这样的东西的方式取决于你的目标。例如,对于我的3D,我编写了一个延迟工具,允许从左右立体声中分别读取,以及音频混音器和音频。播放比简单的数组到SourceDataLine播放器更复杂。