java中实时音频字节数组的快进实现



我正在使用java声音API(targetDataLine和sourceDataLine(管理音频捕获和播放。现在假设在会议环境中,一个参与者的音频队列大小大于抖动大小(由于处理或网络原因(,我想快进该参与者的音频字节,使其小于抖动大小。

如何快进该参与者的音频字节数组?

我不能在正常播放时这样做。播放器线程只从每个参与者的队列中取出一帧并混合播放。我能得到这一点的唯一方法是,如果我取消该参与者的1帧以上,并在将其与其他参与者混合之前将其混合(?(进行快速转发?提前感谢您的任何帮助或建议。

据我所知,有两种方法可以加快播放速度。在一种情况下,速度越快,投球就越高。这方面的编码相对容易。在另一种情况下,音高保持不变,但它涉及到一种处理声音颗粒的技术(颗粒合成(,很难解释。

对于不需要考虑保持相同音高的情况,基本计划如下:不是单帧推进,而是一帧+一个小增量推进。例如,假设在44000帧的过程中前进1.1帧就足以赶上你。(这也意味着音高的增加大约是八度音阶的1/10。(

为了推进";分数";帧,您首先必须将两个括号内帧的字节转换为PCM。然后,使用线性插值来获得中间值。然后将该中间值转换回输出行的字节。

例如,如果从帧[0]前进到帧["1.1"],则需要知道帧[1]和帧[2]的PCM。中间值可以使用加权平均值计算:

value = PCM[1] * 9/10 + PCM[2] * 1/10

我认为让你前进的幅度逐渐改变可能是件好事。需要几十帧来增加增量,并在返回正常排队时留出时间再次减少。如果您突然改变读取音频数据的速率,则可能会引入一种不连续性,这种不连续性会被听到。

我已经使用了这个基本计划来动态控制播放速度,但我还没有将其用于您所描述的情况的经验。如果你也试图强制保持平稳过渡,那么调节可变速度可能会很棘手。

使用颗粒的基本思想包括获得连续的PCM(我不清楚语音的最佳帧数是多少,1到50毫秒被认为是合成中常用的这项技术(,并为其提供一个体积包络,允许您端到端混合顺序颗粒(它们必须重叠(。

我认为颗粒的封套使用了Hann函数或Hamming窗口,但我不清楚细节,比如颗粒的重叠放置,以便它们平稳混合/过渡。我只是涉猎过,我想信号处理公司的人将是最好的选择,以获得如何编码的建议。

我发现了一个很棒的git repo(声音库,主要用于音频播放器(,它实际上用这么多控件实现了我想要的功能。我可以输入一个完整的.wav文件,甚至是大块的音频字节数组,经过处理,我们可以获得更快的播放体验等等。对于实时处理,我实际上在音频字节数组的每个块上都调用了这个函数。

我找到了另一种方法/algo来检测音频块/字节阵列是否为语音,根据结果,我可以简单地忽略播放非语音数据包,这使我们的处理速度提高了1.5倍左右。

public class DTHVAD {
public static final int INITIAL_EMIN = 100;
public static final double INITIAL_DELTAJ = 1.0001;
private static boolean isFirstFrame;
private static double Emax;
private static double Emin;
private static int inactiveFrameCounter;
private static double Lamda; //
private static double DeltaJ;
static {
initDTH();
}
private static void initDTH() {
Emax = 0;
Emin = 0;
isFirstFrame = true;
Lamda = 0.950; // range is 0.950---0.999
DeltaJ = 1.0001;
}
public static boolean isAllSilence(short[] samples, int length) {
boolean r = true;
for (int l = 0; l < length; l += 80) {
if (!isSilence(samples, l, l+80)) {
r = false;
break;
}
}
return r;
}
public static boolean isSilence(short[] samples, int offset, int length) {
boolean isSilenceR = false;
long energy = energyRMSE(samples, offset, length);
// printf("en=%ldn",energy);
if (isFirstFrame) {
Emax = energy;
Emin = INITIAL_EMIN;
isFirstFrame = false;
}
if (energy > Emax) {
Emax = energy;
}
if (energy < Emin) {
if ((int) energy == 0) {
Emin = INITIAL_EMIN;
} else {
Emin = energy;
}
DeltaJ = INITIAL_DELTAJ; // Resetting DeltaJ with initial value
} else {
DeltaJ = DeltaJ * 1.0001;
}
long thresshold = (long) ((1 - Lamda) * Emax + Lamda * Emin);
// printf("e=%ld,Emin=%f, Emax=%f, thres=%ldn",energy,Emin,Emax,thresshold);
Lamda = (Emax - Emin) / Emax;
if (energy > thresshold) {
isSilenceR = false; // voice marking
} else {
isSilenceR = true; // noise marking
}
Emin = Emin * DeltaJ;
return isSilenceR;
}
private static long energyRMSE(short[] samples, int offset, int length) {
double cEnergy = 0;
float reversOfN = (float) 1 / length;
long step = 0;
for (int i = offset; i < length; i++) {
step = samples[i] * samples[i]; // x*x/N=
// printf("step=%ld cEng=%ldn",step,cEnergy);
cEnergy += (long) ((float) step * reversOfN);// for length =80
// reverseOfN=0.0125
}
cEnergy = Math.pow(cEnergy, 0.5);
return (long) cEnergy;
}

}

在这里,我可以将我的字节数组转换为短数组,并通过检测它是语音还是非语音

frame.silence=DTHVAD.isSilence(encodeShortBuffer,0,shortLen(

相关内容

  • 没有找到相关文章

最新更新