使用 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 提高音符合成的性能的主要内容,如果未能解决你的问题,请参考以下文章

Win 10 应用开发MIDI 音乐合成——音符消息篇

Android 音频系统:从 AudioTrack 到 AudioFlinger

Android 音频系统:从 AudioTrack 到 AudioFlinger

如何使用流体合成器从 C# 中的声音字体生成声音

Android音频系统

在c中生成一个.au文件