2013-01-22 5 views
7

Ich möchte eine unglaublich einfache Anwendung für iOS mit einer Taste erstellen, die ein Audiosignal startet und stoppt. Das Signal wird nur eine Sinuswelle sein, und es wird mein Modell (eine Instanzvariable für das Volumen) während seiner gesamten Wiedergabe überprüfen und seine Lautstärke entsprechend ändern.iOS - Generiere und spiele unbestimmtes, einfaches Audio (Sinus)

Meine Schwierigkeit hat mit der unbestimmten Natur der Aufgabe zu tun. Ich verstehe, wie man Tabellen erstellt, sie mit Daten füllt, auf Tastenbetätigungen reagiert und so weiter; Wenn es jedoch darum geht, etwas auf unbestimmte Zeit weiterlaufen zu lassen (in diesem Fall ein Geräusch), bin ich ein wenig festgefahren! Irgendwelche Hinweise wären großartig!

Danke fürs Lesen.

+0

Es kann sein, dass AVAudioPlayer ist, was ich brauche, um loszulegen ... – Rogare

+1

AVAudioPlayer wäre der Weg zu gehen, wenn Sie nur eine vorgefertigte Sounddatei einer Sinuswelle spielen möchten (Sie können die Lautstärke steuern, aber nichts anderes, wie die Frequenz). – admsyn

Antwort

15

Hier ist eine Barebone-Anwendung, die eine generierte Frequenz on-demand spielen wird. Sie haben nicht angegeben, ob Sie iOS oder OSX verwenden möchten. Daher habe ich OSX gewählt, weil es etwas einfacher ist (kein Chaos mit den Audio-Session-Kategorien). Wenn Sie iOS benötigen, können Sie die fehlenden Bits ermitteln, indem Sie sich die Grundlagen der Audio Session-Kategorie ansehen und die Standard-Audioausgabeeinheit für die RemoteIO-Audioeinheit austauschen.

Beachten Sie, dass dies lediglich der Veranschaulichung einiger Grundlagen der Core Audio/Audio Unit dient. Sie werden wahrscheinlich in die API AUGraph schauen wollen, wenn Sie anfangen wollen, komplexer zu werden (auch im Interesse, ein sauberes Beispiel zu liefern, mache ich keine Fehlerüberprüfung. Immer machen Sie Fehlerüberprüfung, wenn Sie sich damit beschäftigen Kernaudio).

Sie müssen die AudioToolbox und AudioUnit Frameworks zu Ihrem Projekt hinzufügen, um diesen Code zu verwenden.

#import <AudioToolbox/AudioToolbox.h> 

@interface SWAppDelegate : NSObject <NSApplicationDelegate> 
{ 
    AudioUnit outputUnit; 
    double renderPhase; 
} 
@end 

@implementation SWAppDelegate 

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
{ 
// First, we need to establish which Audio Unit we want. 

// We start with its description, which is: 
    AudioComponentDescription outputUnitDescription = { 
     .componentType   = kAudioUnitType_Output, 
     .componentSubType  = kAudioUnitSubType_DefaultOutput, 
     .componentManufacturer = kAudioUnitManufacturer_Apple 
    }; 

// Next, we get the first (and only) component corresponding to that description 
    AudioComponent outputComponent = AudioComponentFindNext(NULL, &outputUnitDescription); 

// Now we can create an instance of that component, which will create an 
// instance of the Audio Unit we're looking for (the default output) 
    AudioComponentInstanceNew(outputComponent, &outputUnit); 
    AudioUnitInitialize(outputUnit); 

// Next we'll tell the output unit what format our generated audio will 
// be in. Generally speaking, you'll want to stick to sane formats, since 
// the output unit won't accept every single possible stream format. 
// Here, we're specifying floating point samples with a sample rate of 
// 44100 Hz in mono (i.e. 1 channel) 
    AudioStreamBasicDescription ASBD = { 
     .mSampleRate  = 44100, 
     .mFormatID   = kAudioFormatLinearPCM, 
     .mFormatFlags  = kAudioFormatFlagsNativeFloatPacked, 
     .mChannelsPerFrame = 1, 
     .mFramesPerPacket = 1, 
     .mBitsPerChannel = sizeof(Float32) * 8, 
     .mBytesPerPacket = sizeof(Float32), 
     .mBytesPerFrame = sizeof(Float32) 
    }; 

    AudioUnitSetProperty(outputUnit, 
         kAudioUnitProperty_StreamFormat, 
         kAudioUnitScope_Input, 
         0, 
         &ASBD, 
         sizeof(ASBD)); 

// Next step is to tell our output unit which function we'd like it 
// to call to get audio samples. We'll also pass in a context pointer, 
// which can be a pointer to anything you need to maintain state between 
// render callbacks. We only need to point to a double which represents 
// the current phase of the sine wave we're creating. 
    AURenderCallbackStruct callbackInfo = { 
     .inputProc  = SineWaveRenderCallback, 
     .inputProcRefCon = &renderPhase 
    }; 

    AudioUnitSetProperty(outputUnit, 
         kAudioUnitProperty_SetRenderCallback, 
         kAudioUnitScope_Global, 
         0, 
         &callbackInfo, 
         sizeof(callbackInfo)); 

// Here we're telling the output unit to start requesting audio samples 
// from our render callback. This is the line of code that starts actually 
// sending audio to your speakers. 
    AudioOutputUnitStart(outputUnit); 
} 

// This is our render callback. It will be called very frequently for short 
// buffers of audio (512 samples per call on my machine). 
OSStatus SineWaveRenderCallback(void * inRefCon, 
           AudioUnitRenderActionFlags * ioActionFlags, 
           const AudioTimeStamp * inTimeStamp, 
           UInt32 inBusNumber, 
           UInt32 inNumberFrames, 
           AudioBufferList * ioData) 
{ 
    // inRefCon is the context pointer we passed in earlier when setting the render callback 
    double currentPhase = *((double *)inRefCon); 
    // ioData is where we're supposed to put the audio samples we've created 
    Float32 * outputBuffer = (Float32 *)ioData->mBuffers[0].mData; 
    const double frequency = 440.; 
    const double phaseStep = (frequency/44100.) * (M_PI * 2.); 

    for(int i = 0; i < inNumberFrames; i++) { 
     outputBuffer[i] = sin(currentPhase); 
     currentPhase += phaseStep; 
    } 

    // If we were doing stereo (or more), this would copy our sine wave samples 
    // to all of the remaining channels 
    for(int i = 1; i < ioData->mNumberBuffers; i++) { 
     memcpy(ioData->mBuffers[i].mData, outputBuffer, ioData->mBuffers[i].mDataByteSize); 
    } 

    // writing the current phase back to inRefCon so we can use it on the next call 
    *((double *)inRefCon) = currentPhase; 
    return noErr; 
} 

- (void)applicationWillTerminate:(NSNotification *)notification 
{ 
    AudioOutputUnitStop(outputUnit); 
    AudioUnitUninitialize(outputUnit); 
    AudioComponentInstanceDispose(outputUnit); 
} 

@end 

können Sie rufen AudioOutputUnitStart() und AudioOutputUnitStop() nach Belieben Produktion von Audio zu starten/stoppen. Wenn Sie die Frequenz dynamisch ändern möchten, können Sie einen Zeiger an einen struct übergeben, der sowohl den RenderPhase-Double als auch einen anderen, der die gewünschte Frequenz darstellt, enthält.

Seien Sie vorsichtig beim Render-Callback. Es wird von einem Echtzeit-Thread aufgerufen (nicht vom selben Thread wie die Hauptlaufschleife). Render Rückrufe auf einige ziemlich strenge Zeitanforderungen unterliegen, was bedeutet, dass Sie in Ihrem Rückruf tun sollten nicht viele Dinge ist, wie zum Beispiel:

  • Weisen Speicher
  • Warten auf einem Mutex
  • Lesen aus einer Datei auf der Festplatte
  • Objective-C-Messaging (Ja, ernsthaft.)

Beachten Sie, dass dies nicht der einzige Weg, dies zu tun. Ich habe es nur so gezeigt, seit du dieses Core-Audio markiert hast. Wenn Sie die Frequenz nicht ändern müssen, können Sie einfach die AVAudioPlayer mit einer vorgefertigten Sounddatei verwenden, die Ihre Sinuswelle enthält.

Es gibt auch Novocaine, die eine Menge dieser Ausführlichkeit von Ihnen verbirgt. Sie könnten auch in die Audio Queue-API schauen, die dem von mir geschriebenen Core Audio-Beispiel ziemlich ähnlich ist, aber Sie ein wenig mehr von der Hardware entkoppelt (d. H. Es ist weniger streng, wie Sie sich in Ihrem Render-Callback verhalten).

+0

Vielen Dank dafür! Ich hatte "iOS" in den Titel der Frage eingefügt, aber Entschuldigung, ich hätte einen Tag und/oder eine Notiz in den Fragetext eingefügt. Ich werde das jetzt reparieren. – Rogare

+0

@ Rogare guten Punkt, das habe ich vermisst! Mein Ziel war es, einige der Konzepte von Core Audio zu demonstrieren, damit Sie anfangen können. Das heißt: Sie werden mit ziemlicher Sicherheit später weitere Fragen haben, wenn Sie sich damit beschäftigen: p. Viel Glück! – admsyn

+0

Wie wäre es mit einem Ein-Signal oder Aus-Signal? –