Android AudioTrack getPlaybackHeadPosition 总是returns 0
Android AudioTrack getPlaybackHeadPosition always returns 0
我正在编写一些代码,打算获取一个 Wave 文件,并将其写出到模式流中的 AudioTrack。这是使 AudioTrack 流模式正常工作的最低可行测试。
但是一旦我将一些音频缓冲区写入 AudioTrack,然后调用 play(),方法 getPlaybackHeadPosition() 就会持续 returns 0.
编辑:如果我忽略我的可用帧检查,只是不断地将缓冲区写入 AudioTrack,写入方法 returns 0(在第一个缓冲区写入之后),表明它根本没有写入任何更多音频。所以看起来AudioTrack只是不想开始播放。
我的代码正确启动音轨。 play 方法没有抛出任何异常,所以我不确定出了什么问题。
在单步执行代码时,我这边的一切都与我预期的完全一样,所以我在想我的 AudioTrack 配置有误。
我 运行 在模拟器上,但我认为这不是问题。
我正在使用的 WavFile class 是一个经过审查的 class 我拥有并且 运行 在许多 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();
}
根据 documentation、
For portability, an application should prime the data path to the maximum allowed by writing data until the write() method returns a short transfer count. This allows play() to start immediately, and reduces the chance of underrun.
如果严格阅读,这可能会被认为与之前的陈述相矛盾:
...you can optionally prime the data path prior to calling play(), by writing up to bufferSizeInBytes
...
(强调我的意思),但意图很明确:你应该先写一篇短文。
这只是为了开始游戏。一旦发生这种情况,您实际上可以使用
getPlaybackHeadPosition()
以确定何时有更多 space 可用。我已经在我自己的代码中成功地在许多不同的 devices/API 级别上使用了该技术。
顺便说一句:您应该准备好 getPlaybackHeadPosition()
仅以较大的增量更改(如果我没记错的话,是 getMinBufferSize()/2
)。这是系统可用的最大分辨率; onMarkerReached()
不能用来做得更好。
我正在编写一些代码,打算获取一个 Wave 文件,并将其写出到模式流中的 AudioTrack。这是使 AudioTrack 流模式正常工作的最低可行测试。
但是一旦我将一些音频缓冲区写入 AudioTrack,然后调用 play(),方法 getPlaybackHeadPosition() 就会持续 returns 0.
编辑:如果我忽略我的可用帧检查,只是不断地将缓冲区写入 AudioTrack,写入方法 returns 0(在第一个缓冲区写入之后),表明它根本没有写入任何更多音频。所以看起来AudioTrack只是不想开始播放。
我的代码正确启动音轨。 play 方法没有抛出任何异常,所以我不确定出了什么问题。
在单步执行代码时,我这边的一切都与我预期的完全一样,所以我在想我的 AudioTrack 配置有误。
我 运行 在模拟器上,但我认为这不是问题。
我正在使用的 WavFile class 是一个经过审查的 class 我拥有并且 运行 在许多 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();
}
根据 documentation、
For portability, an application should prime the data path to the maximum allowed by writing data until the write() method returns a short transfer count. This allows play() to start immediately, and reduces the chance of underrun.
如果严格阅读,这可能会被认为与之前的陈述相矛盾:
...you can optionally prime the data path prior to calling play(), by writing up to
bufferSizeInBytes
...
(强调我的意思),但意图很明确:你应该先写一篇短文。
这只是为了开始游戏。一旦发生这种情况,您实际上可以使用
getPlaybackHeadPosition()
以确定何时有更多 space 可用。我已经在我自己的代码中成功地在许多不同的 devices/API 级别上使用了该技术。
顺便说一句:您应该准备好 getPlaybackHeadPosition()
仅以较大的增量更改(如果我没记错的话,是 getMinBufferSize()/2
)。这是系统可用的最大分辨率; onMarkerReached()
不能用来做得更好。