8

我已经构建了一个简单的音乐音序器 Android 应用程序,可以播放多个音频文件。

最初我使用 SoundPool 播放 mp3 文件,它在 2.3.4 上与旧的 HTC Droid Incredible 完美配合。然后我在运行 4.3 的 Galaxy Nexus 上对其进行了测试,结果非常糟糕。整个地方的音频时间都出现了故障/咔嗒声/爆裂声。

所以我花了几天时间使用 AudioTrack 制作了一个播放器,包括一个 mp3 解码器,让它在 Galaxy 和 HTC 上都能完美运行。现在我刚刚在 Nexus 4(运行 4.3)上对其进行了测试,性能很糟糕——时间到处都是。SoundPool 甚至可以在此设备上提供更好的性能。

我真的很沮丧,不知道该怎么做才能完成我的应用程序,所以如果有人能帮助我,我将不胜感激。我在下面放了一些音频播放器的代码示例。我已经尝试了我能想到的所有方法,包括更改缓冲区大小、使用AudioTrack.MODE_STATIC等。新的 Google 设备具有低延迟音频,所以很奇怪在我的旧机器人上一切工作得更好!

提前致谢

/**
* Play note
*/
public void playNote(String note, float vol)
{
    PlayThread oldThread = threadMap.get(note);
    if(oldThread != null) {
        //Cancel timer
        if(oldThread.timer != null) {
            oldThread.timer.cancel();
            oldThread.timer.purge();
            oldThread.timer = null;
        }
        //Stop
        oldThread.requestStop();
        threadMap.remove(note);
    }

    //Play if within Polyphony
    if(threadMap.size() < POLYPHONY) {
        PlayThread thread = new PlayThread(note, vol);
        thread.start();
        threadMap.put(note, thread);
    }       
}


/**
* Stop note
*/
public void stopNote(String note, int fadeDurationInMs)
{   
    PlayThread thread = threadMap.get(note);
    if(thread != null) {
        thread.fadeOut(fadeDurationInMs);
        threadMap.remove(note);
    }
}


/**
* Stop all
*/
public void stopAllPlaying(int fadeDurationInMs)
{
    for(PlayThread thread : threadMap.values()) {
        if(thread != null) {
            thread.fadeOut(fadeDurationInMs);
        }
    }
    threadMap.clear();
}


/**
* PlayThread
*/
private class PlayThread extends Thread
{
    String note;
    float vol;
    float fadeVol;
    boolean stop;
    AudioTrack audioTrack;
    Timer timer;

    /**
    * Constructor
    */
    public PlayThread(String note, float vol)
    {
        super();
        this.note = note;
        this.vol = vol;
        this.fadeVol = vol;
    }

    /**
    * Run
    */
    public void run()
    {
        try {
            android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);

            //Create buffer
            int bufferSize = AudioTrack.getMinBufferSize(SAMPLE_RATE, CHANNELS, AudioFormat.ENCODING_PCM_16BIT);
            Log.v(Constants.TAG, "min buffersize = " + bufferSize);

            bufferSize = bufferSize * 2;

            byte[] buffer = new byte[bufferSize];

            //AudioTrack
            audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLE_RATE, CHANNELS, AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM);
            audioTrack.setStereoVolume(vol, vol);
            audioTrack.play();

            //Get byte data
            byte[] byteData = sampleMap.get(note);

            //Convert to input stream
            InputStream input = new ByteArrayInputStream(byteData);

            //Write to audioTrack
            int bytesRead = 0;
            while(!stop && (bytesRead = input.read(buffer)) != -1) {
                    audioTrack.write(buffer, 0, bytesRead);
            }

            //When finished...
            audioTrack.stop();
            audioTrack.release();
            input.close();
            killThread(this);
        }
        catch(Exception e) {}
    }

    /**
    * Set volume
    */
    private synchronized void setVol(float newVol)
    {
        audioTrack.setStereoVolume(newVol, newVol);
    }

    /**
    * Update volume
    */
    private synchronized void lowerVol()
    {
        fadeVol -= 0.01;
        if(fadeVol < 0) vol = 0;
        audioTrack.setStereoVolume(fadeVol, fadeVol);
    }

    /**
    * Fade out
    */
    public synchronized void fadeOut(int fadeDurationInMs)
    {
        //Start decreasing volume
        if(fadeDurationInMs > 0) {
            timer = new Timer(true);
            TimerTask timerTask = new TimerTask()
            {
                @Override
                public void run()
                {
                    //If thread killed while running
                    try {
                        //Lower volume
                        lowerVol();
                    }
                    catch (Exception e) {}

                    //Stop when volume reaches 0
                    if(fadeVol <= 0) {
                        if(timer != null) {
                            timer.cancel();
                            timer.purge();
                        }
                        stop = true;
                    }
                }
            };

            //Calculate delay, set to 1 if zero
            int delay = (int) (fadeDurationInMs / (vol * 100)); 
            if(delay == 0) delay = 1;

            timer.schedule(timerTask, delay, delay);
        }
    }

    /**
    * Request stop
    */
    public synchronized void requestStop()
    {
        //Stop click/pop when stopping sample
        setVol(0.01f);
        setVol(0.005f);
        stop = true;
    }

    /**
    * Kill Thread
    */
    private synchronized void killThread(Thread theThread)
    {
        if(theThread != null) {
            theThread = null;
        }
    }

}
4

3 回答 3

10

就像用户 harikris 建议的那样,我强烈建议您使用 OpenSL ES 库将所有​​音频播放和处理代码移至 Android NDK,以获得最佳性能。

据我了解,AudioTrack API 建立在 OpenSL ES 缓冲区队列音频播放器之上。因此,您可能可以通过直接使用 NDK 来提高性能,编写从 Java/Android 层调用的 C 代码来处理声音。

上面提到的本机音频示例包含将向您展示如何直接从 URI 播放声音文件的代码。根据我的经验,这种方法的结果优于静态模式下的 AudioTrack。

Soundpool 通常保留用于可以从内存中播放的非常短的声音,对于您的音序器来说,它不是一个可扩展的解决方案,尤其是在您引入大文件时。

以下是一些对我的应用程序有帮助的链接: - 有关适用于 Android 的 OpenSL ES 的一般信息:http: //mobilepearls.com/labs/native-android-api/opensles/

- 带有一些很棒的示例代码的 Android 音频博客:http: //audioprograming.wordpress.com

编辑:旧的移动珍珠链接似乎已关闭。这是一个有效的:http: //mobilepearls.com/labs/native-android-api/ndk/docs/opensles/index.html

于 2013-09-30T20:58:09.807 回答
5

在 Google I/O 2013 的高性能音频会议上(由 Ian Ni-Lewis 介绍,他对原帖发表了评论。我很惊讶他没有注意到这一点),他们谈到了 Nexus 4。在视频中演示文稿,跳到 27:25。

与许多其他使用原生 44.1kHz 采样率的设备不同,Nexus 4 使用 48kHz。如果您以 44.1kHz 的频率提交数据,则必须通过慢速路径上的重采样器。另一个区别是 Nexus 4 上的缓冲区大小为 240 帧,这可能会导致您的回调有规律的抖动。这一切都在链接的视频中进行了解释。

于 2013-12-12T05:36:24.613 回答
4

我建议您在本机代码中进行所有音频捕获和时间敏感处理,并使用 JNI 在 SDK 和 NDK 组件之间进行交互。

您可以在Android NDK 发行版中找到本机音频项目示例代码,向您展示如何在 C/C++ 中制作音频

于 2013-09-18T16:28:08.360 回答