代码之家  ›  专栏  ›  技术社区  ›  Scorb

Android AudioTrack getPlaybackHeadPosition始终返回0

  •  0
  • Scorb  · 技术社区  · 5 年前

    我正在写一些代码,打算采取一个波文件,并写出来和音频跟踪在模式流。这是一个最低可行的测试,使音频跟踪流模式工作。

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

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

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

    当单步执行代码时,我端上的所有东西都是我预期的那样,所以我在想,不知怎么的,我把音频曲目配置错了。

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

    我正在使用的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();
    }
    
    0 回复  |  直到 5 年前
        1
  •  1
  •   greeble31    5 年前

    根据 documentation ,

    对于可移植性,应用程序应将数据路径设置为写入数据所允许的最大值,直到Read()方法返回一个简短的传输计数。这允许play()立即开始,并减少不足的机会。

    如果严格阅读,可能会发现这与之前的说法相矛盾:

    …您可以在调用play()之前,通过写入 高达 bufferSizeInBytes ...

    (强调我的意思),但目的很明确:你应该先写一篇短文。

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

    作为旁白:你应该做好准备 getPlaybackHeadPosition() 只做大幅度的改变(如果我没记错的话 getMinBufferSize()/2 ). 这是系统可用的最大分辨率; onMarkerReached() 不能用来做得更好。