java - Android AudioTrack getPlaybackHeadPosition 总是返回 0
问题描述
我正在编写一些旨在获取 Wave 文件的代码,并将其写入模式流中的 AudioTrack。这是使 AudioTrack 流模式工作的最小可行测试。
但是一旦我将一些音频缓冲区写入 AudioTrack,然后调用 play(),getPlaybackHeadPosition() 方法就会不断返回 0。
编辑:如果我忽略我的可用帧检查,只是不断地将缓冲区写入 AudioTrack,则 write 方法返回 0(在第一次缓冲区写入之后),表明它根本没有再写入任何音频。所以似乎 AudioTrack 只是不想开始播放。
我的代码正确启动了音轨。play 方法没有抛出任何异常,所以我不确定出了什么问题。
单步执行代码时,我的所有内容都与我的预期完全一致,所以我在想我的 AudioTrack 配置错误。
我在模拟器上运行,但我认为这不应该是一个问题。
我使用的 WavFile 类是一个经过审查的类,我已经在许多 Java 项目中可靠地运行它,它经过测试运行良好。
观察以下日志写入,这是来自较大代码块的片段。此日志写入永远不会命中...
if (headPosition > 0)
Log.e("headPosition is greater than zero!!");
..
public static void writeToAudioTrackStream(final WavFile wave)
{
Log.e("writeToAudioTrackStream");
Thread thread = new Thread()
{
public void run()
{
try {
final float[] data = wave.getData();
int format = -1;
if (wave.getChannel() == 1)
format = AudioFormat.CHANNEL_OUT_MONO;
else if (wave.getChannel() == 2)
format = AudioFormat.CHANNEL_OUT_STEREO;
else
throw new RuntimeException("writeToAudioTrackStatic() - unsupported number of channels value = "+wave.getChannel());
final int bufferSizeInFrames = 2048;
final int bytesPerSmp = wave.getBytesPerSmp();
final int bufferSizeInBytes = bufferSizeInFrames * bytesPerSmp * wave.getChannel();
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, wave.getSmpRate(),
format,
AudioFormat.ENCODING_PCM_FLOAT,
bufferSizeInBytes,
AudioTrack.MODE_STREAM);
int index = 0;
float[] buffer = new float[bufferSizeInFrames * wave.getChannel()];
boolean started = false;
int framesWritten = 0;
while (index < data.length) {
// calculate the available space in the buffer
int headPosition = audioTrack.getPlaybackHeadPosition();
if (headPosition > 0)
Log.e("headPosition is greater than zero!!");
int framesInBuffer = framesWritten - headPosition;
int availableFrames = bufferSizeInFrames - framesInBuffer;
// once the buffer has no space, the prime is done, so start playing
if (availableFrames == 0) {
if (!started) {
audioTrack.play();
started = true;
}
continue;
}
int endOffset = availableFrames * wave.getChannel();
for (int i = 0; i < endOffset; i++)
buffer[i] = data[index + i];
int samplesWritten = audioTrack.write(buffer , 0 , endOffset , AudioTrack.WRITE_BLOCKING);
// could return error values
if (samplesWritten < 0)
throw new RuntimeException("AudioTrack write error.");
framesWritten += samplesWritten / wave.getChannel();
index = endOffset;
}
}
catch (Exception e) {
Log.e(e.toString());
}
}
};
thread.start();
}
解决方案
根据文档,
为了可移植性,应用程序应通过写入数据将数据路径设置为允许的最大值,直到 write() 方法返回一个短传输计数。这允许 play() 立即启动,并减少欠载的机会。
仔细阅读,这可能会与之前的说法相矛盾:
...您可以选择在调用 play() 之前初始化数据路径,方法是写到
bufferSizeInBytes
...
(强调我的),但意图很清楚:你应该先写一篇短文。
这只是为了开始游戏。实际上,一旦发生这种情况,您就可以
getPlaybackHeadPosition()
用来确定何时有更多可用空间。我已经在我自己的代码中成功地使用了这种技术,在许多不同的设备/API 级别上。
顺便说一句:您应该准备好getPlaybackHeadPosition()
仅以较大的增量进行更改(如果我没记错的话,它是getMinBufferSize()/2
)。这是系统可用的最大分辨率;onMarkerReached()
不能用来做得更好。
推荐阅读
- c - memcpy 布尔值无效 *
- azure - 仅限特定文件夹的 Azure Web 应用 FTP 或存储资源管理器权限
- google-cloud-platform - 如何使用医疗实体提取作为基础模型制作 Automl 模型?
- javascript - 如何通过 Angular 组件将 HTML 元素 ID 分配给 JavaScript 函数?
- python - Unhashable 类型:将 modin 与 pandas 一起使用时的系列?
- database - 更改为使用 pgx 时 GORM 失败:migrator.go:206:65: undefined: gorm.ColumnType
- ruby - rest-client:如何限制休息请求只询问前 5 条记录?
- r - 在 R 中将 SpatialPolygonsDataFrame 导出为 geojson 或 topojson
- c - 如何在 C 中编译和运行`main;`
- javascript - 如何让 chrome 扩展加载网站而不是 window.html