2017-06-04 140 views
1

我想播放短於1秒的生成聲音。但是,AudioTrack的minBufferSize總是似乎是1秒或更長。在某些設備上,我可以將bufferSize設置爲小於使用AudioTrack.getMinBufferSize評估的值,但是這不適用於所有設備。我想知道是否有可能爲AudioTrack生成較短的聲音。我目前使用此代碼(它包含了一些平滑,因爲我得到不斷有新的頻率的頻道):播放短暫生成的聲音

int buffSize = AudioTrack.getMinBufferSize(sampleRate, 
              AudioFormat.CHANNEL_OUT_MONO, 
              AudioFormat.ENCODING_PCM_16BIT); 
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, 
             AudioFormat.CHANNEL_OUT_MONO, 
             AudioFormat.ENCODING_PCM_16BIT, buffSize, 
             AudioTrack.MODE_STREAM); 
short samples[] = new short[buffSize]; 
int amp = 10000; 
double twopi = 8. * Math.atan(1.); 
double phase = 0.0; 
audioTrack.play(); 
double currentFrequency = getFrequency(); 
double smoothing = 300; 
double deltaTime = buffSize/500; 
while (playing && PreferenceManager.getDefaultSharedPreferences(
     MainActivity.this).getBoolean("effect_switch", true)) 
{ 
    double newFrequency = getFrequency(); 
    for (int i = 0; i < buffSize; i++) 
    { 
     currentFrequency += deltaTime * (newFrequency - currentFrequency)/smoothing; 
     samples[i] = (short) (amp * Math.sin(phase)); 
     phase += twopi * currentFrequency/sampleRate; 
    } 
    audioTrack.write(samples, 0, buffSize); 
} 
audioTrack.stop(); 
audioTrack.release(); 

其實,我想要的聲音更頻繁地更新,這是我需要的原因更短的樣本。

+0

而不是集中於更短的聲音剪輯叫,你就不能同時運行多個audiotracks? –

+0

我一直在玩這個,我得到了一個約80ms的minBufferSize(我相信)。對於8kHz採樣(普通電話音頻採樣率)爲1280字節,對於44.1kHz採樣爲7056字節(最大)。我只嘗試過一種設備。你有幾個設備的最低限度嗎?我使用相同的參數來獲得'minBufferSize','CHANNEL_OUT_MONO'和'ENCODING_PCM_16BIT'。 – Gary99

+0

@ Gary99我在兩個設備上測試過它 – MetaColon

回答

1

我想我有一個解決方案給你。由於我的最小緩衝區似乎比1秒小得多,所以我通過加載緩衝區5秒的數據來模擬你的問題,但是隻能播放0.5秒的數據,緊接着是另一個頻率。這種音調我也創造了5秒的數據,但只播放0.5秒&重複這幾個音調。這一切都適合我。另外,由於我將這個問題塞進了當前正在進行的項目中,所以我很難僅僅剪切和粘貼我的代碼。雖然我已經測試了我的解決方案,但我在這裏發佈的內容並未完全按照書面進行測試。其中一些是剪切&粘貼,一些僞代碼。

關鍵功能是使用OnPlaybackPositionUpdateListener。

private AudioTrack.OnPlaybackPositionUpdateListener audioTrackListener = new AudioTrack.OnPlaybackPositionUpdateListener() { 
    @Override 
    public void onMarkerReached(AudioTrack audioTrack) { 
     int marker = audioTrack.getNotificationMarkerPosition(); 
     // I just used 8 tones of 0.5 sec each to determine when to stop but you could make 
     //  the condition based on a button click or whatever is best for you 
     if(marker < MAX_FRAME_POSITION) { 
      audioTrack.pause(); 
      newSamples(); 
      audioTrack.play(); 
     } else { 
      audioTrack.stop(); 
     } 
     audioTrack.setNotificationMarkerPosition(marker + FRAME_MARKER); 
     Log.d(TAG, "MarkerReached"); 
    } 

    @Override 
    public void onPeriodicNotification(AudioTrack audioTrack) { 
     int position = audioTrack.getPlaybackHeadPosition(); 
     if(position < MAX_FRAME_POSITION) { 
      audioTrack.pause(); 
      newSamples(); 
      audioTrack.play(); 
     } else { 
      audioTrack.stop(); 
     } 
     Log.d(TAG, "PeriodNotification"); 
    } 
}; 

然後

audioTrack.setPlaybackPositionUpdateListener(AudioTrackListener); 

我使用的標誌物(其被反覆重新初始化)對我的測試...

audioTrack.setNotificationMarkerPosition(MARKER_FRAMES);  

,但你應該能夠使用定期通知。

audioTrack.setPositionNotificationPeriod(PERIODIC_FRAMES); 

而且newSamples()方法從聽者

public void newSamples() { 
    /* 
    * generate buffer, I'm doing similar to you, without the smoothing 
    */ 

    // AudioTrack write is a blocking operation so I've moved it off to it's own Thread. 
    //  Could also be done with an AsyncTask. 
    Thread thread = new Thread(writeSamples); 
    thread.start(); 
} 

private Runnable writeSamples = new Runnable() { 
    @Override 
    public void run() { 
     audioTrack.write(samples, 0, buffSize); 
    } 
}; 
+0

我稍後再試。 – MetaColon