2016-01-05 11 views
8

मैं पायथन में "डीएसपी-जैसे" सिग्नल प्रोसेसर को लागू करने की योजना बना रहा हूं। इसे ALSA के माध्यम से ऑडियो के छोटे टुकड़े कैप्चर करना चाहिए, उन्हें संसाधित करना चाहिए, फिर उन्हें ALSA के माध्यम से वापस चलाएं।पाइथन में रीयलटाइम सिग्नल प्रोसेसिंग को कार्यान्वित करें - लगातार ऑडियो कैप्चर कैसे करें?

चीजें शुरू करने के लिए, मैंने निम्नलिखित (बहुत सरल) कोड लिखा था।

import alsaaudio 

inp = alsaaudio.PCM(alsaaudio.PCM_CAPTURE, alsaaudio.PCM_NORMAL) 
inp.setchannels(1) 
inp.setrate(96000) 
inp.setformat(alsaaudio.PCM_FORMAT_U32_LE) 
inp.setperiodsize(1920) 

outp = alsaaudio.PCM(alsaaudio.PCM_PLAYBACK, alsaaudio.PCM_NORMAL) 
outp.setchannels(1) 
outp.setrate(96000) 
outp.setformat(alsaaudio.PCM_FORMAT_U32_LE) 
outp.setperiodsize(1920) 

while True: 
    l, data = inp.read() 
    # TODO: Perform some processing. 
    outp.write(data) 

समस्या यह है कि ऑडियो "स्टटर" और अंतहीन नहीं है। मैंने पीसीएम मोड के साथ प्रयोग करने की कोशिश की, इसे या तो पीसीएम_एएसवाईएनसी या पीसीएम_नॉनब्लॉक पर सेट किया, लेकिन समस्या बनी हुई है। मुझे लगता है कि समस्या यह है कि "inp.read()" में दो बाद की कॉल के बीच "नमूने" खो गए हैं।

क्या पाइथन में ऑडियो "लगातार" कैप्चर करने का कोई तरीका है (अधिमानतः बिना "विशिष्ट"/"गैर-मानक" पुस्तकालयों की आवश्यकता के बिना)? मैं सिग्नल हमेशा कुछ बफर में "पृष्ठभूमि में" कब्जा कर लेना चाहता हूं, जिसमें से मैं कुछ "क्षणिक स्थिति" पढ़ सकता हूं, जबकि समय के दौरान भी ऑडियो को बफर में पकड़ा जा रहा है, जब मैं अपने पढ़ने के संचालन करता हूं । इसे कैसे प्राप्त किया जा सकता है?

भले ही मैं ऑडियो को कैप्चर करने के लिए समर्पित प्रक्रिया/थ्रेड का उपयोग करता हूं, फिर भी इस प्रक्रिया/थ्रेड को कम से कम (1) स्रोत से ऑडियो पढ़ना होगा, (2) फिर इसे कुछ बफर में रखें (जिसमें से "सिग्नल प्रोसेसिंग" प्रक्रिया/थ्रेड तब पढ़ता है)। इसलिए ये दो ऑपरेशन समय पर अनुक्रमिक होंगे और इस प्रकार नमूने खो जाएंगे। मैं इससे कैसे बचूं?

आपकी सलाह के लिए बहुत बहुत धन्यवाद!

संपादित करें 2: अब मैं इसे चला रहा हूं।

import alsaaudio 
from multiprocessing import Process, Queue 
import numpy as np 
import struct 

""" 
A class implementing buffered audio I/O. 
""" 
class Audio: 

    """ 
    Initialize the audio buffer. 
    """ 
    def __init__(self): 
     #self.__rate = 96000 
     self.__rate = 8000 
     self.__stride = 4 
     self.__pre_post = 4 
     self.__read_queue = Queue() 
     self.__write_queue = Queue() 

    """ 
    Reads audio from an ALSA audio device into the read queue. 
    Supposed to run in its own process. 
    """ 
    def __read(self): 
     inp = alsaaudio.PCM(alsaaudio.PCM_CAPTURE, alsaaudio.PCM_NORMAL) 
     inp.setchannels(1) 
     inp.setrate(self.__rate) 
     inp.setformat(alsaaudio.PCM_FORMAT_U32_BE) 
     inp.setperiodsize(self.__rate/50) 

     while True: 
      _, data = inp.read() 
      self.__read_queue.put(data) 

    """ 
    Writes audio to an ALSA audio device from the write queue. 
    Supposed to run in its own process. 
    """ 
    def __write(self): 
     outp = alsaaudio.PCM(alsaaudio.PCM_PLAYBACK, alsaaudio.PCM_NORMAL) 
     outp.setchannels(1) 
     outp.setrate(self.__rate) 
     outp.setformat(alsaaudio.PCM_FORMAT_U32_BE) 
     outp.setperiodsize(self.__rate/50) 

     while True: 
      data = self.__write_queue.get() 
      outp.write(data) 

    """ 
    Pre-post data into the output buffer to avoid buffer underrun. 
    """ 
    def __pre_post_data(self): 
     zeros = np.zeros(self.__rate/50, dtype = np.uint32) 

     for i in range(0, self.__pre_post): 
      self.__write_queue.put(zeros) 

    """ 
    Runs the read and write processes. 
    """ 
    def run(self): 
     self.__pre_post_data() 
     read_process = Process(target = self.__read) 
     write_process = Process(target = self.__write) 
     read_process.start() 
     write_process.start() 

    """ 
    Reads audio samples from the queue captured from the reading thread. 
    """ 
    def read(self): 
     return self.__read_queue.get() 

    """ 
    Writes audio samples to the queue to be played by the writing thread. 
    """ 
    def write(self, data): 
     self.__write_queue.put(data) 

    """ 
    Pseudonymize the audio samples from a binary string into an array of integers. 
    """ 
    def pseudonymize(self, s): 
     return struct.unpack(">" + ("I" * (len(s)/self.__stride)), s) 

    """ 
    Depseudonymize the audio samples from an array of integers into a binary string. 
    """ 
    def depseudonymize(self, a): 
     s = "" 

     for elem in a: 
      s += struct.pack(">I", elem) 

     return s 

    """ 
    Normalize the audio samples from an array of integers into an array of floats with unity level. 
    """ 
    def normalize(self, data, max_val): 
     data = np.array(data) 
     bias = int(0.5 * max_val) 
     fac = 1.0/(0.5 * max_val) 
     data = fac * (data - bias) 
     return data 

    """ 
    Denormalize the data from an array of floats with unity level into an array of integers. 
    """ 
    def denormalize(self, data, max_val): 
     bias = int(0.5 * max_val) 
     fac = 0.5 * max_val 
     data = np.array(data) 
     data = (fac * data).astype(np.int64) + bias 
     return data 

debug = True 
audio = Audio() 
audio.run() 

while True: 
    data = audio.read() 
    pdata = audio.pseudonymize(data) 

    if debug: 
     print "[PRE-PSEUDONYMIZED] Min: " + str(np.min(pdata)) + ", Max: " + str(np.max(pdata)) 

    ndata = audio.normalize(pdata, 0xffffffff) 

    if debug: 
     print "[PRE-NORMALIZED] Min: " + str(np.min(ndata)) + ", Max: " + str(np.max(ndata)) 
     print "[PRE-NORMALIZED] Level: " + str(int(10.0 * np.log10(np.max(np.absolute(ndata))))) 

    #ndata += 0.01 # When I comment in this line, it wreaks complete havoc! 

    if debug: 
     print "[POST-NORMALIZED] Level: " + str(int(10.0 * np.log10(np.max(np.absolute(ndata))))) 
     print "[POST-NORMALIZED] Min: " + str(np.min(ndata)) + ", Max: " + str(np.max(ndata)) 

    pdata = audio.denormalize(ndata, 0xffffffff) 

    if debug: 
     print "[POST-PSEUDONYMIZED] Min: " + str(np.min(pdata)) + ", Max: " + str(np.max(pdata)) 
     print "" 

    data = audio.depseudonymize(pdata) 
    audio.write(data) 

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

संपादित करें 3: मुझे पता चला कि मेरे एल्गोरिदम (यहां शामिल नहीं) काम करते हैं जब मैं उन्हें फ़ाइलों को लहरों पर लागू करता हूं। तो समस्या वास्तव में वास्तव में ALSA एपीआई को उबालने लगती है।

संपादित करें 4: मुझे अंत में समस्याएं मिलीं। वे निम्नलिखित थे।

पहला - ALM चुपचाप पीसीएम_FORMAT_U8_LE को पीसीएम_FORMAT_U8_LE पर अनुरोध करने के लिए चुपचाप "वापस आ गया", इस प्रकार मैंने यह मानकर गलत तरीके से डेटा का अर्थ दिया कि प्रत्येक नमूना 4 बाइट चौड़ा था। जब मैं पीसीएम_FORMAT_S32_LE का अनुरोध करता हूं तो यह काम करता है।

2 - ALSA उत्पादन बाइट्स में इस अवधि के आकार की उम्मीद करने, भले ही वे स्पष्ट रूप बताया गया है कि यह फ्रेम विनिर्देश में में आशा की जाती है लगता है। तो यदि आप 32 बिट नमूना गहराई का उपयोग करते हैं तो आपको आउटपुट के लिए चार गुना अधिक आकार निर्धारित करना होगा।

तीसरा - यहां तक ​​कि पायथन में (जहां "वैश्विक दुभाषिया ताला" है), थ्रेड की तुलना में प्रक्रिया धीमी होती है। आप धागे को बदलकर बहुत कम विलंब प्राप्त कर सकते हैं, क्योंकि I/O थ्रेड मूल रूप से कुछ भी नहीं करते हैं जो कम्प्यूटेशनल रूप से गहन है।

+0

एक कतार को पढ़ने और पोस्ट करने के लिए धागे का उपयोग करना चाहिए। 'पीसीएम' में 'बफररोडिज़ेज़' द्वारा नियंत्रित बफर होता है (यह 32 फ्रेम तक डिफ़ॉल्ट रूप से प्रतीत होता है) जो आपको डेटा को रिटर्न पोस्ट करने का समय देता है। – tdelaney

+0

मुझे लगता है कि समस्या यह है कि "पढ़ा()" केवल ऑडियो डिवाइस से पढ़ता है जबकि यह चलता है। यदि यह लौटाता है, तो पढ़ना ऑपरेशन समाप्त हो जाता है (अन्यथा यह किसी भी सार्थक डेटा को वापस नहीं कर सका)।यहां तक ​​कि यदि मेरे पास दूसरा धागा चल रहा है, तो "पढ़ा()" कर रहा है, फिर लौटाए गए डेटा को एक बफर में जोड़ना, यह संलग्न होने पर "पढ़ा नहीं जाएगा" और इसलिए कैप्चर में एक अंतर होगा। –

+0

वाह। फिर वह इंटरफ़ेस गंभीर रूप से टूटा हुआ है। इंटरफेस जिनमें पारंपरिक अवरोधन/गैर-अवरुद्ध मोड हैं, आपके द्वारा वर्णित कारणों के लिए मध्यवर्ती बफर की आवश्यकता होती है। एक वास्तविक समय इंटरफ़ेस डेटा उत्पन्न होने से पहले प्रीस्टस्टिंग बफर की आवश्यकता होती है। लेकिन 'अउडियो' इस तरह से काम नहीं कर रहा है। मैं कल्पना नहीं कर सकता कि यह मॉड्यूल बफरिंग के बिना कैसे काम करेगा। तो ...., क्या आप निश्चित हैं कि यह कैसे काम करता है या आप अनुमान लगा रहे हैं? मुझे लगता है कि यह एक समय में एक्स फ्रेम को बफर करता है और यदि आप अगली एक्स आने के समय तक इसे नहीं पढ़ते हैं, तो यह खो जाता है। बस मेरे हिस्से पर एक अनुमान! – tdelaney

उत्तर

2

जब आप

  1. डेटा में से एक हिस्सा,
  2. लिखने डेटा में से एक हिस्सा,
  3. तो डेटा की दूसरी हिस्सा के लिए प्रतीक्षा पढ़ने के लिए पढ़ते हैं,

तो आउटपुट डिवाइस का बफर खाली हो जाएगा यदि दूसरा खंड पहले खंड से छोटा नहीं है।

वास्तविक प्रसंस्करण शुरू करने से पहले आपको आउटपुट डिवाइस के बफर को चुप्पी के साथ भरना चाहिए। फिर इनपुट या आउटपुट प्रोसेसिंग में छोटी देरी कोई फर्क नहीं पड़ता।

+0

बहुत बहुत धन्यवाद! मुझे पता चला कि ऑडियो के निरंतर होने के लिए मुझे अपने आउटपुट बफर में कम से कम चार पूर्ण अवधि की आवश्यकता है। चूंकि यह सिस्टम से सिस्टम (और शायद सिस्टम लोड के साथ भी बदल सकता है) से अलग है, इसलिए मैं इसे कॉन्फ़िगर करने योग्य बना दूंगा परिवर्तनीय। बड़ा बफर -> उच्च विलंबता, लेकिन "स्टटरिंग" का कम मौका, छोटे बफर -> कम विलंबता, लेकिन "स्टटरिंग" का उच्च अवसर। बाद में, मैं गतिशील रूप से बढ़ते बफर द्वारा सिस्टम को "ऑटो-ट्यून" भी कर सकता हूं आवेदक पर आउटपुट बफर "अवरुद्ध से दूर" होने पर अंडर्रन्स पर आकार और इसे कम करता है आयन नए नमूने पोस्ट कर रहा है। –

2

आप क्या कर सकते हैं कि सभी मैन्युअल रूप से, @CL के रूप में उसकी/उसके answer में सलाह देते हैं, लेकिन मैं सिर्फ बजाय GNU Radio का उपयोग कर की सलाह देते हैं:

यह एक रूपरेखा है कि सभी "हो रही है छोटे-छोटे टुकड़ों कर का ख्याल रखता है है अपने एल्गोरिदम में नमूने के बाहर और बाहर "; यह बहुत अच्छी तरह से स्केल करता है, और आप पाइथन या सी ++ में अपना सिग्नल प्रोसेसिंग लिख सकते हैं।

वास्तव में, यह ऑडियो स्रोत और एक ऑडियो सिंक के साथ आता है जो सीधे एएलएसए से बात करता है और केवल निरंतर नमूने देता है/लेता है। मैं जीएनयू रेडियो के Guided Tutorials के माध्यम से पढ़ने की सिफारिश करता हूं; वे स्पष्ट रूप से समझाते हैं कि ऑडियो एप्लिकेशन के लिए आपके सिग्नल प्रोसेसिंग को क्या करना आवश्यक है।

Flow graph

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

एक वास्तव में कम से कम प्रवाह ग्राफ लगेगा।

फ़ाइल और wav फ़ाइल सिंक और सूत्रों का कहना है, फिल्टर, resamplers, एम्पलीफायरों (ठीक है, मल्टीप्लायरों), मददगार चीजें जैसे है ...

+0

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

+0

"स्टैंडअलोन"! = पायथन, मैं बहस करता हूं। वास्तव में, जीएनयू रेडियो आपके पायथन प्रोग्राम की निर्भरता बन जाएगा, लेकिन यह वास्तव में है: आपके प्रोग्राम को काम करने के लिए एक लाइब्रेरी स्थापित करने की आवश्यकता है, आपके पाइथन को एएलएसए का समर्थन करने की आवश्यकता है। –

+0

ठीक है, एएलएसए "सीधे" के साथ इंटरफ़ेस करना इतना कठिन नहीं हो सकता है, है ना? मुझे लगता है कि समस्या यह है कि नमूने कुछ "अस्पष्ट डेटा प्रारूप" में हैं जो मैं सही तरीके से इलाज नहीं करता हूं। तुम क्या सोचते हो? –

0

मैं अंत में समस्या नहीं पाई गई। वे निम्नलिखित थे।

पहला - ALM चुपचाप पीसीएम_FORMAT_U8_LE को पीसीएम_FORMAT_U8_LE पर अनुरोध करने के लिए चुपचाप "वापस आ गया", इस प्रकार मैंने यह मानकर गलत तरीके से डेटा का अर्थ दिया कि प्रत्येक नमूना 4 बाइट चौड़ा था। जब मैं पीसीएम_FORMAT_S32_LE का अनुरोध करता हूं तो यह काम करता है।

दूसरा - ALSA आउटपुट बाइट्स में अवधि के आकार की अपेक्षा करता है, भले ही वे स्पष्ट रूप से बताते हैं कि यह विनिर्देश में फ्रेम में अपेक्षित है। तो यदि आप 32 बिट नमूना गहराई का उपयोग करते हैं तो आपको आउटपुट के लिए चार गुना अधिक आकार निर्धारित करना होगा।

तीसरा - यहां तक ​​कि पायथन में (जहां "वैश्विक दुभाषिया ताला" है), थ्रेड की तुलना में प्रक्रिया धीमी होती है। आप धागे को बदलकर बहुत कम विलंब प्राप्त कर सकते हैं, क्योंकि I/O थ्रेड मूल रूप से कुछ भी नहीं करते हैं जो कम्प्यूटेशनल रूप से गहन है।

ऑडियो अंतहीन और निर्विवाद है, लेकिन विलंबता बहुत अधिक है।

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