Android 使用安卓&x27;s AudioTrack组合字节的声音样本会产生噪音

Android 使用安卓&x27;s AudioTrack组合字节的声音样本会产生噪音,android,audio,wav,audiotrack,Android,Audio,Wav,Audiotrack,我正在构建一个相当简单的Android应用程序(sdk修订版14:ICS),它允许用户一次拾取两个音频片段(都是RIFF/WAV格式、little-endian、签名PCM-16位编码),并以各种方式组合它们以创建新的声音。我对这种组合使用的最基本方法如下: //...sound samples are read in to memory as raw byte arrays elsewhere //...offset is currently set to 45 so as to skip t

我正在构建一个相当简单的Android应用程序(sdk修订版14:ICS),它允许用户一次拾取两个音频片段(都是RIFF/WAV格式、little-endian、签名PCM-16位编码),并以各种方式组合它们以创建新的声音。我对这种组合使用的最基本方法如下:

//...sound samples are read in to memory as raw byte arrays elsewhere
//...offset is currently set to 45 so as to skip the 44 byte header of basic
//RIFF/WAV files
...
//Actual combination method
public byte[] makeChimeraAll(int offset){
    for(int i=offset;i<bigData.length;i++){
        if(i < littleData.length){
            bigData[i] = (byte) (bigData[i] + littleData[i]);
        }
        else{
            //leave bigData alone
        }
    } 
    return bigData;
}
/…声音样本作为原始字节数组读入内存
//…偏移量当前设置为45,以便跳过basic的44字节头
//RIFF/WAV文件
...
//实际组合法
公共字节[]makeChimeraAll(整数偏移量){

对于(int i=offset;i我不熟悉android audio,所以我不能回答你所有的问题,但我可以告诉你基本问题是什么:逐字节添加音频数据不起作用。因为它有点起作用,从你的代码和它最常见的事实来看,我假设你有16位PCM数据。然而,无论在哪里,你都是处理字节。字节不适合处理音频(除非音频恰好是8位的)

字节大约是+/-128。您说“我希望看到的值范围为−从上面的Log.d(…)调用在logcat中使用32768到32767,但结果往往在-100到100的范围内(有些异常值超出了该范围)”那么,当您打印字节数组中的值时,您怎么可能进入该范围呢?16位有符号数据的正确数据类型是短的,而不是字节。如果您打印的是短值,您会看到您期望的范围

您必须将字节转换为短字符并对短字符求和。这将解决您听到的大部分杂音。既然您正在读取文件,为什么还要麻烦转换呢?为什么不使用类似的方法将其作为短字符从文件中读取 ()

下一个问题是,您必须处理超出范围的值,而不是让它们“环绕”。最简单的解决方案是简单地将求和作为整数,“剪辑”到较短的范围,然后存储剪辑后的输出。这将消除您的点击和弹出

在psuedo代码中,整个过程如下所示:

file1 = Open file 1
file2 = Open file 2
output = Open output for writing

numSampleFrames1 = file1.readHeader()
numSampleFrames2 = file2.readHeader()
numSampleFrames = min( numSampleFrames1, numSampleFrames2 )
output.createHeader( numSampleFrames )

for( int i=0; i<numSampleFrames * channels; ++i ) {
    //read data from file 1
    int a = file1.readShort();
    //read data from file 2, and add it to data we read from file 1
    a += file2.readShort();
    //clip into range
    if( a > Short.MAX_VALUE )
       a = Short.MAX_VALUE;
    if( a < Short.MIN_VALUE )
       a = Short.MIN_VALUE;
    //write it to the output
    output.writeShort( (Short) a );
}
file1=打开文件1
file2=打开文件2
输出=打开用于写入的输出
numSampleFrames1=file1.readHeader()
numSampleFrames2=file2.readHeader()
numSampleFrames=min(numSampleFrames1,numSampleFrames2)
output.createHeader(numSampleFrames)
对于(int i=0;i Short.MAX_值)
a=短路最大值;
如果(a

在“剪辑”步骤中会有一点失真,但没有简单的方法,剪辑比环绕要好得多。(也就是说,除非你的曲目非常“热”,并且在低频时很重,失真应该不会太明显。如果有问题,您可以做其他事情:例如,将a乘以.5并跳过剪辑,但这样您的输出会安静得多,这在手机上可能不是您想要的).

whoops…感谢您指出需要短数组而不是字节数组;它实际上是16位PCM,所以我不知道为什么我认为逐字节存储和处理可以工作。可能是因为我找不到关于脉冲编码调制如何在内部工作的好解释…您有任何建议参考资料可供学习吗g关于数字音频编码/处理的低级细节?同样值得注意的是,由于我的RIFF/WAV音频文件是使用小端字节顺序编码的,所以我需要使用DataInputStream的修改版本来正确读取短值(股票java版本假定为大端字节)幸运的是,我找到了一个很好的实现必要的按位操作来完成这个任务:是的,你需要处理NealNess,你可以考虑一个类似的RealAccess文件包装器。对于参考文献,你可以从这里开始:还有一本书,叫做java的数字音频,现在已经过时了,有一些不准确的地方,B。但是它有工作代码,这不是你在很多地方都能找到的。更多参考在我的第一个链接中。
//Audio Chimera methods!
public short[] makeChimeraAll(int offset){
    //bigData and littleData are each short arrays, populated elsewhere
    int intBucket = 0;
    for(int i=offset;i<bigData.length;i++){
        if(i < littleData.length){
            intBucket = bigData[i] + littleData[i];
            if(intBucket > SIGNED_SHORT_MAX){
                intBucket = SIGNED_SHORT_MAX;
            }
            else if (intBucket < SIGNED_SHORT_MIN){
                intBucket = SIGNED_SHORT_MIN;
            }
            bigData[i] = (short) intBucket;
        }
        else{
            //leave bigData alone
        }
    } 
    return bigData;
}
file1 = Open file 1
file2 = Open file 2
output = Open output for writing

numSampleFrames1 = file1.readHeader()
numSampleFrames2 = file2.readHeader()
numSampleFrames = min( numSampleFrames1, numSampleFrames2 )
output.createHeader( numSampleFrames )

for( int i=0; i<numSampleFrames * channels; ++i ) {
    //read data from file 1
    int a = file1.readShort();
    //read data from file 2, and add it to data we read from file 1
    a += file2.readShort();
    //clip into range
    if( a > Short.MAX_VALUE )
       a = Short.MAX_VALUE;
    if( a < Short.MIN_VALUE )
       a = Short.MIN_VALUE;
    //write it to the output
    output.writeShort( (Short) a );
}