iphone ऐप्पल एफएफटी का उपयोग करना और फ्रेमवर्क को तेज करना




audio signal-processing (4)

यहां एक असली दुनिया का उदाहरण दिया गया है: सी ++ का स्निपेट जो रिमोट आईओ ऑडियो यूनिट के इनपुट पर स्वत: सहसंबंध करने के लिए एक्सीलरेट के वीडीएसपी एफएफटी दिनचर्या का उपयोग करता है। इस ढांचे का उपयोग करना बहुत जटिल है, लेकिन दस्तावेज बहुत खराब नहीं है

OSStatus DSPCore::initialize (double _sampleRate, uint16_t _bufferSize) {
    sampleRate = _sampleRate;
    bufferSize = _bufferSize;
    peakIndex = 0;
    frequency = 0.f;
    uint32_t maxFrames = getMaxFramesPerSlice();
    displayData = (float*)malloc(maxFrames*sizeof(float));
    bzero(displayData, maxFrames*sizeof(float));
    log2n = log2f(maxFrames);
    n = 1 << log2n;
    assert(n == maxFrames);
    nOver2 = maxFrames/2;
    A.realp = (float*)malloc(nOver2 * sizeof(float));
    A.imagp = (float*)malloc(nOver2 * sizeof(float));
    FFTSetup fftSetup = vDSP_create_fftsetup(log2n, FFT_RADIX2);

    return noErr;
}

void DSPCore::Render(uint32_t numFrames, AudioBufferList *ioData) {

    bufferSize = numFrames;
    float ln = log2f(numFrames);

    //vDSP autocorrelation

    //convert real input to even-odd
    vDSP_ctoz((COMPLEX*)ioData->mBuffers[0].mData, 2, &A, 1, numFrames/2);
    memset(ioData->mBuffers[0].mData, 0, ioData->mBuffers[0].mDataByteSize);
    //fft
    vDSP_fft_zrip(fftSetup, &A, 1, ln, FFT_FORWARD);

    // Absolute square (equivalent to mag^2)
    vDSP_zvmags(&A, 1, A.realp, 1, numFrames/2);
    bzero(A.imagp, (numFrames/2) * sizeof(float));    

    // Inverse FFT
    vDSP_fft_zrip(fftSetup, &A, 1, ln, FFT_INVERSE);

    //convert complex split to real
    vDSP_ztoc(&A, 1, (COMPLEX*)displayData, 2, numFrames/2);

    // Normalize
    float scale = 1.f/displayData[0];
    vDSP_vsmul(displayData, 1, &scale, displayData, 1, numFrames);

    // Naive peak-pick: find the first local maximum
    peakIndex = 0;
    for (size_t ii=1; ii < numFrames-1; ++ii) {
        if ((displayData[ii] > displayData[ii-1]) && (displayData[ii] > displayData[ii+1])) {
            peakIndex = ii;
            break;
        }
    }

    // Calculate frequency
    frequency = sampleRate / peakIndex + quadInterpolate(&displayData[peakIndex-1]);

    bufferSize = numFrames;

    for (int ii=0; ii<ioData->mNumberBuffers; ++ii) {
        bzero(ioData->mBuffers[ii].mData, ioData->mBuffers[ii].mDataByteSize);
    }
}

क्या किसी ने अभी तक एक आईफोन ऐप के लिए Apple FFT इस्तेमाल किया है या पता है कि इसका उपयोग करने के तरीके के बारे में मुझे नमूना आवेदन कहां मिल सकता है? मुझे पता है कि ऐप्पल के पास कुछ नमूना कोड पोस्ट किए गए हैं, लेकिन मुझे सच में यकीन नहीं है कि इसे वास्तविक परियोजना में कैसे कार्यान्वित किया जाए।


जबकि मैं कहूंगा कि ऐप्पल का एफएफटी फ्रेमवर्क तेज़ है ... आपको यह जानने की जरूरत है कि सटीक पिच डिटेक्शन प्राप्त करने के लिए एफएफटी कैसे काम करता है (यानी सटीक पिच खोजने के लिए प्रत्येक क्रमिक एफएफटी पर चरण अंतर की गणना करना, न कि पिच सबसे अधिक बिन पर हावी है)।

मुझे नहीं पता कि यह किसी भी मदद की है, लेकिन मैंने अपने ट्यूनर एप (musicianskit.com/developer.php) से अपनी पिच डिटेक्टर ऑब्जेक्ट अपलोड की है। डाउनलोड के लिए xCode 4 प्रोजेक्ट भी एक उदाहरण है (ताकि आप देख सकें कि कार्यान्वयन कैसे कार्य करता है)।

मैं एक उदाहरण एफएफटी कार्यान्वयन अपलोड करने पर काम कर रहा हूं - इसलिए देखते रहें और ऐसा होने पर मैं इसे अपडेट कर दूंगा।

हैप्पी कोडिंग!



मुझे अभी एक आईफोन परियोजना के लिए काम कर रहे एफएफटी कोड मिल गए हैं:

  • एक नई परियोजना बनाएँ
  • main.m और xxx_info.plist को छोड़कर सभी फ़ाइलों को हटाएं
  • प्रोजेक्ट सेटिंग्स पर जाकर पीएच के लिए खोज करें और इसे .pch लोड करने की कोशिश करने से रोकें (जैसा कि हमने इसे अभी हटा दिया है)
  • मुख्य जो भी है, उस पर कोड उदाहरण पेस्ट करें
  • उस पंक्ति को हटा दें जिसमें # कार्बन शामिल है। कार्बन ओएसएक्स के लिए है।
  • सभी ढांचे को हटाएं, और ढांचे को तेज करें

आपको info.plist से एक प्रविष्टि को हटाने की भी आवश्यकता हो सकती है जो परियोजना को xib लोड करने के लिए कहती है, लेकिन मुझे 9 0% यकीन है कि आपको इससे परेशान करने की आवश्यकता नहीं है।

नोट: प्रोग्राम आउटसोर्स को कंसोल करने के लिए, परिणाम 0.000 के रूप में सामने आते हैं जो कोई त्रुटि नहीं है - यह बहुत तेज़ है

यह कोड वास्तव में बेवकूफ अस्पष्ट है; यह उदारता से टिप्पणी की जाती है, लेकिन टिप्पणियां वास्तव में जीवन को आसान नहीं बनाती हैं।

मूल रूप से इसके दिल में है:

vDSP_fft_zrip(setupReal, &A, stride, log2n, FFT_FORWARD);
vDSP_fft_zrip(setupReal, &A, stride, log2n, FFT_INVERSE);

एन असली फ्लोट पर एफएफटी, और फिर जहां हमने शुरू किया, वहां वापस जाने के लिए विपरीत। आईपी ​​जगह के लिए खड़ा है, जिसका मतलब है और ए ओवरराइट हो जाता है यही कारण है कि यह सभी विशेष पैकिंग मालार्की का कारण है - ताकि हम वापसी मूल्य के रूप में उसी मूल्य में वापसी मूल्य को स्क्वैश कर सकें।

कुछ परिप्रेक्ष्य देने के लिए (जैसे, जैसा कि: हम इस फ़ंक्शन का उपयोग पहली जगह क्यों करेंगे?), मान लें कि हम माइक्रोफ़ोन इनपुट पर पिच डिटेक्शन करना चाहते हैं, और हमने इसे सेट अप किया है ताकि कुछ कॉलबैक हर बार ट्रिगर हो जाए माइक्रोफोन 1024 फ्लोट्स में आता है। माइक्रोफोन नमूना दर का मानना ​​44.1kHz था, इसलिए यह ~ 44 फ्रेम / सेकंड है।

तो, हमारी टाइम-विंडो जो कुछ भी 1024 नमूने की अवधि है, यानी 1/44 एस है।

तो हम mic से 1024 फ्लोट के साथ पैक करेंगे, log2n = 10 (2 ^ 10 = 1024) सेट करें, कुछ बॉबिन (setupReal) को सटीक बनाएं और:

vDSP_fft_zrip(setupReal, &A, stride, log2n, FFT_FORWARD);

अब ए में एन / 2 जटिल संख्याएं होंगी। ये एन / 2 फ्रीक्वेंसी डिब्बे का प्रतिनिधित्व करते हैं:

  • बिन [1] .idealFreq = 44Hz - यानि सबसे कम आवृत्ति जिसे हम विश्वसनीय रूप से पहचान सकते हैं वह उस खिड़की के भीतर एक पूर्ण लहर है, यानी 44 हर्ट्ज तरंग।

  • बिन [2] .idealFreq = 2 * 44 हर्ट्ज

  • आदि।

  • बिन [512] .idealFreq = 512 * 44Hz - उच्चतम आवृत्ति जिसे हम पहचान सकते हैं (Nyquist आवृत्ति के रूप में जाना जाता है) वह है जहां बिंदुओं की प्रत्येक जोड़ी एक लहर का प्रतिनिधित्व करती है, यानि खिड़की के भीतर 512 पूर्ण तरंगें, यानी 512 * 44 हर्ट्ज, या: एन / 2 * बिन [1] .idealFreq

  • असल में एक अतिरिक्त बिन, बिन [0] है जिसे अक्सर 'डीसी ऑफ़सेट' कहा जाता है। ऐसा इसलिए होता है कि बिन [0] और बिन [एन / 2] में हमेशा जटिल घटक 0 होगा, इसलिए ए [0] .realp का उपयोग बिन [0] और ए [0] को स्टोर करने के लिए किया जाता है .imagp का उपयोग बिन को स्टोर करने के लिए किया जाता है [ n / 2]

और प्रत्येक जटिल संख्या की परिमाण उस आवृत्ति के चारों ओर कंपन ऊर्जा की मात्रा है।

इसलिए, जैसा कि आप देख सकते हैं, यह एक बहुत अच्छा पिच डिटेक्टर नहीं होगा क्योंकि इसमें लगभग पर्याप्त ग्रैन्युलरिटी नहीं है। एक चालाक चाल है जो किसी दिए गए बिन के लिए सटीक आवृत्ति प्राप्त करने के लिए फ्रेम के बीच चरण परिवर्तन का उपयोग करके एफएफटी डिब्बे से सटीक आवृत्तियों को निकालना है

ठीक है, अब कोड पर:

VDSP_fft_zrip में 'ip' पर ध्यान दें, = 'जगह में' यानी आउटपुट ओवरराइट ए ('आर' का अर्थ है कि यह वास्तविक इनपुट लेता है)

VDSP_fft_zrip पर प्रलेखन को देखें,

वास्तविक डेटा स्प्लिट कॉम्प्लेक्स फॉर्म में संग्रहीत किया जाता है, जिसमें विभाजित जटिल रूप के काल्पनिक पक्ष पर संग्रहीत विषम वास्तविकताओं और वास्तविक पक्ष पर संग्रहीत वास्तविकता भी होती है।

यह शायद समझने की सबसे कठिन बात है। हम प्रक्रिया के माध्यम से एक ही कंटेनर (और ए) का उपयोग कर रहे हैं। इसलिए शुरुआत में हम इसे वास्तविक संख्याओं से भरना चाहते हैं। एफएफटी के बाद यह एन / 2 जटिल संख्या आयोजित करने जा रहा है। फिर हम इसे उलटा परिवर्तन में फेंक देते हैं, और उम्मीद है कि हमारी मूल वास्तविक संख्याएं प्राप्त करें।

अब जटिल मूल्यों के लिए इसकी स्थापना की संरचना। इसलिए वीडीएसपी को वास्तविक संख्याओं को पैक करने के तरीके को मानकीकृत करने की आवश्यकता है।

तो सबसे पहले हम वास्तविक संख्या उत्पन्न करते हैं: 1, 2, ..., एन

for (i = 0; i < n; i++)
    originalReal[i] = (float) (i + 1);

इसके बाद हम उन्हें ए / 2 जटिल # एस के रूप में पैक करते हैं:

// 1. masquerades n real #s as n/2 complex #s = {1+2i, 3+4i, ...}
// 2. splits to 
//   A.realP = {1,3,...} (n/2 elts)
//   A.compP = {2,4,...} (n/2 elts)
//
vDSP_ctoz(
          (COMPLEX *) originalReal, 
          2,                            // stride 2, as each complex # is 2 floats
          &A, 
          1,                            // stride 1 in A.realP & .compP
          nOver2);                      // n/2 elts

आपको वास्तव में यह देखने की आवश्यकता होगी कि इसे प्राप्त करने के लिए ए आवंटित किया गया है, शायद दस्तावेज़ में COMPLEX_SPLIT देखें।

A.realp = (float *) malloc(nOver2 * sizeof(float));
A.imagp = (float *) malloc(nOver2 * sizeof(float));

इसके बाद हम पूर्व-गणना करते हैं।

गणित के लिए त्वरित डीएसपी वर्ग: फोरियर सिद्धांत आपके सिर को पाने के लिए काफी समय लगता है (मैं इसे कई वर्षों से चालू और बंद कर रहा हूं)

एक सीसाइड है:

z = exp(i.theta) = cos(theta) + i.sin(theta)

यानी जटिल विमान में इकाई सर्कल पर एक बिंदु।

जब आप जटिल संख्या गुणा करते हैं, तो कोण जोड़ते हैं। तो z ^ k इकाई सर्कल के चारों ओर hopping रखना होगा; z ^ k एक कोण k.theta पर पाया जा सकता है

  • Z1 = 0 + 1i चुनें, यानी असली धुरी से एक चौथाई मोड़, और ध्यान दें कि z1 ^ 2 z1 ^ 3 z1 ^ 4 प्रत्येक एक और तिमाही मोड़ देता है ताकि z1 ^ 4 = 1

  • Z2 = -1 चुनें, यानी आधे-मोड़। z2 ^ 4 = 1 लेकिन z2 ने इस बिंदु पर 2 चक्र पूरे किए हैं (z2 ^ 2 भी = 1) है। तो आप जेड 1 को मौलिक आवृत्ति और जेड 2 के रूप में पहले हार्मोनिक के रूप में सोच सकते हैं

  • इसी तरह z3 = 'क्रांति के तीन-चौथाई' बिंदु अर्थात- मैं बिल्कुल 3 चक्र पूरा करता हूं, लेकिन वास्तव में आगे बढ़ रहा है 3/4 हर बार पिछली बार 1/4 के समान होता है

यानी z3 सिर्फ z1 है लेकिन विपरीत दिशा में - इसे एलियासिंग कहा जाता है

z2 उच्चतम अर्थपूर्ण आवृत्ति है, क्योंकि हमने पूर्ण लहर पकड़ने के लिए 4 नमूने चुना है।

  • z0 = 1 + 0i, z0 ^ (कुछ भी) = 1, यह डीसी ऑफ़सेट है

आप किसी भी 4-पॉइंट सिग्नल को z0 z1 और z2 के रैखिक संयोजन के रूप में व्यक्त कर सकते हैं यानी आप इसे इन आधार वैक्टरों पर पेश कर रहे हैं

लेकिन मैंने आपको यह कहते हुए सुना है कि "एक सीसाइड पर सिग्नल प्रोजेक्ट करने का क्या अर्थ है?"

आप इस तरह से सोच सकते हैं: सिसाइड के चारों ओर सुई स्पिन, इसलिए नमूना के पर, सुई दिशा k.theta में इंगित कर रही है, और लंबाई सिग्नल [के] है। एक सिग्नल जो आवृत्ति की आवृत्ति से मेल खाता है, वह परिणामस्वरूप आकार को कुछ दिशा में बढ़ा देगा। तो यदि आप सभी योगदान जोड़ते हैं, तो आपको एक मजबूत परिणामस्वरूप वेक्टर मिल जाएगा। यदि आवृत्ति लगभग मेल खाती है, तो बल्गे की तुलना में छोटा होगा और सर्कल के चारों ओर धीरे-धीरे आगे बढ़ेगा। एक संकेत के लिए जो आवृत्ति से मेल नहीं खाता है, योगदान एक-दूसरे को रद्द कर देंगे।

http://complextoreal.com/tutorials/tutorial-4-fourier-analysis-made-easy-part-1/ आपको सहज ज्ञान प्राप्त करने में मदद करेगा।

लेकिन जिस्ट है; अगर हमने {z0, ..., z512} पर 1024 नमूने प्रोजेक्ट करना चुना है, तो हम z512 के माध्यम से z0 को सटीक रूप से सटीक करेंगे, और यही वह सटीक कदम है

ध्यान दें कि यदि आप इसे वास्तविक कोड में कर रहे हैं तो शायद आप इसे एक बार ऐसा करना चाहते हैं जब ऐप लोड हो जाए और पूरक रिलीज फ़ंक्शन को छोड़कर एक बार कॉल करें। इसे कई बार मत करो - यह महंगा है।

// let's say log2n = 8, so n=2^8=256 samples, or 'harmonics' or 'terms'
// if we pre-calculate the 256th roots of unity (of which there are 256) 
// that will save us time later.
//
// Note that this call creates an array which will need to be released 
// later to avoid leaking
setupReal = vDSP_create_fftsetup(log2n, FFT_RADIX2);

यह ध्यान देने योग्य है कि अगर हम 8 के लिए log2n सेट करते हैं, तो आप इन पूर्ववर्ती मानों को किसी भी fft फ़ंक्शन में फेंक सकते हैं जो संकल्प <= 2 ^ 8 का उपयोग करता है। इसलिए (जब तक कि आप अंतिम स्मृति अनुकूलन नहीं चाहते हैं) केवल उच्चतम रिज़ॉल्यूशन के लिए एक सेट बनाएं, जिसकी आपको आवश्यकता होगी, और इसे सब कुछ के लिए उपयोग करें।

अब वास्तविक परिवर्तन, जो सामान हमने अभी पूर्व निर्धारित किया है उसका उपयोग करना:

vDSP_fft_zrip(setupReal, &A, stride, log2n, FFT_FORWARD);

इस बिंदु पर ए में एन / 2 जटिल संख्याएं होंगी, केवल पहला ही वास्तव में दो वास्तविक संख्या (डीसी ऑफ़सेट, Nyquist #) जटिल संख्या के रूप में मजाक कर रहा है। दस्तावेज़ीकरण अवलोकन इस पैकिंग को बताता है। यह काफी साफ है - मूल रूप से यह ट्रांसफॉर्म के (जटिल) परिणामों को उसी स्मृति पदचिह्न में पैक करने की अनुमति देता है (असली, लेकिन अजीब रूप से पैक किया गया) इनपुट।

vDSP_fft_zrip(setupReal, &A, stride, log2n, FFT_INVERSE);

और फिर वापस ... हमें अभी भी ए से हमारी मूल सरणी को अनपैक करने की आवश्यकता होगी, फिर हम केवल यह जांचने के लिए तुलना करें कि हमने जो कुछ भी शुरू किया है, उसे वापस प्राप्त कर लिया है, हमारे पूर्ववर्ती बॉबिन को जारी किया है और किया है!

लेकिन रुकें! आपके द्वारा अनपॅक करने से पहले, एक अंतिम चीज है जिसे करने की आवश्यकता है:

// Need to see the documentation for this one...
// in order to optimise, different routines return values 
// that need to be scaled by different amounts in order to 
// be correct as per the math
// In this case...
scale = (float) 1.0 / (2 * n);

vDSP_vsmul(A.realp, 1, &scale, A.realp, 1, nOver2);
vDSP_vsmul(A.imagp, 1, &scale, A.imagp, 1, nOver2);




accelerate-framework