使用 AudioTrack 提高音符合成的性能
Posted
技术标签:
【中文标题】使用 AudioTrack 提高音符合成的性能【英文标题】:Improve performance of musical note synthesis using AudioTrack 【发布时间】:2015-07-24 17:22:35 【问题描述】:我正在尝试构建一个合成和播放音符的安卓应用程序。我正在使用 AudioTrack。由于生成音频信息的延迟,我的程序似乎无法足够快地填充轨道缓冲区。我正在从音符的傅立叶系数生成音频信息。因此,在每次写入之前,程序必须创建和采样 2048 个正弦波,每个正弦波的样本大小为 2400,采样率为 44100!
所以我听到的是断断续续的哔哔声,而不是连续的声音。 Logcat 在哔声之间给出以下警告:
W/AudioTrack﹕ obtainBuffer() track 0x177ff80 disabled, restarting
我的代码如下。谁能确定我可以优化代码的方法?
package com.alantaar.alantaar;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import java.util.Arrays;
public class AudioGenerator
private AudioTrack audio;
private double[] coefficients;
private int bufferSize;
private int sampleRate;
private double[] phases;
private double frequency;
public AudioGenerator(double[] coefficients, double frequency, int sampleRate, int bufferSize)
int minBufferSize = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT);
this.bufferSize = bufferSize > minBufferSize ? bufferSize : minBufferSize;
audio = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, this.bufferSize, AudioTrack.MODE_STREAM);
this.coefficients = coefficients;
this.sampleRate = sampleRate;
this.frequency = frequency;
this.phases = new double[coefficients.length];
Arrays.fill(this.phases, 0.0);
Log.i("length", Integer.toString(coefficients.length));
audio.play();
public void playNote()
while(true)
short[] waveSample = sampleWave();
audio.write(waveSample, 0, waveSample.length);
private short[] sampleWave()
short [] waveSample = new short[bufferSize];
Arrays.fill(waveSample, (short)0);
for(int i = 0; i < coefficients.length; i++)
double coefficient = coefficients[i];
short[] sineSamples = sampleSineWave(coefficient, i);
for(int j = 0; j < waveSample.length; j++)
waveSample[j] += sineSamples[j];
return waveSample;
private short [] sampleSineWave(double coefficient, int index)
double freq = frequency * index;
short [] samples = new short[bufferSize];
for(int i = 0; i < bufferSize; i++)
samples[i] = (short) (coefficient * Short.MAX_VALUE * Math.cos(phases[index]));
phases[index] += 2 * Math.PI * freq/sampleRate;
return samples;
public void pauseNote()
audio.stop();
public void stopNote()
audio.release();
【问题讨论】:
【参考方案1】:以下是一些突然出现在我身上的事情:
-
将计算移出内部循环。例如,
2*pi*freq/sampleRate
可以在for
之前完成。与coefficient * Short.MAX_VALUE
相同。
我敢打赌,您的笔记实际上并不包含 2048 个不同的有意义的正弦分量。一种选择是在系数低于某个阈值时不生成音调。
使用 FFT!您正在实现一个 O(n^2) 的 DFT。 FFT 的运行时间为 O(n log n)。
利用audio.write
阻塞的时间来生成更多样本。我不知道AudioTrack
的流模式的行为,但很有可能您的程序大部分时间都在阻塞调用中。
【讨论】:
嗨!感谢您的提示!我实施了 1. 和 2. 结果是压倒性的。我想实现 FFT,但我一点也不熟悉。这个理论超出了我的想象。你知道我可以使用的算法吗?以上是关于使用 AudioTrack 提高音符合成的性能的主要内容,如果未能解决你的问题,请参考以下文章
Android 音频系统:从 AudioTrack 到 AudioFlinger