2013-01-22 23 views
7

मैं एक बटन के साथ आईओएस के लिए एक अविश्वसनीय रूप से सरल एप्लिकेशन बनाना चाहता हूं जो ऑडियो सिग्नल शुरू करता है और बंद करता है। संकेत सिर्फ एक साइन लहर होने जा रहा है, और यह अपने प्लेबैक में मेरे मॉडल (वॉल्यूम के लिए एक आवृत्ति चर) की जांच करने जा रहा है और तदनुसार इसकी मात्रा बदल जाएगा।आईओएस - जेनरेट और अनिश्चित, सरल ऑडियो (साइन लहर) चलाएं

मेरी कठिनाई को कार्य की अनिश्चितकालीन प्रकृति के साथ करना है। मैं समझता हूं कि टेबल कैसे बनाएं, उन्हें डेटा से भरें, बटन दबाएं, और इसी तरह; हालांकि, जब अनिश्चित काल तक कुछ जारी रहता है (इस मामले में, एक आवाज), मैं थोड़ा फंस गया हूँ! कोई संकेतक भयानक होगा!

पढ़ने के लिए धन्यवाद।

+0

यह हो सकता है कि AVAudioPlayer मुझे प्रारंभ करने की आवश्यकता है ... – Rogare

+1

AVAudioPlayer जाने का मार्ग होगा यदि आप सिर्फ साइन लहर की पूर्व-निर्मित ध्वनि फ़ाइल खेलना चाहते हैं (आप वॉल्यूम को नियंत्रित करने में सक्षम होंगे लेकिन आवृत्ति जैसे कुछ और नहीं)। – admsyn

उत्तर

15

यहां एक नंगे हड्डियों का आवेदन है जो ऑन-डिमांड पर जनरेटेड आवृत्ति खेलेंगे। आपने यह निर्दिष्ट नहीं किया है कि आईओएस या ओएसएक्स करना है या नहीं, इसलिए मैं ओएसएक्स के लिए गया हूं क्योंकि यह थोड़ा आसान है (ऑडियो सत्र श्रेणियों के साथ कोई गड़बड़ नहीं है)। यदि आपको आईओएस की आवश्यकता है, तो आप ऑडियो सत्र श्रेणी मूल बातें देखकर और रिमोटियो ऑडियो इकाई के लिए डिफ़ॉल्ट आउटपुट ऑडियो इकाई को स्वैप करके लापता बिट्स को ढूंढ पाएंगे।

ध्यान दें कि इसका उद्देश्य कुछ कोर ऑडियो/ऑडियो यूनिट मूलभूत बातें प्रदर्शित करने के लिए पूरी तरह से है। यदि आप इस से अधिक जटिल होना शुरू करना चाहते हैं तो आप शायद AUGraph एपीआई देखना चाहेंगे (एक साफ उदाहरण प्रदान करने के हित में भी, मैं कोई त्रुटि जांच नहीं कर रहा हूं। हमेशा से निपटने में त्रुटि की जांच करें कोर ऑडियो)।

आपको इस कोड का उपयोग करने के लिए अपनी परियोजना में AudioToolbox और AudioUnit ढांचे को जोड़ने की आवश्यकता होगी।

#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 

आप होगा पर AudioOutputUnitStart() और AudioOutputUnitStop() फोन शुरू/ऑडियो उत्पादन बंद कर सकते हैं। यदि आप आवृत्ति को गतिशील रूप से बदलना चाहते हैं, तो आप एक पॉइंटर में struct पर पास कर सकते हैं जिसमें रेंडरफेस डबल और दूसरा एक आवृत्ति है जो आप चाहते हैं।

कॉलबैक प्रस्तुत करने में सावधान रहें। इसे रीयलटाइम थ्रेड से कहा जाता है (आपके मुख्य रन लूप के समान धागे से नहीं)। प्रस्तुत कॉलबैक काफी कुछ सख्त समय की आवश्यकताओं, जिसका अर्थ है वहाँ इस तरह के रूप में बहुत सी बातें अपने कॉलबैक में क्या नहीं करना चाहिए, यह है कि के अधीन हैं:

  • एक फ़ाइल से स्मृति आवंटित
  • प्रतीक्षा एक म्युटेक्स पर
  • पढ़ें डिस्क पर
  • ऑब्जेक्टिव-सी संदेश (हाँ, गंभीरता से।)

ध्यान दें कि यह यह करने के लिए एक ही रास्ता नहीं है। मैंने इसे इस तरह से प्रदर्शित किया है क्योंकि आपने इस कोर-ऑडियो को टैग किया है। यदि आपको आवृत्ति बदलने की आवश्यकता नहीं है तो आप अपनी साइन लहर वाली पूर्व-निर्मित ध्वनि फ़ाइल के साथ AVAudioPlayer का उपयोग कर सकते हैं।

Novocaine भी है, जो आपके द्वारा इस वर्बोजिटी को बहुत अधिक छुपाता है। आप ऑडियो क्यूई एपीआई में भी देख सकते हैं, जो मैंने लिखा कोर ऑडियो नमूना के समान ही काम करता है लेकिन हार्डवेयर से आपको थोड़ा और कम करता है (यानी यह आपके बारे में कम सख्त है कि आप अपने रेंडर कॉलबैक में कैसे व्यवहार करते हैं)।

+0

इसके लिए बहुत बहुत धन्यवाद! मैंने प्रश्न के शीर्षक में "आईओएस" रखा था, लेकिन खेद है, मुझे प्रश्नपत्र में एक टैग और/या एक नोट जोड़ा जाना चाहिए था। मैं इसे अभी ठीक कर दूंगा। – Rogare

+0

@Rogare अच्छा बिंदु, मुझे याद आया! मेरा लक्ष्य, वास्तव में, कोर ऑडियो में खेलने की कुछ अवधारणाओं को प्रदर्शित करना था, जैसे कि आप शुरू कर सकते हैं। यह कहना है: यदि आप इसमें खोदना शुरू करते हैं तो आपको लगभग निश्चित रूप से अधिक प्रश्न होंगे: पी। सौभाग्य! – admsyn

+0

सिग्नल या ऑफ सिग्नल उत्पन्न करने के बारे में कैसे? –