使用AudioTrack播放循环audio

我有一个function,我想过渡到使用Android的AudioTrack而不是MediaPlayer ,由于与MediaPlayer的一些众所周知的错误,如循环轨道之间出现的小差距。

我已经被推荐使用AudioTrack但没有发现它的许多使用的例子。 我确实发现了一个关于AudioTrack的问题,并使用了一些代码来破解一些东西:

 public class TestActivity extends Activity implements Runnable { Button playButton; byte[] byteData = null; int bufSize; AudioTrack myAT = null; Thread playThread = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); playButton = (Button) findViewById(R.id.testButton); InputStream inputStream = getResources().openRawResource(R.raw.whitenoise_wav); try { byteData = new byte[ inputStream.available()]; } catch (IOException e) { e.printStackTrace(); } try { inputStream.read(byteData); inputStream.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } initialize(); playButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { playThread.start(); } }); } void initialize() { bufSize = android.media.AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_CONFIGURATION_STEREO, AudioFormat.ENCODING_PCM_16BIT); myAT = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_STEREO, AudioFormat.ENCODING_PCM_16BIT, bufSize, AudioTrack.MODE_STREAM); myAT.setVolume(.2f); playThread = new Thread(this); } public void run() { if (myAT != null) { myAT.play(); myAT.setLoopPoints(0, byteData.length, 6); myAT.write(byteData, 0, byteData.length); } } } 

所以这似乎播放整个音轨(〜1:00分钟),然后停止。 现在这里的最终目标是两个同时播放和循环两个独立的音轨。 我目前在/res/raw/目录中有audio轨道,但如果这样会更好,我可以将它们移动到一个简单的assets文件夹中。 我当前的AudioTrack实现AudioTrack正确? 如果是这样,我将如何得到它循环?

总而言之,如何使用AudioTrack播放没有间隙的循环audio?

我们欢迎有关获取循环audio(如第三方库)的其他方法的build议。

Solutions Collecting From Web of "使用AudioTrack播放循环audio"

您无法使用AudioTrack.MODE_STREAMconfiguration的AudioTrack进行AudioTrack.MODE_STREAM 。 如果使用MODE_STREAM AudioTrack需要连续填充新的样本。

但是你可以用AudioTrack.MODE_STATICconfiguration它,并传递整个缓冲区(我的意思是:如果你需要混合两个样本,你必须通过混合样本)。

setLoopPoints:设置循环点和循环计数。 循环可以是无限的。 与setPlaybackHeadPosition类似,必须停止或暂停轨迹以更改循环点,并且必须使用MODE_STATIC模式。

请注意,AudioTrack播放原始PCM样本,不支持WAV,MP3或其他容器。

看这个例子,似乎是一个类似的问题,解决了不断喂养的AudioTrack

 class ToneGenerator { int sampleRate = 8000; double sample[] = null; byte generatedSnd[] = null; int m_ifreq = 400; Thread m_PlayThread = null; boolean m_bStop = false; AudioTrack m_audioTrack = null; int m_play_length = 1000;//in seconds static public void PlayTone(int freq, int play_length) { ToneGenerator player = new ToneGenerator(); player.m_ifreq = freq; player.m_play_length = play_length; player.play(); } synchronized void stop() { m_bStop = true; if (m_PlayThread != null) { try { m_PlayThread.interrupt(); m_PlayThread.join(); m_PlayThread = null; } catch (Exception e) { } } if (m_audioTrack != null) { m_audioTrack.stop(); m_audioTrack.release(); m_audioTrack = null; } } synchronized void play() { m_bStop = false; m_PlayThread = new Thread() { public void run() { try { int iToneStep = 0; m_audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, 2 * sampleRate, AudioTrack.MODE_STREAM); while (!m_bStop && m_play_length-- > 0) { genTone(iToneStep++); m_audioTrack.write(generatedSnd, 0, generatedSnd.length); if (iToneStep == 1) { m_audioTrack.play(); } } } catch (Exception e) { Log.e("Tone", e.toString()); } catch (OutOfMemoryError e) { Log.e("Tone", e.toString()); } } }; m_PlayThread.start(); } //Generate tone data for 1 seconds synchronized void genTone(int iStep) { sample = new double[sampleRate]; for (int i = 0; i < sampleRate; ++i) { sample[i] = Math.sin(2 * Math.PI * (i + iStep * sampleRate) / (sampleRate / m_ifreq)); } // convert to 16 bit pcm sound array // assumes the sample buffer is normalised. generatedSnd = new byte[2 * sampleRate]; int idx = 0; for (final double dVal : sample) { // scale to maximum amplitude final short val = (short) ((dVal * 32767)); // in 16 bit wav PCM, first byte is the low order byte generatedSnd[idx++] = (byte) (val & 0x00ff); generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8); } } } 

您传递的是stream的字节长度。 setLoopPoints()需要stream中audio块的数量。 请参阅文档中的getBufferSizeInFrames()。

然而。 我告诫你,大多数特定的audio格式不是基于块的; 他们是基于样本的。 MP3是唯一的例外; 它在1152字节边界上alignment。 如果源内容是MP3并且没有明确地被编写为块alignment,则无缝循环是不可能的。 您似乎正在使用“whitenoise.wav”作为您的audio源。 除非此文件的长度与块大小明确alignment,否则循环时可能会出现audio失真。

解决方法是在循环时交叉淡入淡出开始帧和最后一帧,并自己处理所有缓冲区,但是您必须编写自己的代码才能执行此操作。