2011-07-26 39 views
1

我的一個應用程序具有簡單的節拍器式功能,可以每分鐘(bpm)播放指定次數的咔嗒聲。我正在通過啓動一個NSTimer來實現這一點,從指定的bpm計算出一個間隔,該間隔調用一個播放聲音的方法。爲什麼我的音頻沒有按時播放?

如果我把一個NSLog行放入播放方法,我可以看到NSTimer準確地發射到大約1毫秒。但是,如果我將聲音輸出記錄到音頻編輯器中,然後測量點擊之間的時間間隔,我可以看到它們的間距不均勻。例如,每分鐘150次,計時器每400毫秒觸發一次。但是大部分聲音在395毫秒後播放,在418毫秒後播放每三分之一或四分之一聲音。

所以聲音不是均勻延遲,而是它們遵循更短和更長時間間隔的模式。看起來好像iOS的聲音定時分辨率較低,並將每個聲音事件四捨五入到最近的可用點,根據需要向上或向下舍入以保持整體軌道。

我已經嘗試過使用系統聲音,AVAudioPlayer和OpenAL,並且使用這三種方法都得到了完全相同的結果。使用每種方法時,我會在視圖加載時執行所有設置,因此每次播放聲音時我只需要播放它。使用AVAudioPlayer時,我在每次播放聲音後都嘗試使用第二個定時器調用prepareToPlay,因此它被初始化並準備下次使用,但得到相同的結果。

這裏的(改編自this tutorial)設立在viewDidLoad中的OpenAL的聲音代碼:

// set up the context and device 
ALCcontext *context; 
ALCdevice *device; 
OSStatus result; 
device = alcOpenDevice(NULL); // select the "preferred device" 
if (device) { 
    context = alcCreateContext(device, NULL); // use the device to make a context 
    alcMakeContextCurrent(context); // set the context to the currently active one 
} 

// open the sound file 
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:@"TempoClick" ofType:@"caf"]; 
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath]; 
AudioFileID fileID; 
result = AudioFileOpenURL((CFURLRef)soundFileURL, kAudioFileReadPermission, 0, &fileID); 
if (result != 0) DLog(@"cannot open file %@: %ld", soundFilePath, result); 

// get the size of the file data 
UInt32 fileSize = 0; 
UInt32 propSize = sizeof(UInt64); 
result = AudioFileGetProperty(fileID, kAudioFilePropertyAudioDataByteCount, &propSize, &fileSize); 
if (result != 0) DLog(@"cannot find file size: %ld", result); 
DLog(@"file size: %li", fileSize); 

// copy the data into a buffer, then close the file 
unsigned char *outData = malloc(fileSize); 
AudioFileOpenURL((CFURLRef)soundFileURL, kAudioFileReadPermission, 0, &fileID); // we get a "file is not open" error on the next line if we don't open this again 
result = AudioFileReadBytes(fileID, false, 0, &fileSize, outData); 
if (result != 0) NSLog(@"cannot load data: %ld", result); 
AudioFileClose(fileID); 
alGenBuffers(1, &tempoSoundBuffer); 
alBufferData(self.tempoSoundBuffer, AL_FORMAT_MONO16, outData, fileSize, 44100); 
free(outData); 
outData = NULL; 

// connect the buffer to the source and set some preferences 
alGenSources(1, &tempoSoundSource); 
alSourcei(tempoSoundSource, AL_BUFFER, tempoSoundBuffer); 
alSourcef(tempoSoundSource, AL_PITCH, 1.0f); 
alSourcef(tempoSoundSource, AL_GAIN, 1.0f); 
alSourcei(tempoSoundSource, AL_LOOPING, AL_FALSE); 

然後在播放方法我只要致電:

alSourcePlay(self.tempoSoundSource); 

誰能解釋一下什麼是在這裏發生,以及我如何解決它?

更新1:

我有另一個項目,播放音頻單元簡短的聲音,從而快速測試我添加了一個定時器,該項目發揮出自己的咔嚓聲每隔400毫秒。在這種情況下,時機幾乎完美。所以,NSTimer似乎很好,但系統聲音,AVAudioPlayer和OpenAL的播放比音頻單元的準確性要低。

更新2:

我只是重做我的項目使用的音頻單元,現在音頻更準確地回放了。它在任一方向上偶爾漂移最多四毫秒,但這比其他音頻方法要好。我仍然很好奇爲什麼其他方法都顯示短,短,短,長的時間間隔模式 - 就像音頻播放時間被向上或向下四捨五入以映射某種幀速率 - 所以我會任何可以解釋和/或爲其他音頻方法提供解決方法的人都可以打開此問題。

+0

不知道,但可能是你的NSLog造成滯後,如果它存在 – Ahmed

+0

@Ahmed:這是一個很好的想法,但我剛剛評論了NSLog並得到了相同的結果。 – arlomedia

+0

[如何在iPhone上編程實時精確音頻音序器?](http://stackoverflow.com/questions/907137/how-to-program-a-real-time-accurate-audio-sequencer -on-the-iphone) – benzado

回答

1

好吧,我已經想通了。真正的原因音頻單位工作過,比其他音頻方法更好的是,我的聲音單元類,我是從其他項目中調整,在音頻會議設置緩存時間屬性,像這樣:

Float32 preferredBufferSize = .001; 
UInt32 size = sizeof(preferredBufferSize); 
AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, size, &preferredBufferSize); 

當我將此代碼添加到OpenAL版本,甚至添加到AVAudioPlayer版本中,我可以在幾毫秒內獲得準確性,與音頻單元相同。 (然而,系統聲音仍然不是很準確。)我可以通過增加緩衝區大小和觀看播放間隔的準確性來驗證連接。

當然,我花了整整一天的時間調整我的項目以使用音頻單元 - 調整它在C++下編譯,測試中斷處理程序等,我只想到了這一點。我希望這可以幫助別人避免同樣的麻煩。

1

NSTimer不能保證你的方法何時會被實際觸發。

此處瞭解詳情:How to program a real-time accurate audio sequencer on the iphone?

關於您的編輯:

AVAudioPlayer需要一些時間來初始化。如果您調用prepareToPlay,它將初始化自己,以便在調用播放時立即播放當前加載的聲音。播放停止後,它會自行初始化,因此您需要再次調用prepareToPlay以重新初始化。最好將這個類用於stream-y回放,而不是離散的聲音回放。

使用OpenAL,一旦你加載了緩衝區,將它附加到源代碼並播放它應該不會造成任何延遲。

您可以將音頻單元代碼封裝到.mm文件中,然後從.m模塊調用它,而無需將它們編譯爲C++。

+1

根據我的NSLogs上的時間戳,該方法被解僱到最近的毫秒,這足夠接近我的目的。上面的鏈接着重於製作更精確的計時器,但在我看來,計時器不是問題。 – arlomedia

+0

「您可以將音頻單元代碼封裝到.mm文件中,然後從.m模塊中調用它,而無需將它們編譯爲C++。」你確定嗎?我無法讓它工作。如果你可以澄清,我已經發布了一個關於此問題的單獨問題:http://stackoverflow.com/questions/6944465/how-can-i-use-a-few-c-files-in-my-project-without -compiling-每個文件,爲-C – arlomedia

相關問題