2012-09-10 9 views
5

W weekend trafiłem na przeszkodę, ucząc się programowania syntezatora audio na iOS. Rozwijam się na iOS od kilku lat, ale dopiero zaczynam zajmować się syntezą dźwięku. Właśnie teraz programuję aplikacje demo, aby pomóc mi uczyć się tych pojęć. Obecnie mogę bez problemu tworzyć i układać fale sinusoidalne w rendererze odtwarzania dla jednostek audio. Ale chcę zrozumieć, co dzieje się w rendererze, więc mogę renderować 2 oddzielne fale sinusoidalne w każdym kanale lewym i prawym. Obecnie zakładam, że w moim Init sekcji audio musiałbym wprowadzić następujące zmiany:Jednostka audio iOS - tworzenie fal sinusoidalnych

Od:

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; 

Do:

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; 

Ale renderer jest nieco grecki do mnie . Pracowałem nad dowolnym samouczkiem lub kodem przykładowym, który mogę znaleźć. Mogę sprawić, żeby rzeczy działały dla danego kontekstu sygnału mono, ale nie mogę sprawić, żeby renderer generował sygnały stereo. Wszystko, czego chcę, to jedna wyraźna częstotliwość w lewym kanale i inna częstotliwość w prawym kanale - ale szczerze mówiąc nie rozumiem renderera na tyle, aby to działało. Próbowałem funkcji memcpy w mBuffers [0] i mbuffers [1], ale to powoduje awarię aplikacji. Mój render jest poniżej (obecnie zawiera skumulowane fale sinusoidalne, ale dla przykładu stereo mogę użyć jednej fali ustawionej częstotliwości w każdym kanale).

#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; 

} 

Z góry dziękuję za pomoc!

+0

Jeśli format jest przeplatany (zgodnie z sugestią ASBD), próbki będą znajdować się w jednym buforze naprzemiennie w lewo i w prawo: "LRLRLRLR". Jednak w wywołaniu zwrotnym nietypowy byłby format z przeplotem - zazwyczaj format jest formatem kanonicznym dla systemu operacyjnego. – sbooth

+0

Dzięki. Właśnie to wymyśliłem kilka minut temu. Jest jednak przeplatany, jak powiedziałeś. Musiałem wymyślić, jak przechodzić przez wywołanie zwrotne, aby renderować fale sinusoidalne w odrębnych kanałach L & R. Dzięki za pomoc! – jwkerr

+0

Witaj jwkerr, Miałem nadzieję, że mogę Ci pomóc w opublikowaniu funkcji renderowania. Próbowałem uruchomić renderowanie stereo przez jakiś czas i nie mogę tego całkiem zrozumieć. Dzięki – VaporwareWolf

Odpowiedz

3

OP najwyraźniej rozwiązał swój problem, ale myślałem, że wysłanie jednoznacznej odpowiedzi byłoby pomocne dla reszty z nas.

Miałem to samo pytanie o to, czy chcę kierować dźwięki do lewego i prawego kanału niezależnie. Najłatwiej opisać w kategoriach standardowego teraz Matta Gallaghera: An iOS tone generator (an introduction to AudioUnits).

Pierwsza zmiana aby jest ustawienie (po @jwkerr) streamFormat.mChannelsPerFrame = 2; (zamiast streamFormat.mChannelsPerFrame = 1;) W sposobie createToneUnit. Gdy to zrobisz i masz dwa kanały/buforów w każdej ramce, trzeba wypełnić bufory lewy i prawy niezależnie w 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; 
} 

Oczywiście channelLR[2] jest bool macierz, której elementy można ustawić, aby wskazać, czy odpowiedni kanał jest słyszalny. Zauważ, że program musi jawnie ustawić ramki cichych kanałów na zero, w przeciwnym razie wydobywasz śmieszne dźwięki.

+0

channelLR potrzebuje więcej wyjaśnień. –

+0

Edytowane. Czy to pomocne? – JohnK

+0

kiedy ustawiasz channelLR? Próbowałem twojego przykładu, ale jest statyczne –

Powiązane problemy