【问题标题】:Control gain of sound channels individually using Java使用 Java 单独控制声道增益
【发布时间】:2016-04-26 11:22:14
【问题描述】:

我已经编写了这个小信号生成方法。我的目标是在两个通道(左右)之间产生轻微的时间延迟或通道之间的增益略有不同的哔哔声。 目前,我通过为一个通道填充一个零并为第二个通道填充一个值并进一步向下交换通道之间的行为来创建延迟(如果您有任何提示或想法如何更好地做到这一点,我们将不胜感激。) 下一阶段是对增益做类似的事情。我已经看到 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();
}

【问题讨论】:

标签: java audio javasound sampling


【解决方案1】:

你想让你的哔哔声相距多远?我编写了一个程序,它可以使正弦哔声的声音相隔数百帧(44100 fps),并附上源代码here,欢迎您检查/复制/重写。

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

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

单文件:F1、F2、F3、... 立体文件 F1L, F1R, F2L, F2R, F3L, F3R, ...

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

立体文件 F1L, 0, F2L, 0, F3L, F1R, F4L, F2R, ...

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

制作哔哔声的第一个单声道数组只需像您一样使用正弦函数即可。您可以通过在某些帧的过程中增加音量来“修整边缘”,以最大限度地减少因突然启动或停止的不连续性而产生的咔嗒声。

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

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

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

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

【讨论】:

  • 非常感谢您的输入,您的程序看起来很有用。我会调查一下。最初声音之间的距离应该非常小,大约 40-100 uSec。然而,在这一点上,我愿意尽可能低地使用普通声卡。作为一个小的后续问题,在搜索了更多内容后,我发现 FloatControl 还支持 PAN 和 BALANCE 操作。这些和你在这里所做的有什么不同吗?
  • 我认为声卡的极限可以被认为是它所能支持的最高帧率。标准声卡可以支持 44100 fps,也就是说 1 帧大约是 22.5 微秒?使用线性插值可能会变小,例如,帧 1 R 获得 90% 帧 1 L 和 10% 帧 2 L。我没有发现 PAN 和 BALANCE 控件始终如一地可靠或响应与个人一起工作帧。当它们工作时,它们通常与在完整的数据缓冲区上操作有关,而不是在单个帧级别上进行操作,因此没有太多的粒度。
猜你喜欢
  • 1970-01-01
  • 2012-09-02
  • 2014-05-07
  • 1970-01-01
  • 2016-09-16
  • 2014-04-01
  • 2021-10-27
  • 1970-01-01
  • 2012-12-31
相关资源
最近更新 更多