2009-09-01 10 views
18

पर CoreAudio के साथ ध्वनियों को कैसे संश्लेषित करूं मैं एक आईफोन में एक संश्लेषित ध्वनि खेलना चाहता हूं। एक पूर्व-रिकॉर्ड की गई ध्वनि का उपयोग करने और मौजूदा बाइनरी चलाने के लिए SystemSoundID का उपयोग करने के बजाय, मैं इसे संश्लेषित करना चाहता हूं। आंशिक रूप से, ऐसा इसलिए है क्योंकि मैं लगातार एक ध्वनि ध्वनि नमूना के बजाय ध्वनि को चलाने में सक्षम होना चाहता हूं (उदाहरण के लिए जब उपयोगकर्ता की उंगली स्क्रीन पर होती है)।मैं आईफोन/मैक

यदि मैं मध्य ए + 1 (ए 4) (440 हर्ट्ज) को संश्लेषित करना चाहता हूं, तो मैं पाप() का उपयोग करके साइन लहर की गणना कर सकता हूं; जो मुझे नहीं पता है कि उन बिट्स को एक पैकेट में व्यवस्थित करने का तरीका है जो CoreAudio तब खेल सकता है। नेट पर मौजूद अधिकांश ट्यूटोरियल मौजूदा बाइनरी खेलने से संबंधित हैं।

क्या कोई मुझे 440 हर्ट्ज पर एक सरल संश्लेषित साइन ध्वनि तरंग के साथ मदद कर सकता है?

उत्तर

13

क्या आप शायद ऑडियोक्यूयू सेट अप करने के लिए ऐसा करना चाहते हैं। यह आपको कॉलबैक में संश्लेषित ऑडियो डेटा के साथ एक बफर भरने की अनुमति देता है। आप सेटअप AudeioQueue इस तरह के रूप में एक नया सूत्र में चलाने के लिए होगा:

#define BUFFER_SIZE 16384 
#define BUFFER_COUNT 3 
static AudioQueueRef audioQueue; 
void SetupAudioQueue() { 
    OSStatus err = noErr; 
    // Setup the audio device. 
    AudioStreamBasicDescription deviceFormat; 
    deviceFormat.mSampleRate = 44100; 
    deviceFormat.mFormatID = kAudioFormatLinearPCM; 
    deviceFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; 
    deviceFormat.mBytesPerPacket = 4; 
    deviceFormat.mFramesPerPacket = 1; 
    deviceFormat.mBytesPerFrame = 4; 
    deviceFormat.mChannelsPerFrame = 2; 
    deviceFormat.mBitsPerChannel = 16; 
    deviceFormat.mReserved = 0; 
    // Create a new output AudioQueue for the device. 
    err = AudioQueueNewOutput(&deviceFormat, AudioQueueCallback, NULL, 
           CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 
           0, &audioQueue); 
    // Allocate buffers for the AudioQueue, and pre-fill them. 
    for (int i = 0; i < BUFFER_COUNT; ++i) { 
     AudioQueueBufferRef mBuffer; 
     err = AudioQueueAllocateBuffer(audioQueue, BUFFER_SIZE, mBuffer); 
     if (err != noErr) break; 
     AudioQueueCallback(NULL, audioQueue, mBuffer); 
    } 
    if (err == noErr) err = AudioQueueStart(audioQueue, NULL); 
    if (err == noErr) CFRunLoopRun(); 
    } 

आप कॉलबैक विधि AudioQueueCallback तो कहा जाएगा जब भी AudioQueue अधिक डेटा की आवश्यकता। की तरह कुछ के साथ लागू करें:

void AudioQueueCallback(void* inUserData, AudioQueueRef inAQ, 
         AudioQueueBufferRef inBuffer) { 
    void* pBuffer = inBuffer->mAudioData; 
    UInt32 bytes = inBuffer->mAudioDataBytesCapacity; 
    // Write max <bytes> bytes of audio to <pBuffer> 
    outBuffer->mAudioDataByteSize = actualNumberOfBytesWritten 
    err = AudioQueueEnqueueBuffer(audioQueue, inBuffer, 0, NULL); 
} 
+0

यह सही नहीं है। आपको आवंटन लूप में AudioQueueCallback को कॉल नहीं करना चाहिए। मुझे विश्वास नहीं है कि वर्णन सही है, या तो। प्लस, आपको इस अजीब तरीके के बजाय AudioQueueStart (audioQueue, nil) को कॉल करना चाहिए। इसके बजाय AudioUnit फ्रेमवर्क देखें। – thefaj

+0

@thefaj: मुझे विश्वास है कि आप ही गलत हैं। यह उदाहरण मेरे ऐप एससी 68 प्लेयर (http://itunes.apple.com/se/app/sc68-player/id295290413?mt=8) से लिया गया है, जहां मैंने मूल रूप से ऐप्पल के उदाहरण आईफोन ऐप से ऑडियो रीप्ले के लिए कोड लिया है SpeakHere (http://developer.apple.com/iphone/library/samplecode/SpeakHere/), AQPlayer.mm फ़ाइल को देखें। एससी 68 प्लेयर के लिए पूर्ण स्रोत कोड उपलब्ध है (http://www.peylow.se/sc68player.html)। – PeyloW

+0

आपके उदाहरण में ऑडियोQueueStart() गुम है, जो ऑडियो क्यूई कलकबैक को कॉल किया जाना चाहिए। – thefaj

0

कई ऑडियो तकनीकें ध्वनि फ़ाइल की बजाय डेटा को पारित करने की अनुमति देती हैं। AVAudioPlayer, उदाहरण के लिए, है:

-initWithData:error: 
Initializes and returns an audio player for playing a designated memory buffer. 

- (id)initWithData:(NSData *)data error:(NSError **)outError 

हालांकि, मुझे यकीन है कि तुम कैसे, एक डेटा ptr में से होकर गुजरेगा ध्वनि शुरू नहीं कर रहा हूँ, और फिर इसे अन्य डेटा ptrs में गुजर, या एक ही दोहराते हुए पाशन रखते हुए, आदि

4

http://lists.apple.com/archives/coreaudio-api/2008/Dec/msg00173.html को डेविड Vosti के लिंक अब काम करता है, के बाद से एप्पल सूचियों अनुत्तरदायी होने लगते हैं। पूर्णता के लिए Google का कैश यहां है।

// 
// AudioUnitTestAppDelegate.m 
// AudioUnitTest 
// 
// Created by Marc Vaillant on 11/25/08. 
// Copyright __MyCompanyName__ 2008. All rights reserved. 
// 

#import "AudioUnitTestAppDelegate.h" 
#include <AudioUnit/AudioUnit.h> 
//#include "MachTimer.hpp" 
#include <vector> 
#include <iostream> 

using namespace std; 

#define kOutputBus 0 
#define kInputBus 1 
#define SAMPLE_RATE 44100 

vector<int> _pcm; 
int _index; 

@implementation AudioUnitTestAppDelegate 

@synthesize window; 

void generateTone(
       vector<int>& pcm, 
       int freq, 
       double lengthMS, 
       int sampleRate, 
       double riseTimeMS, 
       double gain) 
{ 
    int numSamples = ((double) sampleRate) * lengthMS/1000.; 
    int riseTimeSamples = ((double) sampleRate) * riseTimeMS/1000.; 

    if(gain > 1.) 
    gain = 1.; 
    if(gain < 0.) 
    gain = 0.; 

    pcm.resize(numSamples); 

    for(int i = 0; i < numSamples; ++i) 
    { 
    double value = sin(2. * M_PI * freq * i/sampleRate); 
    if(i < riseTimeSamples) 
     value *= sin(i * M_PI/(2.0 * riseTimeSamples)); 
    if(i > numSamples - riseTimeSamples - 1) 
     value *= sin(2. * M_PI * (i - (numSamples - riseTimeSamples) + riseTimeSamples)/ (4. * riseTimeSamples)); 

    pcm[i] = (int) (value * 32500.0 * gain); 
    pcm[i] += (pcm[i]<<16); 
    } 

} 

static OSStatus playbackCallback(void *inRefCon, 
            AudioUnitRenderActionFlags *ioActionFlags, 
            const AudioTimeStamp *inTimeStamp, 
            UInt32 inBusNumber, 
            UInt32 inNumberFrames, 
            AudioBufferList *ioData) 
{  
    cout<<"index = "<<_index<<endl; 
    cout<<"numBuffers = "<<ioData->mNumberBuffers<<endl; 

    int totalNumberOfSamples = _pcm.size(); 
    for(UInt32 i = 0; i < ioData->mNumberBuffers; ++i) 
    { 
     int samplesLeft = totalNumberOfSamples - _index; 
     int numSamples = ioData->mBuffers[i].mDataByteSize/4; 
     if(samplesLeft > 0) 
     { 
     if(samplesLeft < numSamples) 
     { 
      memcpy(ioData->mBuffers[i].mData, &_pcm[_index], samplesLeft * 4); 
      _index += samplesLeft; 
      memset((char*) ioData->mBuffers[i].mData + samplesLeft * 4, 0, (numSamples - samplesLeft) * 4) ; 
     } 
     else 
     { 
      memcpy(ioData->mBuffers[i].mData, &_pcm[_index], numSamples * 4) ; 
      _index += numSamples; 
     } 
     } 
     else 
     memset(ioData->mBuffers[i].mData, 0, ioData->mBuffers[i].mDataByteSize); 
    } 

    return noErr; 
} 

- (void)applicationDidFinishLaunching:(UIApplication *)application 
{  
    //generate pcm tone freq = 800, duration = 1s, rise/fall time = 5ms 

    generateTone(_pcm, 800, 1000, SAMPLE_RATE, 5, 0.8); 
    _index = 0; 

    OSStatus status; 
    AudioComponentInstance audioUnit; 

    // Describe audio component 
    AudioComponentDescription desc; 
    desc.componentType = kAudioUnitType_Output; 
    desc.componentSubType = kAudioUnitSubType_RemoteIO; 
    desc.componentFlags = 0; 
    desc.componentFlagsMask = 0; 
    desc.componentManufacturer = kAudioUnitManufacturer_Apple; 

    // Get component 
    AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc); 

    // Get audio units 
    status = AudioComponentInstanceNew(inputComponent, &audioUnit); 
    //checkStatus(status); 

    UInt32 flag = 1; 
    // Enable IO for playback 
    status = AudioUnitSetProperty(audioUnit, 
        kAudioOutputUnitProperty_EnableIO, 
        kAudioUnitScope_Output, 
        kOutputBus, 
        &flag, 
        sizeof(flag)); 
    //checkStatus(status); 

    // Describe format 

    AudioStreamBasicDescription audioFormat; 
    audioFormat.mSampleRate = SAMPLE_RATE; 
    audioFormat.mFormatID = kAudioFormatLinearPCM; 
    audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; 
    audioFormat.mFramesPerPacket = 1; 
    audioFormat.mChannelsPerFrame = 2; 
    audioFormat.mBitsPerChannel = 16; 
    audioFormat.mBytesPerPacket = 4; 
    audioFormat.mBytesPerFrame = 4; 

    // Apply format 

    status = AudioUnitSetProperty(audioUnit, 
        kAudioUnitProperty_StreamFormat, 
        kAudioUnitScope_Input, 
        kOutputBus, 
        &audioFormat, 
        sizeof(audioFormat)); 
// checkStatus(status); 

    // Set output callback 
    AURenderCallbackStruct callbackStruct; 
    callbackStruct.inputProc = playbackCallback; 
    callbackStruct.inputProcRefCon = self; 
    status = AudioUnitSetProperty(audioUnit, 
        kAudioUnitProperty_SetRenderCallback, 
        kAudioUnitScope_Global, 
        kOutputBus, 
        &callbackStruct, 
        sizeof(callbackStruct)); 

    // Initialize 
    status = AudioUnitInitialize(audioUnit); 

    // Start playing 

    status = AudioOutputUnitStart(audioUnit); 

    [window makeKeyAndVisible]; 
} 


- (void)dealloc { 
    [window release]; 
    [super dealloc]; 
} 


@end 
+0

मैं इसे डेविड के प्रश्न पर टिप्पणी के रूप में जोड़ता, लेकिन टिप्पणियों के लिए 600 वर्ण सीमा है। – AlBlue