Android AudioTrack 缓冲问题

2024-04-12

好的,我有一个频率发生器,它使用 AudioTrack 将 PCM 数据发送到硬件。这是我使用的代码:

private class playSoundTask extends AsyncTask<Void, Void, Void> {
  float frequency;
  float increment;
  float angle = 0;
  short samples[] = new short[1024];

  @Override
  protected void onPreExecute() {
   int minSize = AudioTrack.getMinBufferSize( 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT );        
   track = new AudioTrack( AudioManager.STREAM_MUSIC, 44100, 
     AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, 
     minSize, AudioTrack.MODE_STREAM);
   track.play();
  }

  @Override
  protected Void doInBackground(Void... params) {
   while( Main.this.isPlaying)
   {
    for( int i = 0; i < samples.length; i++ )
    {
     frequency = (float)Main.this.slider.getProgress();
     increment = (float)(2*Math.PI) * frequency / 44100;
     samples[i] = (short)((float)Math.sin( angle )*Short.MAX_VALUE);
     angle += increment;
    }

    track.write(samples, 0, samples.length);
   }
   return null;
  }
 }

频率与滑动条相关联,并且在样本生成循环中报告正确的值。当我启动应用程序时,一切都很好。当您沿着滑动条拖动手指时,您会听到美妙的扫过声音。但在玩了大约 10 秒后,音频开始变得不稳定。它不是平滑的扫描,而是交错的,并且大约每 1000 Hz 左右改变一次音调。关于可能导致这种情况的原因有什么想法吗?

这是所有代码,以防问题出在其他地方:

    public class Main extends Activity implements OnClickListener, OnSeekBarChangeListener {
 AudioTrack track;
 SeekBar slider;
 ImageButton playButton;
 TextView display; 

 boolean isPlaying=false;

 /** Called when the activity is first created. */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

  display = (TextView) findViewById(R.id.display);
  display.setText("5000 Hz");

  slider = (SeekBar) findViewById(R.id.slider);
  slider.setMax(20000);
  slider.setProgress(5000);
  slider.setOnSeekBarChangeListener(this);


  playButton = (ImageButton) findViewById(R.id.play);
  playButton.setOnClickListener(this);

 }

 private class playSoundTask extends AsyncTask<Void, Void, Void> {
  float frequency;
  float increment;
  float angle = 0;
  short samples[] = new short[1024];

  @Override
  protected void onPreExecute() {
   int minSize = AudioTrack.getMinBufferSize( 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT );        
   track = new AudioTrack( AudioManager.STREAM_MUSIC, 44100, 
     AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, 
     minSize, AudioTrack.MODE_STREAM);
   track.play();
  }

  @Override
  protected Void doInBackground(Void... params) {
   while( Main.this.isPlaying)
   {
    for( int i = 0; i < samples.length; i++ )
    {
     frequency = (float)Main.this.slider.getProgress();
     increment = (float)(2*Math.PI) * frequency / 44100;
     samples[i] = (short)((float)Math.sin( angle )*Short.MAX_VALUE);
     angle += increment;
    }

    track.write(samples, 0, samples.length);
   }
   return null;
  }
 }



 @Override
 public void onProgressChanged(SeekBar seekBar, int progress,
   boolean fromUser) {
  display.setText(""+progress+" Hz");
 }

 public void onClick(View v) {
  if (isPlaying) {
   stop();
  } else {
   start();
  }
 }

 public void stop() {
  isPlaying=false;
  playButton.setImageResource(R.drawable.play);
 }

 public void start() {
  isPlaying=true;
  playButton.setImageResource(R.drawable.stop);
  new playSoundTask().execute();
 }

 @Override
 protected void onResume() {
  super.onResume();

 }

 @Override
 protected void onStop() {
  super.onStop();
  //Store state
  stop();

 }


 @Override
 public void onStartTrackingTouch(SeekBar seekBar) {
  // TODO Auto-generated method stub

 }


 @Override
 public void onStopTrackingTouch(SeekBar seekBar) {
  // TODO Auto-generated method stub

 }
}

我找到原因了!

问题是 AudioTrack 缓冲区的大小。如果您无法足够快地生成样本,样本就会用完,播放会暂停,并在再次有足够样本时继续播放。

唯一的解决方案是确保您可以足够快地生成样本(44100/s)。作为快速修复,请尝试将采样率降低到 22000(或增加缓冲区的大小)。

至少这对我来说是这样的——当我优化样本生成例程时,跳跃消失了。

(增加缓冲区的大小会使声音稍后开始播放,因为有一些保留等待 - 直到缓冲区填满。但是,如果生成样本的速度不够快,样本最终会用完)。


哦,你应该将不变变量放在循环之外!也许可以使样本数组更大,以便循环运行更长的时间。

short samples[] = new short[4*1024];
...

frequency = (float)Main.this.slider.getProgress();
increment = (float)(2 * Math.PI) * frequency / 44100;
for(int i = 0; i < samples.length; i++)
{
   samples[i] = (short)((float)Math.sin(angle) * Short.MAX_VALUE);
   angle += increment;
}

您还可以预先计算频率 200Hz-8000Hz 的所有正弦值,步长为 10Hz。或者按需执行此操作:当用户选择频率时,使用您的方法生成一段时间样本并将它们保存到数组中。当您生成足够的样本以获得一个完整的正弦波时,您可以继续循环数组(因为 sin 是一个周期函数)。实际上,声音可能会出现一些小的不一致,因为您从未精确地达到一个周期(因为您将角度增加 1,并且一个完整正弦波的长度是sampleRate / (double)frequency样品)。但采用正确的周期倍数会使不一致之处变得不明显。

另请参阅http://developer.android.com/guide/practices/design/performance.html http://developer.android.com/guide/practices/design/performance.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android AudioTrack 缓冲问题 的相关文章

  • 启动画面反应本机后出现白屏

    编辑 似乎是因为 MainActivity 加载太重而生成白屏 我设法首先使用本机启动屏幕来解决 然后在本机被杀死后立即基于下一个插件的 js 实现 我做了一些修改完美匹配两个启动画面https github com crazycodebo
  • Parcelable 写入可序列化对象 getactivity() 时遇到 IOException

    所以我在 logcat 中得到了这个 java lang RuntimeException Parcelable encountered IOException writing serializable object name com re
  • 不支持动态值作为注释中的属性 - AspectJ Android [AOP Android]

    我正在使用自定义注释来记录用户单击的 id 但我收到一个错误 属性值必须是常量 我的代码片段如下 mAssetId Asset getContentId TrackEvent track event ArrayParams Params k
  • 以编程方式启用/禁用广播接收器

    我们有一个可以扫描蓝牙设备的应用程序 负责扫描的代码应仅在启用蓝牙时运行 此外 我们希望随时禁用 启用此功能 我们选择实现一个注册BluetoothAdapter ACTION STATE CHANGED广播的BroadcastReceiv
  • 如何在发布版本中使用 Zebra EMDK?

    所以我有一台 Zebra MC330M 设备 我之前创建了一个应用程序 我想使用 PDA 内置的条形码扫描仪 如果在调试模式下运行我的应用程序 一切正常 我可以读取条形码 但是如果我创建暂存或释放版本 apk 则条形码读取器不活动 因此如果
  • 如何防止 Activity 在后退操作时重新加载

    我有连接到互联网以获取数据的应用程序 我可以多层次访问数据 假设我从第 3 级开始 在第 4 级我决定返回 每当我按回之前的活动时 就会从互联网重新加载数据 有可能阻止这种情况吗 我尝试以单顶模式运行该活动 将数据加载代码移至 single
  • 使用反向无限滚动添加到 ListView 时保持滚动位置

    我正在构建一个类似聊天的 Android 应用程序 类似于环聊 为此 我使用垂直 ListViewstackFromBottom true and transcriptMode normal 该列表按从较旧的消息 顶部 到较新的消息 底部
  • 检查 Firebase 邀请是否引导至 Play 商店

    当在 Android 上使用 Firebase 邀请并在应用程序启动时访问动态链接时 有没有办法知道用户是通过邀请刚刚安装了该应用程序还是已经安装了该应用程序 非常感谢 Borja 编辑 感谢 Catalin Morosan 的回答 事实证
  • 如何在android中的应用程序小部件中找到哪个按钮被点击?

    我想设计一个简单的应用程序小部件 它有两个文本视图和两个用于上一个 下一个的按钮 我很难处理应用程序小部件中的按钮单击 实际上我的愿望是 如果用户单击上一个按钮 我想显示以前的值 如果用户单击下一个按钮 我想显示数据库中的下一个值 如何知道
  • 完成特定 Activity 的所有实例

    应用程序中可以有很多活动 最后启动的活动保留在堆栈顶部 按下后它会完成当前活动 我有一系列活动 这是流程 如果我们有 A B C 1 D C 2 活动 C 1 和 C 2 是在导航应用程序时启动的活动 C 的两个不同实例 因此 必需的是清除
  • 无法调整 Android React Native 模块中线性布局子项的大小

    完整代码在这里 https github com sbaar ResizableLLRN 这里有关于 java 中正确行为和 React Native 中错误行为的视频 https drive google com file d 0Bxl2
  • EditText 不显示当前输入(Android 4)

    我的 Android 应用程序包含一个EditText http developer android com reference android widget EditText html查看可以在其中键入一些短消息 单行 按键盘的DONE键
  • Android 连接有时会被拒绝(并非所有时候)

    我编写了一个 WiFi Direct 代码连接并在它们之间创建了一个连接 然后我创建了一个ServerSocket在第一面和一个Socket在客户端并开始在它们之间发送数据 第一次启动应用程序时它工作成功 但是当我关闭应用程序并再次启动它时
  • Android Youtube API 可用吗? [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 有适用于 Android 的 YouTube API 吗 如果不是 除了通过网络浏览器之外 如何从 Yo
  • 如何为工具栏上的溢出菜单中的菜单项设置字体

    我想更改项目的默认字体溢出菜单并设置自定义字体 我尝试添加一个工厂LayoutInflater并在onCreateView 方法我改变了TextView的字体 但这没有用 这是代码 在 onCreateOptionsMenu 内 getLa
  • 日志记录在 Android 设备上实际上有什么作用?

    我一直在 Android 示例中看到这样的代码 try catch Exception e Log e Error e getMessage 什么是Log e实际上在物理设备上做什么 它进入系统日志 开发人员可以通过 SDK 工具访问该日志
  • Activity 上的 OnTouchListener 从不调用

    我使用了这段代码 但是当我在运行时单击活动时 它永远不会在 OnTouch 方法中命中 有人可以指导我我做错了什么吗 我需要设置此活动的内容视图吗 实际上我想要用户在执行过程中触摸的活动的坐标 public class TouchTestA
  • Android应用程序中的模式输入

    我想知道是否有其他替代方案可以替代 Android 上平庸的 EditText 密码输入 是否有 API 或开源代码可以集成到我的应用程序中 类似于锁屏图案解锁 Intent 可能会返回哈希值 数字 字符串或代表用户输入的模式的任何内容 我
  • RecyclerView 适配器的 Kotlin 泛型

    我正在尝试编写一个通用的 recyclerview 适配器 我找到了几个例子 然而 仍然无法弄清楚如何实现通用适配器 我写的代码是 open abstract class BaseAdapter
  • Android 使用非公历

    我正在创建一个DatePickerDialogFragment用户将在其中选择出生日期 我想确保我可以处理非公历日期 我无法更改在我的设备上使用的日历类型 Android 是否允许用户切换日历类型 如果是的话 步骤是什么 到目前为止我还没有

随机推荐

  • 多线程时 For 循环索引超出范围 ArgumentOutOfRangeException

    当我迭代时 我遇到了一些奇怪的行为dummyText List in the ThreadTest方法我得到索引超出范围异常 ArgumentOutOfRangeException 但是如果我删除线程并且只打印出文本 那么一切都会正常 这是
  • Azure 文件存储 SMB 列出目录中的文件速度很慢

    我们有一个通过 Azure 文件列出文件夹中文件的应用程序 当我们使用C 方法时 Directory GetFiles account file core windows net xyz 当有 2000 个文件时 大约需要一分钟 如果我们使
  • MPMoviePlayerController 取消全屏模式在 iOS 6 中不起作用

    The MPMoviePlayerController在装有 iOS 5 的 iPad 上运行良好 最初视频通过添加MPMoviePlayerController到某个帧的自身子视图 按全屏按钮MPMoviePlayerController
  • 如何记录 REST API?

    如何记录 REST API 不仅仅是资源的文档 而且实际上是请求中发送的数据是什么以及响应中发回的数据是什么 知道某些东西期望发送 XML 并返回 XML 是不够有用的 或 JASN 管他呢 如何记录请求中发送的数据和响应中发回的数据 到目
  • Realm Swift:如何捕获 RLMException?

    我以为我做得正确 let realm try Realm do try realm write realm add myObject catch print something went wrong 但我仍然遇到崩溃而不是打印语句 我对避免
  • 我怎样才能有不同的提交来进行合并和冲突解决

    我将开发分支合并到我的功能分支中 这导致在解决我提交和推送的分支后出现合并冲突 现在的问题是合并和冲突解决更改位于一次提交中 很难找到解决冲突的方法 当存在合并冲突时 如何才能有两个单独的提交 一个用于合并 另一个用于冲突修复 如果你真的想
  • Angularjs 未捕获错误:[$injector:unpr]

    我正在用java开发购物网站 我正在使用angularjs 我对这些文件有问题 DashboardControll js use strict var app angular module DashboardApp app controll
  • MLlib MatrixFactorizationModel suggestProducts(user, num) 在某些用户上失败

    我训练了一个MatrixFactorizationModel https spark apache org docs 1 3 0 api java org apache spark mllib recommendation MatrixFa
  • 在 JPA 查询中转义冒号字符“:”

    我正在尝试通过使用 字符的 JPA 运行本机查询 特定实例在查询中使用 MySQL 用户变量 SELECT foo bar baz rownum if id foo rownum 1 1 as rownum id foo as rep id
  • 在 View AngularJS 中使用服务

    我对 angularJS 服务有疑问 我有简单的服务 angular module mainApp services factory AuthService function http var currentUser null var au
  • 删除过多的 try-catch 块

    我正在重构一个由其他开发人员编写的中型 WinForms 应用程序 几乎每个类的每个方法都被一个try catch堵塞 99 的情况下 这些 catch 块仅记录异常或清理资源并返回错误状态 我认为很明显这个应用程序缺乏适当的异常处理机制
  • 以逗号分隔的标签输入字段

    我正在开发一个网络应用程序 允许用户通过标签发布内容 但问题是 如果标签被comma并且文本字段值仍然相同 只是用户的视图会有所不同 一个例子是 YouTube 或 StackOverflow 目前我不需要它来检查数据库或任何东西 Than
  • Java 8 从 Map 中的匹配值中提取所有键

    我对 Java8 比较陌生 我有一个场景 我需要从 Map 中检索与对象匹配的所有键 想知道是否有一种方法可以获取所有键而无需再次从列表中迭代它们 Person java private String firstName private S
  • 如何在 Android 上使用 GraphQL 和 Retrofit?

    我是 GraphQL 的新手 但我已经使用 Retrofit 一段时间了 它易于使用且快速 GraphQL 在传递数据的方式方面与 REST API 有很大不同 关于在 Android 上使用 GraphQL 的教程确实不多 我只能找到这个
  • 使用 numpy.genfromtxt 填充缺失值

    尽管前面的问题提出了建议 9999 作为 numpy genfromtxt 的缺失值 https stackoverflow com questions 12274709 9999 as missing value with numpy g
  • 将默认值传递给表值参数 - SQL Server

    我在我们的一个存储过程中使用表值参数 这是我使用的语法 districtlist NumericList readonly NumericList是用户定义的表类型 但是 根据要求 我需要将默认值传递给该表值参数 districtlist
  • Boost 单元测试链接错误——abi 不匹配?

    我正在尝试使用 boost 构建一个单元测试 但链接器抱怨缺少函数 拿这个骨架代码 define BOOST TEST DYN LINK define BOOST TEST MAIN include
  • 无法使用如此大的 URL 生成 XML 文件

    我想使用下面的代码生成 XML 但是当我包含大 URL 时它会出错 甚至 cdata 也不起作用 xml new SimpleXMLElement
  • 数据框操作 - 捕获值的变化

    我目前有一个数据框 如下所示 它显示位置变化 添加 1 个单位 减去 1 个单位或不执行任何操作 0 我正在寻找带有净头寸的第二个数据框 该数据框可以是多头 1 或持平 0 假设不可能有净空头 1 头寸 因此逻辑是从 0 开始 当第一个 1
  • Android AudioTrack 缓冲问题

    好的 我有一个频率发生器 它使用 AudioTrack 将 PCM 数据发送到硬件 这是我使用的代码 private class playSoundTask extends AsyncTask