2012-09-10 5 views
5

Über das Wochenende habe ich einen Stolperstein gefunden und gelernt, wie man Audiosynthese auf iOS programmiert. Ich entwickle seit einigen Jahren auf iOS, aber ich bin gerade dabei, den Aspekt der Audiosynthese zu verstehen. Im Moment programmiere ich nur Demo-Apps, um die Konzepte zu lernen. Ich bin derzeit in der Lage, Sinuswellen in einem Playback-Renderer für Audio Units ohne Probleme zu erstellen und zu stapeln. Aber ich möchte verstehen, was im Renderer vor sich geht, damit ich zwei getrennte Sinuswellen in jedem linken und rechten Kanal rendern kann. Derzeit Ich gehe davon aus, dass in meinem init Audio-Sektion würde ich folgende Änderungen vornehmen müssen:iOS Audio Unit - Erstellen von Stereosinuswellen

Von:

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; 

An:

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; 

Aber ist der Renderer etwas griechisch mir . Ich habe von jedem Tutorial oder Beispielcode gearbeitet, den ich finden kann. Ich kann Dinge für den gegebenen Kontext eines Monosignals arbeiten lassen, aber ich kann den Renderer nicht dazu bringen, Stereosignale zu erzeugen. Alles, was ich will, ist eine unterschiedliche Frequenz in einem linken Kanal und eine andere Frequenz in einem rechten Kanal - aber ich verstehe ehrlich gesagt den Renderer nicht genug, um es zum Laufen zu bringen. Ich habe die Memcpy-Funktion in mBuffers [0] und mbuffers [1] versucht, aber das stürzt die App ab. Mein Rendering ist unten (es enthält derzeit gestapelte Sinuswellen, aber für das Stereobeispiel kann ich nur eine Welle einer bestimmten Frequenz in jedem Kanal verwenden).

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

} 

Vielen Dank im Voraus für jede Hilfe!

+0

Wenn das Format interleaved ist (wie Ihr ASBD vorschlägt), dann sind die Proben in einem Puffer abwechselnd links und rechts: 'LRLRLRLR'. Es wäre jedoch ungewöhnlich, ein verschachteltes Format in einem Callback zu haben - typischerweise ist das Format das kanonische Format für das OS. – sbooth

+0

Danke. Ich habe es vor ein paar Minuten herausgefunden. Es ist verschachtelt, wie Sie gesagt haben, obwohl. Ich musste herausfinden, wie man den Callback durchläuft, um die Sinuswellen in verschiedenen L & R-Kanälen zu rendern. Danke für die Hilfe !! – jwkerr

+0

Hallo jwkerr, ich hatte gehofft, dass ich dich dazu bringen könnte, deine Rendering-Funktion zu posten. Ich habe versucht, Stereo-Rendering für eine Weile arbeiten zu lassen und kann es nicht ganz bekommen. Danke – VaporwareWolf

Antwort

3

Das OP scheint sein Problem gelöst zu haben, aber ich dachte, eine explizite Antwort wäre hilfreich für den Rest von uns.

Ich hatte die gleiche Frage, die Töne zu linken und rechten Kanälen unabhängig zu richten. Es ist am einfachsten zu beschreiben in Bezug auf Matt Gallaghers heutigen Standard An iOS tone generator (an introduction to AudioUnits).

Die erste Änderung ist die Einstellung streamFormat.mChannelsPerFrame = 2; (anstelle von streamFormat.mChannelsPerFrame = 1;) in der createToneUnit Methode (nach @jwkerr). Sobald dies geschehen ist, und Sie haben zwei Kanäle/Puffer in jedem Frame, müssen Sie die linken und rechten Puffer unabhängig in RenderTone() füllen:

// 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; 
} 

Natürlich channelLR[2] ein bool Array, dessen Elemente Sie gesetzt, um anzuzeigen, ob die entsprechender Kanal ist hörbar. Beachten Sie, dass das Programm die Frames von Silent-Kanälen explizit auf Null setzen muss, sonst werden einige lustige Töne ausgegeben.

+0

channelLR muss mehr erklärt werden. –

+0

Bearbeitet. Ist das hilfreich? – JohnK

+0

Wann setzen Sie channelLR? Ich habe Ihr Beispiel versucht, aber es ist statisch –