当前位置: 首页 > 面试题库 >

Android音频-流式正弦波发生器奇怪的行为

陆沈浪
2023-03-14
问题内容

第一次海报在这里。我通常喜欢自己找到答案(通过研究或反复试验得出),但是我很困惑。

我要做什么: 我正在构建一个简单的android音频合成器。现在,我只是在实时播放正弦音,UI中的滑块会随着用户的调整而改变音的频率。

如何构建它: 基本上,我有两个线程-
工作线程和输出线程。每当调用其tick()方法时,工作线程就用正弦波数据填充一个缓冲区。一旦缓冲区被填满,它会警告输出线程数据已准备好写入音频轨道。我使用两个线程的原因是因为audiotrack.write()阻塞,并且我希望工作线程能够尽快开始处理其数据(而不是等待音频轨道完成写入)。UI上的滑块只是更改了工作线程中的变量,因此(通过滑块)对频率的任何更改都将由工作线程的tick()方法读取。

什么有效:
几乎所有东西;线程之间的沟通很好,播放中似乎没有间隙或点击。尽管缓冲区很大(感谢android),但响应能力还不错。频率变量确实会发生变化,tick()方法(由Log.i()验证)的缓冲区计算过程中使用的中间值也会发生变化。

什么不起作用:
由于某种原因,我似乎听不见频率的连续变化。调整滑块时,频率会逐步变化,通常为四分之一或五分之一。从理论上讲,我应该听到1Hz的微小变化,但不是。奇怪的是,似乎对滑块的更改导致正弦波在谐波序列的间隔中播放。但是,我可以验证该频率变量未捕捉到默认频率的整数倍。

我的音轨设置如下:

_buffSize = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);
_audioTrackOut = new AudioTrack(AudioManager.STREAM_MUSIC, _sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, _buffSize, AudioTrack.MODE_STREAM);

工人线程的缓冲区是这样填充的(通过tick()):

public short[] tick()
{
    short[] outBuff = new short[_outBuffSize/2]; // (buffer size in Bytes) / 2
    for (int i = 0; i < _outBuffSize/2; i++) 
    {
        outBuff[i] = (short) (Short.MAX_VALUE * ((float) Math.sin(_currentAngle)));

        //Update angleIncrement, as the frequency may have changed by now
        _angleIncrement = (float) (2.0f * Math.PI) * _freq / _sampleRate;
        _currentAngle = _currentAngle + _angleIncrement;    
    }
    return outBuff;     
}

音频数据是这样写的:

_audioTrackOut.write(fromWorker, 0, fromWorker.length);

任何帮助将不胜感激。如何获得逐渐的频率变化?我非常有信心自己在tick()中的逻辑是正确的,因为Log.i()验证变量angleIncrement和currentAngle是否已正确更新。

谢谢!

更新:

我在这里发现了一个类似的问题:AndroidAudioTrack缓冲问题
解决方案提出,必须能够足够快地为audioTrack生成样本,这才有意义。我将采样率降低到22050Hz,并进行了一些经验测试-
在最坏的情况下,我可以在大约6毫秒内填充缓冲区(通过tick())。这绰绰有余。在22050Hz时,audioTrack为我提供了2048个样本(或4096字节)的缓冲区大小。因此,每个已填充的缓冲区持续约0.0928秒的音频,这比创建数据所需的时间长得多(1〜6
ms)。因此,我知道我没有足够快地生产样品的问题。

我还应注意,在应用程序生命周期的前3秒内,它可以正常工作-
滑块的平滑扫描会在音频输出中产生平滑的扫描。此后,它开始变得非常震荡(声音每100Mhz仅变化一次),此后,它完全停止响应滑块输入。

我还修复了一个错误,但我认为它没有效果。AudioTrack.getMinBufferSize()返回BYTES中最小的允许缓冲区大小,并且我使用此数字作为tick()中缓冲区的长度-
现在我使用此数字的一半(每个样本2字节)。


问题答案:

我找到了!

事实证明,该问题与缓冲区或线程无关。

在最初的几秒钟内听起来不错,因为计算的角度相对较小。随着程序的运行以及角度的增大,Math.sin(_currentAngle)开始生成不可靠的值。

因此,我替换Math.sin()FloatMath.sin()

我也换了 _currentAngle = _currentAngle + _angleIncrement;

_currentAngle = ((_currentAngle + _angleIncrement) % (2.0f * (float) Math.PI));,因此角度总是<2 * PI。

奇迹般有效!非常感谢您的帮助,praetorian droid!



 类似资料:
  • 第一次海报在这里。我通常喜欢自己找到答案(无论是通过研究还是反复试验),但我在这里被难住了。 我正在努力做:我正在建立一个简单的android音频合成器。现在,我只是在实时播放正弦音调,在用户界面中有一个滑块,可以随着用户的调整而改变音调的频率。 我是如何构建它的:基本上,我有两个线程--一个工作线程和一个输出线程。工作线程只是在每次调用其tick()方法时用正弦波数据填充缓冲区。一旦缓冲区被填满

  • 用C/C++编写了一个三维正弦波发生器 代码:

  • 问题内容: 在Java中以任何频率生成正弦波声音的最简单方法是什么?样本大小大于2个字节会有所帮助,但这并不 重要。 问题答案: 见一个自包含的例子。 也许更简单? 如链接答案顶部所示,这51行代码段(在下面重复-分隔为单行和行内注释)大约与 生成音调一样简单(好的,您可以取出5行以上用于谐波)。 人们似乎认为这应该是工具包中内置的一种产生 纯净音调的方法。并非如此,要花一点时间就可以做到。

  • 我想创建一个实时正弦发生器使用苹果核心音频框架。我想做低水平,这样我就可以学习和理解基本原理。 通过使用VSYNC,我可以将循环降低到60 fps。时间不是很紧,但相当稳定。我也有一些代码来手动使用马赫计时,这甚至更不精确。为了可读性我把它省略了。不使用VSYNC或使用马赫定时来获得每秒60次迭代也会造成音频故障。 定时日志: 这里重要的是函数。它每秒被调用60次,并传递给它一个包含基本信息的结构

  • 我试图生成一组点,当绘制成一个图表示1个周期的正弦波。要求是: 1个周期的正弦波 下限=29491 上限=36043 点数=100 振幅=3276 零偏移量=32767 代码: 我正在生成并存储一个文件中的点。当这些点被绘制出来时,我得到了下面的图。 但我只需要一个循环。我怎么能这么做?

  • 我如何建立一个方程来产生扫频正弦波。我是信号处理新手,在网上找不到太多关于生成扫描正弦波的话题。请告诉我一些我可以用来生成方程式并在代码中使用的源代码。非常感谢。