安卓:正弦波生成

12
我正在尝试使用AudioTrack生成正弦、方波和锯齿波。然而,这个音频听起来并不像是一个纯正的正弦波,而像是有其他波形叠加在上面。如何在使用第一个示例中的方法时获得第二个示例中的纯正弦波?因为第一个示例只是在第二个示例中移动了一些算术运算,它们不应该产生相同的波形吗?
@Override
        protected Void doInBackground(Void... foo) {
            short[] buffer = new short[1024];
            this.track = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM);
            float samples[] = new float[1024];

            this.track.play();

            while (true) {
                for (int i = 0; i < samples.length; i++) {
                    samples[i] = (float) Math.sin( (float)i * ((float)(2*Math.PI) * frequency / 44100));    //the part that makes this a sine wave....
                    buffer[i] = (short) (samples[i] * Short.MAX_VALUE);
                }
                this.track.write( buffer, 0, samples.length );  //write to the audio buffer.... and start all over again!

            }           
        }

注意:这确实给我一个纯正的正弦波:

@Override
        protected Void doInBackground(Void... foo) {
            short[] buffer = new short[1024];
            this.track = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM);
            float increment = (float)(2*Math.PI) * frequency / 44100; // angular increment for each sample
            float angle = 0;
            float samples[] = new float[1024];

            this.track.play();

            while (true) {
                for (int i = 0; i < samples.length; i++) {
                    samples[i] = (float) Math.sin(angle);   //the part that makes this a sine wave....
                    buffer[i] = (short) (samples[i] * Short.MAX_VALUE);
                    angle += increment;
                }
                this.track.write( buffer, 0, samples.length );  //write to the audio buffer.... and start all over again!

            }           
        }

感谢Martijn:问题在于波在缓冲区的波长之间被截断了。增加缓冲区大小解决了第二个示例中的问题。似乎Math.PI * 2算术运算是循环中最密集的,因此将该值移动到仅计算一次的外部变量可以解决所有问题。


2
听起来你可能听到了一些混叠或削波。尝试减小你正在生成的声波的振幅。 - Robert Harvey
1
那为什么不使用可行的代码呢?我不确定你的问题是什么。 - Robert Harvey
1
尝试通过以下方式优化您的代码:1)增加缓冲区大小,2)预先准备好缓冲区,并将其不断重写到输出流中(这将需要一些数学计算来确定缓冲区的完美大小,以确保整个正弦波完全适合其中)。 - Martijn Courteaux
我认为@martijn的意思是,您需要确保波形的某个部分在水平方向上没有被截断。 - Robert Harvey
2
哦,我现在明白你的意思了。就是缓冲区可能会在完整波长之间结束波形,导致每次缓冲推送的开头和结尾都会变形? - K. Barresi
显示剩余7条评论
3个回答

3
尝试通过以下方法优化您的代码:
1. 增加缓冲区大小 2. 仅需准备一次缓冲区,并将其不断重写到输出流中(这需要进行一些数学计算,以确保整个正弦波完美地适合其中)。
为什么?因为我怀疑缓冲区准备时间过长,在两次缓冲区推送之间造成了过大的延迟,可能导致噪音。

2
我唯一看到您两个代码示例之间的实质性差异是第一个示例中的方程包含整数 (I),因此您可能正在执行整数(而不是浮点)运算。这会导致阶梯状效应,为您的波形添加不必要的谐波。
我怀疑如果您在方程中将 I 强制转换为浮点数,它将产生纯正弦波。
samples[i] 
    = (float) Math.sin( (float)i * ((float)(2*Math.PI) * frequency / 44100));

将 i 转换为浮点数不会对计算产生任何影响。 - you786
@you786 好的,没问题。我想我们已经确认过了。 - Robert Harvey

1

这些答案都无法解决问题。缓冲区长度应该是采样率的倍数,或者至少是一次旋转的长度。让我们将其分解为很多变量来展示正在发生的事情:

int sampleRate = 44100;
int bitsPerChannel = 16;
int bytesPerChannel = bitsPerChannel / 8;
int channelCount = 1;
int bytesPerSample = channelCount * bytesPerChannel;
int bytesPerRotation = sampleRate * bytesPerSample * (1d / (double) frequency);

然后您可以将这个bytesPerRotation乘以任何数字,它都不会改变一个事实:声音不会出现故障。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接