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).
Es kann sein, dass AVAudioPlayer ist, was ich brauche, um loszulegen ... – Rogare
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