【问题标题】:Noise in background when generating sine wave in Java在 Java 中生成正弦波时背景中的噪声
【发布时间】:2010-10-18 22:39:25
【问题描述】:

当我运行以下代码时,我在后台得到了轻微的失真(听起来像嗡嗡声)。由于其微妙的性质,它让人相信字节转换存在某种别名。

AudioFormat = PCM_SIGNED 44100.0 Hz,16 位,立体声,4 字节/帧,大端

注意:代码假定(目前)数据采用大端序。

public static void playFreq(AudioFormat audioFormat, double frequency, SourceDataLine sourceDataLine)
{
    System.out.println(audioFormat);
    double sampleRate = audioFormat.getSampleRate();
    int sampleSizeInBytes = audioFormat.getSampleSizeInBits() / 8;
    int channels = audioFormat.getChannels();

    byte audioBuffer[] = new byte[(int)Math.pow(2.0, 19.0) * channels * sampleSizeInBytes];

    for ( int i = 0; i < audioBuffer.length; i+=sampleSizeInBytes*channels )
    {
        int wave = (int) (127.0 * Math.sin( 2.0 * Math.PI * frequency * i / (sampleRate * sampleSizeInBytes * channels) )  );

        //wave = (wave > 0 ? 127 : -127);

        if ( channels == 1 )
        {
            if ( sampleSizeInBytes == 1 )
            {
                audioBuffer[i] = (byte) (wave);
            }

            else if ( sampleSizeInBytes == 2 )
            {
                audioBuffer[i] = (byte) (wave);
                audioBuffer[i+1] = (byte)(wave >>> 8);
            }
        }

        else if ( channels == 2 )
        {
            if ( sampleSizeInBytes == 1 )
            {
                audioBuffer[i] = (byte) (wave);
                audioBuffer[i+1] = (byte) (wave);
            }

            else if ( sampleSizeInBytes == 2 )
            {
                audioBuffer[i] = (byte) (wave);
                audioBuffer[i+1] = (byte)(wave >>> 8);

                audioBuffer[i+2] = (byte) (wave);
                audioBuffer[i+3] = (byte)(wave >>> 8);
            }
        }
    }

    sourceDataLine.write(audioBuffer, 0, audioBuffer.length);
}

【问题讨论】:

    标签: java audio javasound


    【解决方案1】:

    您的 cmets 说代码采用大端序。

    从技术上讲,您实际上以 little-endian 输出,但这似乎并不重要,因为幸运的是,您的最高有效字节始终为 0。

    编辑:进一步解释 - 当您的值处于最大值 127 时,您应该编写 (0x00, 0x7f),但代码的实际输出是 (0x7f, 0x00),即 32512。发生这种情况接近正确的 16 位 最大值 32767,但底部 8 位全为零。最好始终使用 32767 作为最大值,然后根据需要丢弃低 8 位。

    这意味着即使您输出的是 16 位数据,有效分辨率也只有 8 位。这似乎是音质不佳的原因。

    我已经制作了您的代码版本,它只是将原始数据转储到文件中,并且看不到位移本身有任何其他问题。没有意外的符号变化或丢失位,但有符合 8 位采样质量的嗡嗡声。

    此外,如果您根据样本计数计算波动方程,然后单独考虑字节偏移量,那么您的数学运算会更容易:

    int samples = 2 << 19;
    byte audioBuffer[] = new byte[samples * channels * sampleSizeInBytes];
    
    for ( int i = 0, j = 0; i < samples; ++i )
    {
        int wave = (int)(32767.0 * Math.sin(2.0 * Math.PI * frequency * i / sampleRate));
        byte msb = (byte)(wave >>> 8);
        byte lsb = (byte) wave;
    
        for (int c = 0; c < channels; ++c) {
            audioBuffer[j++] = msb;
            if (sampleSizeInBytes > 1) {
                audioBuffer[j++] = lsb;
            }
        }
     }
    

    【讨论】:

    • 啊!现在看到错误,修复幅度部分后,错误变得非常明显,但您的代码也更有效率。谢谢!
    【解决方案2】:

    我假设您重复调用此代码以播放较长的声音。

    您正在生成的波浪是否有可能在写入之前没有完成一个完整的周期?

    如果波形在完成一个完整周期之前被“切断”,然后将下一个波形写入输出,您肯定会听到一些奇怪的声音,我认为这可能是导致嗡嗡声的原因。

    例如:

            /-------\              /-------\              /-------\
      -----/         \       -----/         \       -----/         \
                      \                      \                      \
                       \-----                 \-----                 \-----
    

    请注意此波各部分之间的脱节。这可能会引起嗡嗡声。

    【讨论】:

    • 是的,这不是问题,因为缓冲区足够大,声音可以通过多个周期。它确实考虑了点击,但不考虑持续的嗡嗡声。
    • 这不是正确的答案 - 代码清楚地生成了大量样本,并且在每个循环结束时没有截断
    • 我已经测试了要检查的代码 - 这是一个无意的 8 位量化错误导致了嗡嗡声。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-08
    • 2012-01-27
    • 1970-01-01
    • 1970-01-01
    • 2018-10-26
    相关资源
    最近更新 更多