jjzjj

java - Android Audio - 流式正弦音发生器奇怪的行为

coder 2023-11-22 原文

第一次发贴在这里。我通常喜欢自己找到答案(无论是通过研究还是反复试验),但我在这里遇到了困难。

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

我是如何构建它的: 基本上,我有两个线程——一个工作线程和一个输出线程。每次调用 tick() 方法时,工作线程只需用正弦波数据填充缓冲区。缓冲区填满后,它会提醒输出线程数据已准备好写入音轨。我使用两个线程的原因是因为 audiotrack.write() block ,我希望工作线程能够尽快开始处理其数据(而不是等待音轨完成写入)。 UI 上的 slider 只是更改工作线程中的一个变量,因此工作线程的 tick() 方法将读取频率的任何更改(通过 slider )。

有效的方法: 几乎所有的;线程通信良好,播放中似乎没有任何间隙或咔哒声。尽管缓冲区大小很大(感谢 android),但响应能力还可以。频率变量确实发生了变化,tick() 方法中缓冲区计算期间使用的中间值也发生了变化(由 Log.i() 验证)。

什么不起作用: 出于某种原因,我似乎无法获得可听频率的连续变化。当我调整 slider 时,频率会逐步变化,通常宽至四分之一或五分之一。理论上,我应该听到最小 1Hz 的变化,但我没有。奇怪的是, slider 的变化似乎导致正弦波在谐波序列中播放间隔;但是,我可以验证频率变量没有捕捉到默认频率的整数倍。

我的音轨是这样设置的:

_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 是否正在正确更新。

谢谢!

更新:

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

我还应该注意到,在应用程序生命周期的前 3 秒左右,它工作正常 - slider 的平滑扫描会在音频输出中产生平滑的扫描。在此之后,它开始变得非常不稳定(声音仅每 100Mhz 变化一次),然后,它完全停止响应 slider 输入。

我还修复了一个错误,但我认为它没有影响。 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。="">

工作起来很有魅力!非常感谢你的帮助,禁卫军机器人!

关于java - Android Audio - 流式正弦音发生器奇怪的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10158409/

有关java - Android Audio - 流式正弦音发生器奇怪的行为的更多相关文章

  1. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  2. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  3. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  4. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  5. ruby-on-rails - 启用 Rack::Deflater 时 ETag 发生变化 - 2

    在启用Rack::Deflater来gzip我的响应主体时偶然发现了一些奇怪的东西。也许我遗漏了一些东西,但启用此功能后,响应被压缩,但是资源的ETag在每个请求上都会发生变化。这会强制应用程序每次都响应,而不是发送304。这在没有启用Rack::Deflater的情况下有效,我已经验证页面源没有改变。我正在运行一个使用thin作为Web服务器的Rails应用程序。Gemfile.lockhttps://gist.github.com/2510816有没有什么方法可以让我从Rack中间件获得更多的输出,这样我就可以看到发生了什么?提前致谢。 最佳答案

  6. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  7. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

  8. 【Java 面试合集】HashMap中为什么引入红黑树,而不是AVL树呢 - 2

    HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候

  9. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  10. java - 为什么 ruby​​ modulo 与 java/other lang 不同? - 2

    我基本上来自Java背景并且努力理解Ruby中的模运算。(5%3)(-5%3)(5%-3)(-5%-3)Java中的上述操作产生,2个-22个-2但在Ruby中,相同的表达式会产生21个-1-2.Ruby在逻辑上有多擅长这个?模块操作在Ruby中是如何实现的?如果将同一个操作定义为一个web服务,两个服务如何匹配逻辑。 最佳答案 在Java中,模运算的结果与被除数的符号相同。在Ruby中,它与除数的符号相同。remainder()在Ruby中与被除数的符号相同。您可能还想引用modulooperation.

随机推荐