在Java中生成正弦波时背景噪音

发布于 2024-07-16 07:52:34 字数 1813 浏览 5 评论 0原文

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

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);
}

I'm getting a slight distortion (sounds like buzzing) in the background when I run the following code. Because of its subtle nature it makes believe there is some sort of aliasing going on with the byte casting.

AudioFormat = PCM_SIGNED 44100.0 Hz, 16 bit, stereo, 4 bytes/frame, big-endian

Note: code assumes (for now) that the data is in big endian.

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);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

孤独岁月 2024-07-23 07:52:35

您的评论说代码假定为大端字节序。

从技术上讲,您实际上以小端方式输出,但这似乎并不重要,因为通过幸运的怪癖,您的最高有效字节始终为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;
        }
    }
 }

Your comments say that the code assumes big-endian.

Technically you're actually outputting in little-endian, however it doesn't seem to matter because through a lucky quirk your most significant byte is always 0.

EDIT: to explain that further - when your value is at its maximum value of 127, you should be writing (0x00, 0x7f), but the actual output from your code is (0x7f, 0x00) which is 32512. This happens to be near the proper 16 bit maximum value of 32767, but with the bottom 8 bits all zero. It would be better to always use 32767 as the maximum value, and then discard the bottom 8 bits if required.

This means that even though you're outputting 16-bit data, the effective resolution is only 8 bit. This seems to account for the lack of sound quality.

I've made a version of your code that just dumps the raw data to a file, and can't see anything otherwise wrong with the bit shifting itself. There's no unexpected changes of sign or missing bits, but there is a buzz consistent with 8 bit sample quality.

Also, for what it's worth your math will be easier if you calculate the wave equation based on sample counts, and then worry about byte offsets separately:

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;
        }
    }
 }
橘味果▽酱 2024-07-23 07:52:35

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

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

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

例如:

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

注意该波形各部分之间的断开。 这可能会引起嗡嗡声。

I assume you are calling this code repeatedly to play a long sound.

Is there a chance that the wave you are generating is not getting to complete a full period before it is written?

If the wave gets "cut-off" before it completes a full period and then the next wave is written to the output, you will certainly hear something strange and I assume that may be what is causing the buzzing.

For example:

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

Notice the disconnect between parts of this wave. That might be causing the buzzing.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文