2012-05-16 4 views
10

मैं एक साधारण सी अनुप्रयोग विकसित करने की कोशिश कर रहा हूं जो एक डब्ल्यूएवी-फ़ाइल में दिए गए टाइमस्टैंप पर एक निश्चित आवृत्ति सीमा पर 0-100 से मूल्य दे सकता है।डब्ल्यूएवी-फाइल विश्लेषण सी (libsndfile, fftw3)

उदाहरण: मेरे पास 44.1kHz (सामान्य एमपी 3 फ़ाइल) की आवृत्ति रेंज है और मैं उस सीमा को श्रेणियों की संख्या (0 से शुरू) में विभाजित करना चाहता हूं। मैं तो, प्रत्येक रेंज के आयाम प्राप्त करने की आवश्यकता 0 से 100 के

मैं अब तक क्या प्रबंधित किया है करने के लिए किया जा रहा है:

का उपयोग libsndfile अब मैं एक WAV- के डेटा को पढ़ने में सक्षम हूँ फ़ाइल।

infile = sf_open(argv [1], SFM_READ, &sfinfo); 

float samples[sfinfo.frames]; 

sf_read_float(infile, samples, 1); 

हालांकि, एफएफटी की मेरी समझ सीमित है। लेकिन मुझे पता है कि मुझे आवश्यक श्रेणियों में आयाम प्राप्त करने के लिए जरूरी है। लेकिन मैं यहां से कैसे आगे बढ़ूं? मुझे पुस्तकालय FFTW-3 मिला, जो उद्देश्य के लिए उपयुक्त प्रतीत होता है।

मैं यहाँ कुछ मदद मिली: https://stackoverflow.com/a/4371627/1141483

और FFTW ट्यूटोरियल यहाँ को देखा: http://www.fftw.org/fftw2_doc/fftw_2.html

लेकिन जैसा कि मैंने FFTW के व्यवहार के बारे में अनिश्चित हूँ, मैं यहाँ से प्रगति के लिए पता नहीं है ।

और एक और सवाल, मान लीजिए कि आप libsndfile का उपयोग करते हैं: यदि आप पढ़ने को एकल चैनल (स्टीरियो फ़ाइल के साथ) करने के लिए मजबूर करते हैं और फिर नमूने पढ़ते हैं। क्या आप वास्तव में केवल कुल फ़ाइल के नमूने के आधा पढ़ रहे होंगे? उनमें से आधा चैनल 1 से है, या स्वचालित रूप से उनको फ़िल्टर करता है?

आपकी मदद के लिए धन्यवाद एक टन।

संपादित करें: मेरी कोड यहाँ देखा जा सकता:

double blackman_harris(int n, int N){ 
double a0, a1, a2, a3, seg1, seg2, seg3, w_n; 
a0 = 0.35875; 
a1 = 0.48829; 
a2 = 0.14128; 
a3 = 0.01168; 

seg1 = a1 * (double) cos(((double) 2 * (double) M_PI * (double) n)/((double) N - (double) 1)); 
seg2 = a2 * (double) cos(((double) 4 * (double) M_PI * (double) n)/((double) N - (double) 1)); 
seg3 = a3 * (double) cos(((double) 6 * (double) M_PI * (double) n)/((double) N - (double) 1)); 

w_n = a0 - seg1 + seg2 - seg3; 
return w_n; 
} 

int main (int argc, char * argv []) 
{ char  *infilename ; 
SNDFILE  *infile = NULL ; 
FILE  *outfile = NULL ; 
SF_INFO  sfinfo ; 


infile = sf_open(argv [1], SFM_READ, &sfinfo); 

int N = pow(2, 10); 

fftw_complex results[N/2 +1]; 
double samples[N]; 

sf_read_double(infile, samples, 1); 


double normalizer; 
int k; 
for(k = 0; k < N;k++){ 
    if(k == 0){ 

     normalizer = blackman_harris(k, N); 

    } else { 
     normalizer = blackman_harris(k, N); 
    } 

} 

normalizer = normalizer * (double) N/2; 



fftw_plan p = fftw_plan_dft_r2c_1d(N, samples, results, FFTW_ESTIMATE); 

fftw_execute(p); 


int i; 
for(i = 0; i < N/2 +1; i++){ 
    double value = ((double) sqrtf(creal(results[i])*creal(results[i])+cimag(results[i])*cimag(results[i]))/normalizer); 
    printf("%f\n", value); 

} 



sf_close (infile) ; 

return 0 ; 
} /* main */ 

उत्तर

13

खैर यह सब आवृत्ति रेंज आप के बाद कर रहे हैं पर निर्भर करता है। एक एफएफटी 2^एन नमूनों को ले कर काम करता है और आपको 2^(एन -1) असली और काल्पनिक संख्या प्रदान करता है। मुझे यह मानना ​​है कि मैं वास्तव में इन मूल्यों का प्रतिनिधित्व करने के बारे में बहुत आलसी हूं (मुझे एक दोस्त मिला है जिसने मुझे ऋण के बदले मेरे साथ यह सब करने का वादा किया है जब मैंने वित्तीय मुद्दों पर उन्हें बनाया था;)) एक सर्कल के चारों ओर एक कोण। प्रभावी रूप से वे आपको प्रत्येक आवृत्ति बिन के लिए एक साइन और कोसाइन के कोण कोण पैरामीटर प्रदान करते हैं, जिससे मूल 2^एन नमूने पूरी तरह से पुनर्निर्मित किए जा सकते हैं।

वैसे भी इसका बड़ा लाभ है कि आप असली और काल्पनिक भागों (sqrtf ((असली * असली) + (कल्पना * कल्पना) की euclidean दूरी ले कर परिमाण की गणना कर सकते हैं)। यह आपको एक असामान्य दूरी मूल्य प्रदान करता है। इस मान का उपयोग प्रत्येक आवृत्ति बैंड के लिए एक परिमाण बनाने के लिए किया जा सकता है।

तो आइए 10 एफएफटी (2^10) ऑर्डर करें। आप 1024 नमूने इनपुट करते हैं। आप उन नमूनों को एफएफटी करते हैं और आपको 512 काल्पनिक और वास्तविक मान वापस मिलते हैं (उन मानों का विशेष क्रम आपके द्वारा उपयोग किए जाने वाले एफएफटी एल्गोरिदम पर निर्भर करता है)। तो इसका मतलब है कि 44.1 किलोहर्ट्ज़ ऑडियो फ़ाइल के लिए प्रत्येक बिन 44100/512 हर्ट्ज या ~ 86Hz प्रति बिन का प्रतिनिधित्व करता है।

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

असल में आपको आवश्यक डेटा प्राप्त करने के लिए आवृत्ति डिब्बे और समय/स्थानिक रिज़ॉल्यूशन को ट्यून करने की आवश्यकता होगी।

पहले नामकरण का थोड़ा सा। 1024 बार डोमेन नमूने जिन्हें मैंने पहले संदर्भित किया था, को आपकी खिड़की कहा जाता है। आम तौर पर इस तरह की प्रक्रिया करने पर आप अगले 1024 नमूने को एफएफटी प्राप्त करने के लिए विंडो को कुछ मात्रा में स्लाइड करना चाहेंगे। करने के लिए स्पष्ट बात नमूना 0-> 1023, फिर 1024-> 2047, और आगे लेना होगा। दुर्भाग्यवश यह सर्वोत्तम परिणाम नहीं देता है। आदर्श रूप में आप कुछ डिग्री तक खिड़कियों को ओवरलैप करना चाहते हैं ताकि आपको समय के साथ एक आसान आवृत्ति परिवर्तन मिल सके। आमतौर पर लोग खिड़की को आधे खिड़की के आकार से स्लाइड करते हैं। यानी आपकी पहली विंडो 0-> 1023 दूसरी 512-> 1535 और इतनी आगे और आगे होगी।

अब यह एक और समस्या लाता है। हालांकि यह जानकारी सही उलटा एफएफटी सिग्नल पुनर्निर्माण के लिए प्रदान करती है, यह आपको एक समस्या के साथ छोड़ देती है जो आवृत्तियों को कुछ हद तक आसपास के डिब्बे में रिसाव करता है। इस मुद्दे को हल करने के लिए कुछ गणितज्ञ (मेरे से कहीं ज्यादा बुद्धिमान) window function की अवधारणा के साथ आए। विंडो फ़ंक्शन आवृत्ति डोमेन में कहीं अधिक बेहतर आवृत्ति अलगाव प्रदान करता है, हालांकि समय डोमेन में जानकारी का नुकसान होता है (यानी विंडो फ़ंक्शन, AFAIK का उपयोग करने के बाद सिग्नल को पूरी तरह से पुन: निर्माण करना असंभव है)।

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

हालांकि जैसा कि मैंने पहले उल्लेख किया था कि एफएफटी आपको एक असामान्य स्पेक्ट्रम प्रदान करता है। स्पेक्ट्रम को सामान्य करने के लिए (यूक्लिडियन दूरी गणना के बाद) आपको सभी मानों को सामान्यीकरण कारक से विभाजित करने की आवश्यकता होती है (मैं अधिक जानकारी here में जाता हूं)।

यह सामान्यीकरण आपको 0 और 1 के बीच एक मान प्रदान करेगा। इसलिए आप 0 से 100 स्केल प्राप्त करने के लिए आसानी से 100 से अधिक मूल्य प्राप्त कर सकते हैं।

हालांकि, यह समाप्त नहीं होता है। इससे प्राप्त स्पेक्ट्रम बल्कि असंतुष्ट है। ऐसा इसलिए है क्योंकि आप एक रैखिक पैमाने का उपयोग कर परिमाण को देख रहे हैं। दुर्भाग्यवश मानव कान एक लॉगरिदमिक पैमाने का उपयोग सुनता है। इससे स्पेक्ट्रोग्राम/स्पेक्ट्रम कैसा दिखता है इसके साथ समस्याएं होती हैं।

इसे पाने के लिए आपको इन 0 से 1 मानों को परिवर्तित करने की आवश्यकता है (मैं इसे 'x' कहूंगा) डेसिबल पैमाने पर। मानक परिवर्तन 20.0f * log10f(x) है। यह आपको एक मूल्य प्रदान करेगा जिससे 1 को 0 और 0 में परिवर्तित कर दिया गया है -इनफिनिटी में परिवर्तित हो गया है। आपके परिमाण अब उचित लघुगणकीय पैमाने में हैं। हालांकि यह हमेशा सहायक नहीं है।

इस बिंदु पर आपको मूल नमूना बिट गहराई को देखने की आवश्यकता है। 16-बिट नमूनाकरण पर आपको 32767 और -32768 के बीच एक मान मिलता है। इसका मतलब है कि आपका dynamic range fabsf (20.0f * log10f (1.0f/65536.0f)) या ~ 96.33 डीबी है। तो अब हमारे पास यह मूल्य है।

ऊपर दिए गए डीबी गणना से प्राप्त मूल्यों को लें। इसे -96.33 मान इसमें जोड़ें। जाहिर है अधिकतम आयाम (0) अब 96.33 है। अब उसी मूल्य से didivde और आप अब से अनंत से 1.0f तक एक मूल्य है। निचले सिरे को 0 पर क्लैंप करें और अब आपके पास 0 से 1 तक की रेंज है और 100 से गुणा करें और आपके पास आपकी अंतिम 0 से 100 रेंज है।

और यह मूल रूप से इरादे से अधिक राक्षस पोस्ट है, लेकिन आपको एक इनपुट सिग्नल के लिए एक अच्छा स्पेक्ट्रम/स्पेक्ट्रोग्राम उत्पन्न करने के तरीके में अच्छी ग्राउंडिंग देना चाहिए।

और साँस लेने

इसके अलावा (मूल पोस्टर के अलावा अन्य लोग हैं, जो पहले से ही यह पाया गया है के लिए) पढ़ने:

Converting an FFT to a spectogram

संपादित: जैसा कि एक अलग रूप में मैं चुंबन पाया एफएफटी का उपयोग करना बहुत आसान है, मेरा कोड अग्रेषित एफएफटी करने के लिए निम्नानुसार है:

CFFT::CFFT(unsigned int fftOrder) : 
    BaseFFT(fftOrder) 
{ 
    mFFTSetupFwd = kiss_fftr_alloc(1 << fftOrder, 0, NULL, NULL); 
} 

bool CFFT::ForwardFFT(std::complex<float>* pOut, const float* pIn, unsigned int num) 
{ 
    kiss_fftr(mFFTSetupFwd, pIn, (kiss_fft_cpx*)pOut); 
    return true; 
} 
+0

गोज़, आप गंभीरता से मेरे नायक हैं। मदद के लिए एक लाख धन्यवाद। मैं इसे अभी पढ़ रहा हूं, और कल आपने जो वर्णन किया है उसे लागू करने का प्रयास करूंगा :) –

+0

@ थॉमसकोबरबरम: कोई जांच नहीं :) – Goz

+0

हाय गोज़, मैंने अभी तक अपना कोड पोस्ट कर दिया है। मैंने अभी तक ओवरलैपिंग लागू नहीं किया है। मैं बस कुछ सामान्य मूल्यों को शुरू करने की कोशिश कर रहा हूं। मैं नहीं देख सकता कि मैं क्या गलत कर रहा हूं? मुझे अभी भी इन बड़ी संख्याएं मिलती हैं, जो समझ में आता है क्योंकि सामान्यीकृत मान कम है ... लेकिन यह किसी भी तरह गलत होना चाहिए? –

संबंधित मुद्दे