【问题标题】:Android buffer size and frequencyAndroid 缓冲区大小和频率
【发布时间】:2013-06-05 07:05:30
【问题描述】:

我使用 Source 数据线生成正弦波并发出声音。

采样率为 44100,块大小为 441。

如果我播放0.1秒,则产生4410短正弦波并发出如下声音。

ByteBuffer cBuf = ByteBuffer.allocate(Constants.BLOCK_SIZE);
    for(int i=0; i<SamplesTotal; i++){  
       if(!cBuf.hasRemaining()){
            cBuf.putShort((short) (Short.MAX_VALUE * Math.sin(2 * Math.PI * time)));
            time += samplingInterval;
       }else{
            line.write(cBuf.array(), 0, cBuf.position());
            cBuf.clear();
            cBuf.putShort((short) (Short.MAX_VALUE * Math.sin(2 * Math.PI * time)));
            time += samplingInterval;
       }
   }
line.write(cBuf.array(), 0, cBuf.position());

现在的问题,当我用智能手机从这个声音中读取数据时,为了找到频率,使用 fftpack 读取 441 短类型数据并进行转换。比如下面的源码。

while (started) {
    int bufferReadResult = audioRecord.read(buffer, 0, blockSize);
    for (int i = 0; i < blockSize && i < bufferReadResult; i++) {
    toTransform[i] = (double) buffer[i] / (Short.MAX_VALUE);
}
    transformer.ft(toTransform);
    publishProgress(toTransform);
}

为了找到特定的频率 19000 和 20000,我像这样过滤并打印出如下所示的频率。

int temp = 0;
        int temp_idx=0;
        double freq=0;
        int testFreq_19 = 380;
        int testFreq_20 = 400;

        for (int i = 0; i < toTransform[0].length; i++) {
            int x = i;
            int downy = (int) (100 - (toTransform[0][i] * 10));
            int upy = 100;
            if(i>testFreq_19-1 && downy<99){
                if(temp<downy){
                    temp=downy;
                    temp_idx=i;
                }
            }
            canvas.drawLine(x/2, downy, x/2, upy, paint);
        }

        if(temp_idx>testFreq_19-10 && temp_idx<testFreq_19+10){
            freq = testFreq_19*(frequency/882);
        }else if(temp_idx>testFreq_20-10 && temp_idx<testFreq_20+10){
            freq = testFreq_20*(frequency/882);
        }else{
            freq= -1;
        }
        Log.d("frequency log", "frequency: " + String.valueOf(freq));

问题是当我生成 0.1 秒时,会写入 10 个块(4410)。 当我从智能手机读取时,我以为我会收到 4410 块,这意味着使用上述源打印 19000 10 次。

但是,结果很糟糕。有时收到 20 或 30 次,即使我收到了 5 次。数据之间有空白(没有数据)我得到了如下结果示例:

frequency: -1
frequency: -1
frequency: 19000.0
frequency: 19000.0
frequency: 19000.0
frequency: -1
frequency: -1
frequency: -1
frequency: 19000.0
frequency: -1
frequency: -1
frequency: -1
frequency: -1
frequency: -1
frequency: 20000
frequency: 20000
frequency: 20000
frequency: 20000
frequency: 20000
frequency: 20000
frequency: 20000
frequency: 20000
frequency: -1
frequency: -1
frequency: 20000
frequency: 20000
frequency: 20000
frequency: 20000
frequency: -1
frequency: 20000
frequency: 20000
frequency: 20000
frequency: 20000
frequency: -1
frequency: -1
frequency: -1
frequency: -1

这段代码有什么问题?

【问题讨论】:

    标签: java android audio frequency


    【解决方案1】:

    当您生成声波时,您是以 16 位样本写出数据。但是,当您再次读回数据时,您一次处理一个字节的数据(但仍将值除以Short.MAX_VALUE),这似乎不正确。

    AudioRecord 对象的音频数据格式是什么 - ENCODING_PCM_8BITENCODING_PCM_16BIT?如果它是 8 位,那么您肯定应该除以 127 (Byte.MAX_VALUE)。如果它是 16 位,那么您应该假设一次处理两个字节并将它们转换为短字节。

    更新

    如果buffer 变量实际上是一个短数组而不是一个字节数组,那么您读取的代码可能就没有问题,您可以忽略我上面的 cmets。但是,我刚刚注意到您编写的代码中有一些奇怪的地方。在本节中:

       if(!cBuf.hasRemaining()){
            cBuf.putShort((short) (Short.MAX_VALUE * Math.sin(2 * Math.PI * time)));
            time += samplingInterval;
       }else{
            line.write(cBuf.array(), 0, cBuf.position());
            cBuf.clear();
            cBuf.putShort((short) (Short.MAX_VALUE * Math.sin(2 * Math.PI * time)));
            time += samplingInterval;
       }
    

    假设if 中的条件应该只是cBuf.hasRemaining() 而没有! 运算符。如果缓冲区有剩余空间,您想写入它。如果没有,您想刷新缓冲区中已经累积的内容,然后在尝试写入之前将其清除。

    但是,一旦你解决了这个问题,我希望你会得到一个 BufferOverflowException。正如我在 cmets 中提到的,您使用的 ByteBuffer 的大小为奇数字节,但一次写出两个字节。假设声明应该是:

    ByteBuffer cBuf = ByteBuffer.allocate(Constants.BLOCK_SIZE*2);
    

    我不确定这是否是导致您的其他问题的原因,但值得修复一下,看看是否有任何不同。

    【讨论】:

    • 我都使用 pcm16 位。那么我必须将格式从 pcm16 更改为 pcm8 吗?除以 127???
    • 如何一次处理两个字节并将它们转换为短字节。上面的来源我有问题吗???
    • 如果要一次处理一个字节,只需更改为 pcm8 即可。我说除以 127,因为这是有符号字节的最大值——你可能只使用 Byte.MAX_VALUE。如果您要使用 pcm16,请查看 these answers 了解如何将两个字节转换为短字节。如果您不确定数据是大端还是小端,您可能需要尝试字节顺序。
    • 也许这篇博文会有所帮助:blog.bjornroche.com/2013/05/…
    • 谢谢大家的关注。但是,我不能很好地理解。使用上面的源代码,我使用了短类型,当我读写时可以代表两个字节。我需要处理转换两个字节吗??
    猜你喜欢
    • 2012-02-03
    • 2017-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-01
    • 1970-01-01
    • 2015-11-08
    • 1970-01-01
    相关资源
    最近更新 更多