java - Android AudioTrack getPlaybackHeadPosition 总是返回 0

标签 java android audio audiotrack

我正在编写一些代码,打算获取一个 Wave 文件,并将其写出到模式流和 AudioTrack 中。这是使 AudioTrack 流模式正常工作的最小可行测试。

但是一旦我将一些音频缓冲区写入 AudioTrack,并随后调用 play(),方法 getPlaybackHeadPosition() 就会持续返回 0。

编辑:如果我忽略我的可用帧检查,只是不断地向 AudioTrack 写入缓冲区,write 方法返回 0(在第一个缓冲区写入之后),表明它不再写入任何音频。所以看起来 AudioTrack 只是不想开始播放。

我的代码正确启动音轨。 play 方法没有抛出任何异常,所以我不确定出了什么问题。

当单步执行代码时,我这边的一切都完全符合我的预期,所以我在想我的 AudioTrack 配置有误。

我在模拟器上运行,但我认为这不是问题。

我正在使用的 WavFile 类是一个经过审查的类,我已经在许多 Java 项目中可靠地运行它,它经过测试可以正常工作。

观察以下日志写入,这是较大代码块的一个 fragment 。此日志写入永远不会命中...

                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...

(强调我的),但 Intent 很明确:你应该先写一篇简短的文章。

这只是为了开始游戏。一旦发生这种情况,您实际上可以使用 getPlaybackHeadPosition() 以确定何时有更多空间可用。我已经在我自己的代码中,在许多不同的设备/API 级别上成功地使用了该技术。

顺便说一句:您应该为 getPlaybackHeadPosition() 的变化做好准备(如果我没记错的话,它是 getMinBufferSize()/2)。这是系统可用的最大分辨率; onMarkerReached() 不能用来做得更好。

关于java - Android AudioTrack getPlaybackHeadPosition 总是返回 0,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54798818/

相关文章:

java - 导致此异常 "HTTP 500 Erreur Interne de Servlet"的可能原因是什么?

android - 除了蓝牙之外,还有其他跨平台离线发送数据的方法吗?

python-3.x - scipy.io.wavfile.read()返回什么?

javascript - 以编程方式在音频元素上设置当前时间属性会导致事件监听器无限期触发

javascript - p5.sound在手机上声音失真了吗?

java - 在 JAVA 中将 PDF 打印为字节数组

java - 如何在 Java 中访问字节数组作为短裤

android - 通过 Intent 调用显示特定 fragment

java - 错误 : Unsupported method: AndroidProject. getVariantNames()

android - 如何将 WebView 添加到 Widget 中?