Soru Apple FFT ve Hızlandırıcı Çerçeveyi Kullanma


Kimseyi kullandı mı Apple FFT Bir iPhone uygulaması için henüz nasıl kullanılacağını öğrenmek için örnek bir uygulama bulabilir miyim? Apple'ın bazı örnek kodlar yayınladığını biliyorum, ancak bunu gerçek bir projeye nasıl uygulayacağımı gerçekten bilmiyorum.


68
2017-08-03 16:48


Menşei


İyi bağırışlar. Belgeler iğrenç. - P i
@Pi Özellikle, özellikle birçok durumda geçerli olmayan özel veri siparişi ile ilgili bölüm. - marko


Cevaplar:


Bir iPhone projesi için çalışan FFT kodunu aldım:

  • yeni bir proje oluştur
  • main.m ve xxx_info.plist dışındaki tüm dosyaları sil
  • proje ayarlarına gidip pch arayın ve bir .pch yüklemeye çalışmasını durdurun (yeni sildiğimiz gibi)
  • kopyala kod örneğini main.m'de sahip olduğunuz her şeye yapıştırın.
  • # include’s Carbon satırını kaldırın. Karbon OSX içindir.
  • tüm çerçeveleri silin ve hızlandırma çerçevesi ekleyin

Ayrıca projeye bir xib yüklemesi gerektiğini belirten bir girdiyi de kaldırmanız gerekebilir, ancak bununla uğraşmanıza gerek kalmadığından% 90 eminim.

NOT: Program çıktıları konsola, sonuçlar hata olarak değil 0,000 olarak çıkar - sadece çok hızlı

Bu kod gerçekten aptalca belirsizdir; cömertçe yorumlandı, ancak yorumlar aslında hayatı kolaylaştırmıyor.

Temel olarak onun kalbinde:

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

FFT üzerinde n gerçek yüzer, ve sonra başladığımız yere geri dönmek için geri. ip yerinde duruyor, yani & üzerine yazılır Tüm bu özel paketleme malarkeyinin nedeni budur - böylece geri dönüş değerini gönderme değeriyle aynı alana getirebiliriz.

Bir perspektiften bahsetmek için (örneğin, bu işlevi neden ilk etapta kullanıyor olalım?), Mikrofon girişi üzerinde perde tespitini gerçekleştirmek istediğimizi varsayalım ve bunu her seferinde bazı geri bildirimlerin tetiklenmesi için ayarladık. Mikrofon, 1024 yüzerlikte alır. Mikrofon örnekleme oranını varsaymak 44.1kHz, yani ~ 44 frame / sn.

Yani, bizim zaman penceremiz, 1024 örneğin zaman süresi ne olursa olsun, yani 1/44 s'dir.

Böylelikle 1024 şamandıraları A'dan alalım, log2n = 10 (2 ^ 10 = 1024), bazı bobinleri (setupReal) önceden hesaplayalım ve:

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

Şimdi A, n / 2 karmaşık sayıları içerecektir. Bunlar n / 2 frekans kutularını temsil eder:

  • bin [1] .idealFreq = 44Hz - yani güvenilir bir şekilde algılayabildiğimiz en düşük frekans, o pencerenin içindeki bir tam dalgadır, yani bir 44Hz dalgasıdır.

  • kutu [2] .idealFreq = 2 * 44Hz

  • vb.

  • bin [512] .idealFreq = 512 * 44Hz - Algılayabildiğimiz en yüksek frekans (Nyquist frekansı olarak bilinir), her bir nokta çiftinin bir dalgayı, yani pencerede 512 tam dalgaları, yani 512 * 44Hz'i temsil ettiği yerdir: n / 2 * kutu [1] .idealFreq

  • Aslında, genellikle 'DC Ofseti' olarak adlandırılan fazladan bir Bin, Bin [0] bulunmaktadır. Bu durumda, Bin [0] ve Bin [n / 2] 'nin her zaman karmaşık komponent 0'a sahip olacağı, yani Bin [0] ve A [0] .gerp'in depolanması için bir realp kullanılır. n / 2]

Ve her karmaşık sayının büyüklüğü, bu frekansın etrafında titreşen enerji miktarıdır.

Gördüğünüz gibi, yeterince iyi bir ayrıntıya sahip olmadığından çok büyük bir perde dedektörü olmayacaktır. Kurnaz bir hile var Çerçeveler arasındaki faz değişimi kullanılarak FFT Bins'den kesin frekanslar çıkarılıyor Belirli bir bin için kesin frekansı almak için.

Tamam, Şimdi kodun üzerine:

VDSP_fft_zrip, = 'in' ip 'ini not edin, yani' çıktı ', A'nın üzerine yazılır (' r ', gerçek girdileri alır)

VDSP_fft_zrip üzerindeki belgelere bakın,

Gerçek veriler bölünmüş komplekste saklanır   formda, garip reals   bölünmüş kompleksin hayali tarafı   formda ve hatta depolanan reals   gerçek taraf

Bu muhtemelen anlaşılması en zor şeydir. Süreç boyunca aynı konteynırı (& A) kullanıyoruz. böylece başlangıçta n gerçek sayılarla doldurmak istiyoruz. FFT'den sonra n / 2 karmaşık sayılara sahip olacak. sonra onu ters dönüşüme atarız ve umarım orijinal n sayılarımızı çıkarırız.

şimdi A'nın yapısı karmaşık değerler için bir kurulum. Bu nedenle vDSP'nin gerçek sayıların nasıl paketleneceğini standartlaştırması gerekiyor.

İlk önce n reel sayı üretiyoruz: 1, 2, ..., n

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

Ardından onları A / n 2 kompleksi olarak paketleyelim:

// 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

Bunu elde etmek için A'nın nasıl ayrıldığına bakmanız gerekebilir, belki de belgelere COMPLEX_SPLIT bakın.

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

Daha sonra bir ön hesaplama yapıyoruz.


Maths Bods için Hızlı DSP sınıfı: Fourier teorisi kafanızı almak için uzun zaman alır (şimdi birkaç yıl boyunca bakıp duruyordum)

Bir cisoid:

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

diğer bir deyişle, karmaşık düzlemde birim dairesi üzerinde bir nokta.

Karmaşık sayıları çarptığınızda, açıları ekler. Böylece z ^ k birim dairenin etrafında gezinmeye devam edecektir; z ^ k bir açı k.theta bulunabilir

  • Z1 = 0 + 1i'yi, yani gerçek eksenden çeyrek dönüşü seçin ve z1 ^ 2 z1 ^ 3 z1 ^ 4'ün her birinin bir başka çeyrek dönüş verdiğini fark edin, böylece z1 ^ 4 = 1

  • Z2 = -1, yani yarım dönüş seçin. ayrıca z2 ^ 4 = 1 ama z2 bu noktada 2 döngü tamamladı (z2 ^ 2 de = 1). Böylece z1'i temel frekans olarak ve ilk harmonik olarak z2 olarak düşünebilirsiniz.

  • Benzer şekilde z3 = 'bir devrimin üç çeyreği' noktası, yani -i tam olarak 3 döngüyü tamamlar, ama aslında her seferinde ileriye doğru giden 3/4 ileriye doğru gidiyor her seferinde 1/4 ile aynıdır

yani z3 sadece z1'dir, ancak zıt yöndedir - takma adı verilir

Tam dalgayı tutmak için 4 örnek seçtiğimizden, z2 en yüksek anlamlı frekanstır.

  • z0 = 1 + 0i, z0 ^ (bir şey) = 1, bu DC ofsetidir

4 noktalı sinyalin z0 z1 ve z2'nin lineer kombinasyonu olarak ifade edebilirsiniz. yani bu temel vektörlerine yansıtıyorsunuz

ama duyduğumu duyuyorum "bir sinyalin üzerine bir sinyalin yansıtılması ne anlama geliyor?"

Bunu şu şekilde düşünebilirsiniz: İğne, cisoidin etrafına döner, böylece k numunesinde iğne, k.theta yönünde işaret eder ve uzunluk [k] sinyalidir. Cisoidin frekansı ile eşleşen bir sinyal, sonuçta oluşan şekli bir doğrultuda şişirecektir. Yani tüm katkıları eklerseniz, güçlü bir sonuç elde edersiniz. Frekans neredeyse eşleşiyorsa, çıkıntı daha küçük olacak ve dairenin etrafında yavaşça hareket edecektir. Frekansla eşleşmeyen bir sinyal için, katılımlar birbirini iptal eder.

http://complextoreal.com/tutorials/tutorial-4-fourier-analysis-made-easy-part-1/  sezgisel bir anlayış elde etmenize yardımcı olacaktır.

Ama ana fikir; Eğer {z0, ..., z512} üzerine 1024 numune seçmeyi seçtiysek, z0 thru z512'yi önceden hesaplayabilirdik ve bu ön hesaplama adımı budur.


Bunu gerçek kodda yapıyorsanız, uygulama yüklendiğinde ve tamamlandığında serbest bırakma işlevini çağırdığında bir kez bunu yapmak isteyeceğinizi unutmayın. Çok zaman yapmayın - pahalı.

// 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);

Log2n'yi örneğin 8'e ayarlarsak, bu önceden hesaplanmış değerleri, çözünürlük <= 2 ^ 8 kullanan herhangi bir fft işlevine atabileceğinizi de unutmamak gerekir. Yani (nihai bellek optimizasyonunu istemediğiniz sürece) ihtiyacınız olacak en yüksek çözünürlük için bir set oluşturun ve her şey için kullanın.

Şimdi gerçek dönüşümler, sadece önceden hesaplanan şeyleri kullanıyorlar:

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

Bu noktada A, n / 2 karmaşık sayıları içerecektir, sadece birincisi, karmaşık sayı olarak maskelenen iki gerçek sayıdır (DC sapması, Nyquist #). Belgelere genel bakış bu ambalajı açıklar. Oldukça düzgün - temel olarak, dönüşümün (karmaşık) sonuçlarının, (gerçek, fakat tuhaf şekilde paketlenmiş) girdilerle aynı bellek tabanına sığdırılmasına izin verir.

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

ve tekrar geri dönelim ... orijinal dizimizi A'dan ayırmaya ihtiyacımız var, o zaman biz sadece tam olarak ne yaptığımızın geri geldiğini kontrol etmek, önceden hesaplanan bobinlerimizi serbest bırakmak ve bitti!

Fakat bekle! paketini açmadan önce yapılması gereken son bir şey var:

// 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);

131
2017-11-04 07:41



onun değil 44 onun 43! ve bu daha yüksek bidonlarda çok önemlidir! 22050/512 = 43! - Curnelious
Derinlemesine açıklama. Bunun için başvurulan elma bağlantısını gönderir misiniz? Ben aradım ama beni birden fazla örneğe götürüyor, ve bunu sizin açıklamanızla gerçekten anlamak istiyorum. Teşekkürler! - Nirav Bhatt
Bu harika bir yazı. Koddan geçmek için bir github projesi var mı? - Michael
Merhaba. Tam kodu bir yerlerde görebilir miyiz? Burada referans verilen Apple örneğini bulamıyorum. Teşekkürler - Andrei Filip


İşte gerçek dünyadaki bir örnek: Uzaktan IO ses biriminin girişinde otokorelasyon yapmak için Accelerate'in vDSP fft rutinlerini kullanan bir c ++ parçacığı. Bu çerçeveyi kullanmak oldukça karmaşıktır, ancak dokümantasyon çok kötü.

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);
    }
}

25
2017-08-20 21:09



Harika bir örnek, ancak bu iki işlev için uygulamaların yönünü gösterebilir misiniz: getMaxFramesPerSlice () ve quadInterpolate ()? - CJ Hanson
Maalesef, bir soru daha var ... sesim 16bit lpcm olduğundan, arabelleklerimde tamsayı verileri alıyorum, fft koduyla kullanmak için verimli bir şekilde nasıl değiştirebilirim? - CJ Hanson
@CJ: getMaxFramesPerSlice () işlevi, geri arama her tetiklendiğinde gönderilen kare sayısını almak gibi görünüyor. Bu eşit derecede iyi bir #define olabilirdi bence. - P i
@Ohmu gelen sinyalin otokorelasyonunu kullanarak naif bir saha algılama algoritmasıdır. getMaxFramesPerSlice() olamaz #defined bu durumda her koşuya göre değişebilir. Yöntem aslında karşılık gelen ses birimi özellik erişimcisi için bir sarıcıdır. Bu kod, girişin sıfırlanmasını sağlar çünkü aynı arabellek, aygıtın çıkışına iletilir - sıfırlama, bir geri besleme döngüsünü önler. - Art Gillespie
Ben düşünmüyorum vDSP_zvmags hayali bileşeni gerçekten Nyquist kovanın gerçek bileşeni olduğu için eleman 0'a uygulanmalıdır. Sadece kare yapmamalısın A.realp[0] ve A.imagp[0], ve yok bzero  A.imagp[0]? - pat


Apple'ın FFT Framework'ünün hızlı olduğunu söyleyeyim de ... Bir FFT'nin, doğru perde tespiti elde etmek için nasıl çalıştığını bilmeniz gerekir (örneğin, her bir ardışık FFT'deki faz farkını hesaplamak için, kesin perdeyi bulmak için değil. en domine bin).

Herhangi bir yardımın olup olmadığını bilmiyorum, ama benim Pitch Detector nesneyi tuner uygulamasından yükledim (musicianskit.com/developer.php). İndirme için bir örnek xCode 4 projesi de vardır (böylece uygulamanın nasıl çalıştığını görebilirsiniz).

Örnek bir FFT uygulamasının yüklenmesi üzerinde çalışıyorum - bu yüzden bizi takip etmeye devam edin ve bu gerçekleştikten sonra bunu güncelleyeceğim.

Mutlu kodlar!


12
2018-06-14 23:05



Paylaştığınız için teşekkür ederiz, ancak örneğiniz aşağıdaki hatalarla derlenmiyor: 1). hata: 'yorum' için çelişen türler [3]. 2). Otomatik İlişkilendirme / Otomatik İlişkilendirme / AudioController.m: 92: 32: hata: bildirilmemiş tanımlayıcı 'recordingCallback' kullanımı [3] - Meir
Zip dosyası bağlantıları çalışmıyor. - Nirav Bhatt
github.com/kevmdev/PitchDetectorExample Üzgünüm, tembel oldum ... Ama proje var. Doğru derleme olmalı (en azından birkaç hafta önce son kez yaptım) ama bu gece tekrar kontrol edeceğim! - Kpmurphy91
Swift'de bunun için herhangi bir güncelleme var mı? - iOS Geek


İşte başka bir gerçek dünya örneği: https://github.com/krafter/DetectingAudioFrequency


4
2018-04-28 10:42



Krafter - biliyorum eski, ama senin repo harika! sadece en yüksek frekans yerine en yüksek frekansı bulmanın bir yolu olup olmayacağını merak ediyorum? - Alan_s
Teşekkür ederim! Sorunuzu cevaplamak için - evet yapabilirsiniz. Çıkış dizisinde, frekans olarak indeksler ve büyüklükler olarak değerler vardır. Yani ilk elemanlar en düşük frekans ve son eleman en yüksek olanıdır (veya tersi). - krafter
Fakat gerçek en yüksek frekans varlığı size pek bir şey söylemez, gerçek dünya sesi her zaman tam spektrum içerir, ancak bazı frekanslar sadece zayıftır ve bazıları öne çıkar. Bunu düşün. Ayrıca, sınırlı frekans aralığını sadece algılayabileceğinizi de unutmayın. Nyquist teoremi. Detaylar için cevabımı kontrol edin: stackoverflow.com/a/19966776/468812 - krafter
Tamam harika. Hala sadece 18000hz gibi yüksek frekansı tespit edip edemeyeceğimi görmek istiyorum, diğer yandan daha belirgin gürültü aynı zamanda meydana geliyor. Mümkünse emin değil misiniz? ViewController.mm'deki bu işlevin içinde, maxIndex spektrumda bulunan en yüksek frekansı temsil ediyor mu? Statik Float32 strongestFrequencyHZ (Float32 * arabellek, FFTHelperRef * fftHelper, UInt32 frameSize, Float32 * freqValue) - Alan_s
Hiçbir değişiklik yapmadan benim örneğimi kullanarak, iPhone 4'te 18000hz'i tespit ettim, Audacity kullanarak ton ve SVEN küçük hoparlörleri problemsiz hale getirdim. Teorik olarak 44100 örnek oranı kullanıyorsanız, 22050Hz'e kadar tespit edebilirsiniz. Bugün 19000Hz ve hatta 20 000Hz'yi de tespit ediyordum. Kafamdaki bazı ağrı da tespit edildi :)) - krafter