2012-09-10 54 views
5

在上週末我碰到了一個絆腳石,學習如何在iOS上編寫音頻合成。我在iOS上開發了好幾年,但我只是進入音頻合成方面。現在,我只是編寫演示程序來幫助我學習這些概念。我目前已經能夠在沒有問題的音頻單元的播放渲染器中構建和堆疊正弦波。但是,我想了解渲染器中發生了什麼,所以我可以在每個左右聲道中渲染2個獨立的正弦波。目前,我認爲在我的初始化音頻部分,我需要做以下修改:iOS音頻單元 - 創建立體聲正弦波

來源:

AudioStreamBasicDescription audioFormat; 
    audioFormat.mSampleRate = kSampleRate; 
    audioFormat.mFormatID = kAudioFormatLinearPCM; 
    audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; 
    audioFormat.mFramesPerPacket = 1; 
    audioFormat.mChannelsPerFrame = 1; 
    audioFormat.mBitsPerChannel = 16; 
    audioFormat.mBytesPerPacket = 2; 
    audioFormat.mBytesPerFrame = 2; 

要:

AudioStreamBasicDescription audioFormat; 
    audioFormat.mSampleRate = kSampleRate; 
    audioFormat.mFormatID = kAudioFormatLinearPCM; 
    audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; 
    audioFormat.mFramesPerPacket = 1; 
    audioFormat.mChannelsPerFrame = 2; 
    audioFormat.mBitsPerChannel = 16; 
    audioFormat.mBytesPerPacket = 4; 
    audioFormat.mBytesPerFrame = 4; 

但是,渲染器有點希給我。我一直在研究任何教程或示例代碼,我可以找到。我可以使單聲道信號的上下文工作,但是我無法讓渲染器生成立體聲信號。我想要的只是左聲道的一個不同頻率和右聲道的不同頻率 - 但我真的不明白渲染器足以讓它工作。我試圖將memcpy函數放入mBuffers [0]和mbuffers [1]中,但會使應用程序崩潰。我的渲染在下面(它目前包含疊加的正弦波,但對於立體聲示例,我可以在每個通道中使用一個設定頻率的波形)。

#define kOutputBus 0 
#define kSampleRate 44100 
//44100.0f 
#define kWaveform (M_PI * 2.0f/kSampleRate) 

OSStatus playbackCallback(void *inRefCon, 
          AudioUnitRenderActionFlags *ioActionFlags, 
          const AudioTimeStamp *inTimeStamp, 
          UInt32 inBusNumber, 
          UInt32 inNumberFrames, 
          AudioBufferList *ioData) { 

     HomeViewController *me = (HomeViewController *)inRefCon; 

    static int phase = 1; 
    static int phase1 = 1; 

    for(UInt32 i = 0; i < ioData->mNumberBuffers; i++) { 

     int samples = ioData->mBuffers[i].mDataByteSize/sizeof(SInt16); 

     SInt16 values[samples]; 

     float waves; 
     float volume=.5; 
     float wave1; 

     for(int j = 0; j < samples; j++) { 


      waves = 0; 
      wave1 = 0; 

      MyManager *sharedManager = [MyManager sharedManager]; 


      wave1 = sin(kWaveform * sharedManager.globalFr1 * phase1)*sharedManager.globalVol1; 
      if (0.000001f > wave1) { 
       [me setFr1:sharedManager.globalFr1]; 
       phase1 = 0; 
       //NSLog(@"switch"); 
      } 

      waves += wave1; 
      waves += sin(kWaveform * sharedManager.globalFr2 * phase)*sharedManager.globalVol2; 
      waves += sin(kWaveform * sharedManager.globalFr3 * phase)*sharedManager.globalVol3; 
      waves += sin(kWaveform * sharedManager.globalFr4 * phase)*sharedManager.globalVol4; 
      waves += sin(kWaveform * sharedManager.globalFr5 * phase)*sharedManager.globalVol5; 
      waves += sin(kWaveform * sharedManager.globalFr6 * phase)*sharedManager.globalVol6; 
      waves += sin(kWaveform * sharedManager.globalFr7 * phase)*sharedManager.globalVol7; 
      waves += sin(kWaveform * sharedManager.globalFr8 * phase)*sharedManager.globalVol8; 
      waves += sin(kWaveform * sharedManager.globalFr9 * phase)*sharedManager.globalVol9; 
      waves *= 32767/9; // <--------- make sure to divide by how many waves you're stacking 

      values[j] = (SInt16)waves; 
      values[j] += values[j]<<16; 

      phase++; 
      phase1++; 

     } 

     memcpy(ioData->mBuffers[i].mData, values, samples * sizeof(SInt16)); 

    } 


    return noErr; 

} 

在此先感謝您的幫助!

+0

如果格式是交錯的(就像你的ASBD所建議的那樣),那麼樣本將在一個緩衝區左右交替變化:'LRLRLRLR'。然而,在回調中使用交織格式通常是不尋常的 - 通常格式是操作系統的規範格式。 – sbooth

+0

謝謝。其實我只是在幾分鐘前就把它全部弄清楚了。儘管如此,它是交錯的。我必須弄清楚如何循環回調才能在不同的L&R通道中渲染正弦波。感謝您的幫助! – jwkerr

+0

你好jwkerr,我希望我能說說你發佈你的渲染函數。我一直試圖讓立體渲染工作一段時間,並不能完全得到它。謝謝 – VaporwareWolf

回答

3

OP似乎已經解決了他的問題,但我認爲發佈明確的答案會對我們其他人有所幫助。

我有同樣的問題想要獨立地將音調指向左聲道和右聲道。根據馬特加拉格爾的現行標準An iOS tone generator (an introduction to AudioUnits)來描述最簡單。

要做的第一項更改是在createToneUnit方法中設置(遵循@jwkerr)streamFormat.mChannelsPerFrame = 2;(而不是streamFormat.mChannelsPerFrame = 1;)。一旦做到這一點,你有兩個通道/緩衝器中的每一幀,你需要獨立填寫左右的緩衝在RenderTone()

// Set the left and right buffers independently 
Float32 tmp; 
Float32 *buffer0 = (Float32 *)ioData->mBuffers[0].mData; 
Float32 *buffer1 = (Float32 *)ioData->mBuffers[1].mData; 

// Generate the samples 
for (UInt32 frame = 0; frame < inNumberFrames; frame++) { 
    tmp = sin(theta) * amplitude; 

    if (channelLR[0]) buffer0[frame] = tmp; else buffer0[frame] = 0; 
    if (channelLR[1]) buffer1[frame] = tmp; else buffer1[frame] = 0; 

    theta += theta_increment; 
    if (theta > 2.0 * M_PI) theta -= 2.0 * M_PI; 
} 

當然channelLR[2]是一個bool數組,其元素設置指示是否相應的頻道是可聽的。請注意,該程序需要明確地將無聲通道的幀設置爲零,否則會得到一些有趣的音調。

+0

channelLR需要更多解釋。 –

+0

已編輯。這有幫助嗎? – JohnK

+0

你什麼時候設置channelLR?我試過你的例子,但是它是靜態的 –