[英]Control gain of sound channels individually using Java
我已经编写了这种小信号生成方法。 我的目标是发出一个蜂鸣声,两个通道(左右)之间的时间延迟很小,或者两个通道之间的增益略有差异。 当前,我通过在缓冲区中填充一个通道的零值和第二个通道的值并进一步向下交换通道之间的行为来创建延迟(如果您有任何技巧或想法可以更好地做到这一点,将不胜感激。)正在做与收益相似的事情。 我已经看到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();
}
您想发出多远的哔哔声? 我写了一个程序,由正弦蜂鸣声听起来高达几百帧(在44100个FPS)分开,并用源代码张贴在这里 ,你是欢迎检查/复制/重写。
在如此低的分离度下,声音在听觉上保持融合,但是可以开始移到一只耳朵或另一只耳朵。 我写这篇文章是因为我想比较卷平移和基于延迟的平移。 为了能够灵活地测试多个文件,该代码比您开始使用的代码更具模块化。 不过,我不会声称自己写的东西更好。
一类采用单声道PCM(范围是浮点型,-1到1)并将其转换为立体声阵列,并在通道之间具有所需的帧延迟。 同一类还可以将单声道文件拆分为立体声文件,其中唯一的区别是音量,并具有第三个选项,当您将单声道数据转换为立体声时,可以结合使用延迟和音量差异。
Monofile: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剪辑中的代码。
Java-Gaming.org上的该线程上对此代码有更多讨论。
最终,我利用在此学到的知识来制作简化的实时3D声音系统。 但是,设置此类内容的“最佳”方法取决于您的目标。 例如,对于我的3D,我编写了一个延迟工具,该工具允许左右立体声分别读取,并且音频混音器和播放比简单的数组到SourceDataLine播放器要复杂得多。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.