使用opensl es和android ndk实时播放音频

时间:2013-01-09 16:13:18

标签: android android-ndk opensl

我想要做的是录制语音,处理它就像为录制的音频添加一些效果,然后使用opensl尽快重播。我在网上找到关于做这样的事情的信息很困难,所以我实际上有几个问题。

我看过android原生音频样本,这本书开始使用android ndk,这让我开始使用opensl,但是他们并没有真正帮助我理解录音效果。

1)首先,要立即回放录制的音频,我是否能够从播放音频的线程中读取缓冲区,同时录制线程正在写入缓冲区?我知道有人可能会对这个想法发疯,因为同时通过两个不同的线程访问内存中的某个对象的想法可能会导致问题,但是如果记录线程确保始终在内存之前写入内存播放音频线程,那可能是对的吗?

2)或者为了实时回放,我将使用具有两个或三个非常小的缓冲区的缓冲区队列,以及每次填充一个时调用的回调,然后在填充下一个缓冲区时播放该缓冲区通过录制音频线程?但是我正在阅读并且有人说回调并不总是被调用(我发现的最有用的链接是:https://groups.google.com/forum/#!msg/android-ndk/hLSygQrmcPI/qtwB76JNa_EJ)。此外,这意味着录制的音频和播放的音频之间的时间差将是缓冲区的大小加上回调使记录对象知道使用下一个缓冲区开始记录所花费的时间。我认为录音机停止录音和再开始录音之间会有间隙。

3)android记录缓冲区队列也是我理解的问题。我是否必须在android中使用缓冲队列来录制音频?或者是否可以在不使用缓冲队列的情况下直接记录到缓冲区?我遇到了这个问题,因为SLAndroidSimpleBufferQueueItf的明确方法实际上似乎不起作用。显然它是一个错误。我似乎无法在已填充数据后记录缓冲区。由于clear方法似乎不起作用,如何告诉记录器队列中的哪个缓冲区要记录?

我知道这有点问题,而且我没有说过我尝试过的所有内容,但我希望有人对此有一些经验,可以为我和其他有问题的人提供一些帮助。使用opensl实时播放录制的音频。

2 个答案:

答案 0 :(得分:5)

我发现了我正在寻找的东西。实时播放录制音频的一种方法是使用称为循环缓冲器(或圆形缓冲器)的东西,其中基本上将录制的音频读入其中,而播放的音频读取所有录制的音频正在写入的内容。

http://audioprograming.wordpress.com/

Android中的OpenSL ES仍然不能完全支持“低延迟”,因为仍有轻微的延迟,但它仍然可以正常工作,它听起来更像是一个轻微的回声

答案 1 :(得分:1)

我意识到这已得到回答,这是一个老问题。但是,为了澄清,在我的回放队列中,我不使用Clear()。我只是停止播放,然后在循环中使用GetState(state)等待队列耗尽,如下所示:

SLresult res;
SLAndroidSimpleBufferQueueState state = {0};
res = (*playbackBufferQueue)->GetState(playbackBufferQueue, &state);

while (state.count && (res == SL_RESULT_SUCCESS))  
{  
    prevState = state;
    res = (*m_playbackBufferQueue)->GetState(m_playbackBufferQueue, &state); 
    // Otherwise this will peg the CPU
    sleep(1);
}

为了支持低延迟,您可以使用从Android查询的“最佳”速率和样本大小。你能用原生API调用吗?当然不是,不要傻。在Java中执行并将其传递回native:

int getBestPlaybackRate() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        return Integer.parseInt(audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE));
    }
    return 0;
}
int getBestBufferSize() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        return Integer.parseInt(audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER));
    }
    return 0;
}