हम VB.Net पर AGC (स्वचालित वॉल्यूम नियंत्रण) प्रोग्राम करते हैं

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

सिद्धांत

सामान्य सिद्धांत


कार्य, वास्तव में, बहुत सरल है: वर्तमान वॉल्यूम स्तर को मापें, आवश्यक लाभ की गणना करें और इसके द्वारा इनपुट सिग्नल के वर्तमान मूल्य को गुणा करें। वह जो 44.1 kHz या उससे अधिक की आवृत्ति के साथ आता है। गणना की सुविधा के लिए, हम प्रति यूनिट सिग्नल का अधिकतम संभव मूल्य लेंगे। यदि हम हमेशा अधिकतम आयाम को बढ़ाना चाहते हैं, तो लाभ की गणना k = 1 / v तक कम हो जाती है, जहां v वर्तमान वॉल्यूम स्तर है, और k लाभ है। यह स्पष्ट है, लेकिन v की गणना कैसे करें? आइए देखें कि तैयार एजीसी कैसे करता है। यहाँ इसका ब्लॉक आरेख है:



पहले तीन ब्लॉक वर्तमान वॉल्यूम स्तर की गणना करने में लगे हुए हैं।
गीतात्मक विषयांतर
कुछ ब्लॉकों को "ब्लैक बॉक्स" के रूप में चित्रित किया गया है, क्योंकि उनके हार्डवेयर समकक्षों का उपकरण मेरे लिए अज्ञात है। उदाहरण के लिए, एक रेक्टिफायर के रूप में एक डायोड ब्रिज उपयुक्त नहीं है, यदि केवल इसलिए कि यह आपको इनपुट ग्राउंड को सीधे सर्किट ग्राउंड से कनेक्ट करने की अनुमति नहीं देता है। लेकिन हमारे लिए यह महत्वपूर्ण नहीं है, क्योंकि हमारा एजीसी सॉफ्टवेयर है। हम जारी रखते हैं।
V का वैध मान 0 से 1 तक है, है ना? नहीं। हम इसे विभाजित करेंगे, लेकिन 0 से विभाजित करना असंभव है! इसलिए, आपको उदाहरण के लिए, 100 में कुछ अधिकतम लाभ दर्ज करने की आवश्यकता है, जो मौन के मिनटों में कार्य करेगा। अर्थात्, v का स्वीकार्य मान 1 / किमीमैक्स से 1. तक 0.01 से 1 होगा।

अब हम याद करते हैं कि इनपुट वैल्यू -1 से लेकर 1. तक है। हमें v के लिए नकारात्मक मान की आवश्यकता नहीं है, इसलिए हम उन्हें एक रेक्टिफायर का उपयोग करके सकारात्मक में बदल देते हैं। यदि रेक्टिफायर का आउटपुट 0.01 से कम मूल्य का उत्पादन करता है, तो हम इसे 0.01 से ठीक करते हैं। यह सीमक है।

अब मजा हिस्सा है। इनपुट सिग्नल फ़्रीक्वेंसी की एक विस्तृत श्रृंखला पर उतार-चढ़ाव करता है। रेक्टिफायर और सीमक के बाद, इस रेंज का और भी विस्तार हुआ है। और हमें सुचारू रूप से काम करने के लिए एजीसी की आवश्यकता है, तेज छलांग लगाने की अनुमति केवल क्षणों में जब जोर से ध्वनि अचानक इनपुट पर दिखाई देती है। अन्य मामलों में, हमें उच्च कंपन आवृत्तियों की आवश्यकता नहीं है k । हम उनसे छुटकारा पा लेंगे। "क्या? फूरियर रैंक में गोता? ओह, यो! ”- युवा पाठक अगाध है। कोई घबराहट नहीं! एक सरल विधि भी हमारे उद्देश्य के लिए उपयुक्त है। यदि हमारे एजीसी हार्डवेयर थे, तो हम एक चौरसाई संधारित्र का उपयोग करेंगे। और एक संधारित्र का गणितीय अवतार घातीय क्षीणन है। यहाँ यह कैसे काम करता है। पत्र यू द्वारा सीमक के उत्पादन में मूल्य को नकारें। हमें v के पिछले मान की भी आवश्यकता होगी। अगर u> v , तो हम कूदते हैं: v = u ; अन्यथा, हम चुपचाप v को u : v = v - b * (v - u) तक खींच लेते हैं। यह बी ही निर्धारित करता है कि हम चुपचाप कैसे खींचते हैं। यह शून्य से थोड़ा ही बड़ा है, इसलिए प्रत्येक चरण के साथ हम एक अल्प राशि से v कम करेंगे। आप AGC को इस तरह से शुरू करने से पहले b की गणना कर सकते हैं: b = 1 - 10 ^ (-1 / (sr * rt)) , जहाँ sr इनपुट सिग्नल की सैंपलिंग आवृत्ति (44100 हर्ट्ज या इस तरह) है, आरटी लगभग अनुमानित प्रतिक्रिया समय (समायोज्य है) पैरामीटर, डिफ़ॉल्ट 10 सेकंड)। दूसरे शब्दों में, यदि तेज ध्वनि के बाद चुप्पी के आरटी सेकंड हैं, तो इस समय के दौरान v को यू के इतना करीब खींचा जाएगा कि उनके बीच का अंतर 10 गुना कम हो जाएगा। इसलिए हमें एक असममित कम-पास फ़िल्टर (AFF) मिला। असममित क्योंकि यह तुरंत यू में वृद्धि का जवाब देता है, और धीमी गति से यू में कमी का जवाब देता है। 10 सेकंड के प्रतिक्रिया समय का अर्थ है कि फ़िल्टर 0.1 हर्ट्ज से ऊपर की आवृत्तियों को दबा देता है और शेष को पास करता है। यह, ज़ाहिर है, एक बहुत ही क्रूड स्टेटमेंट है, वास्तव में, फ़िल्टरिंग के लिए कोई स्पष्ट सीमा नहीं है। हालांकि, 0.1 हर्ट्ज की आवृत्ति श्रव्य सीमा से परे है, यानी रेक्टिफायर के क्लिक लगभग फिल्टर में नहीं घुसेंगे और मानक श्रोता को परेशान करेंगे। लेकिन केवल तब जब कोई छलांग न हो।

कतरन


अब छलांग लगाने के दौरान क्या होता है, इस पर ध्यान दें। आमतौर पर वे कुछ नमूने एक पंक्ति में रखते हैं। इस समय, फ़िल्टर का असममित "वाल्व" खुला है, और लाभ तेजी से एक असामान्य मात्रा में समायोजित हो गया है। नतीजतन, इन कई नमूनों के मूल्य "छत" के खिलाफ आराम करते हैं और ध्वनि तरंग का आकार विकृत होता है। अंग्रेजी में, इसे क्लिपिंग कहा जाता है, और इसे घरघराहट के रूप में सुना जाता है। यहां एक ध्वनि संपादक में ऐसा दिखता है:



Anticlipping


क्या हम किसी तरह से लड़ सकते हैं? हां। और इसमें हमें मदद मिलेगी कि हमारा एजीसी वास्तविक समय में नहीं, बल्कि पहले से दर्ज आंकड़ों पर काम करेगा। एक छलांग लगाने के बाद, हम आसानी से समय पर वापस जा सकते हैं और कई पिछले नमूनों के लिए लाभ को ठीक कर सकते हैं। कितनी दूर जाना चाहिए? पहले संक्रमण से पहले, इनपुट सिग्नल का मान 0. के माध्यम से है। तस्वीर में, यह क्षण एक सफेद तीर द्वारा दिखाया गया है। वह सबसे उपयुक्त क्यों है? हां, क्योंकि एम्पलीफायर 0 से k गुणा होगा, गुणक के स्थानों को बदलने से उत्पाद नहीं बदलता है, और 0 से गुणा करने पर 0. मिलता है। अर्थात, कश्मीर में तेज गिरावट स्वाभाविक रूप से बाहर निकल जाएगी , और यह सुनने वाले को लगेगा कि हमारा AGC भविष्य को देख रहा था (हालांकि वास्तव में वह बदल गया था) अतीत) और 0 के माध्यम से पार करने के समय वह पहले से जानता था कि एक छलांग का पालन करेगा। हम देखेंगे कि कार्यक्रम चलाने से यह ध्वनि संपादक में कैसा दिखता है। यह अभ्यास करने के लिए आगे बढ़ने का समय है!

अभ्यास

हम VB.Net पर विंडोज पर निर्माण करेंगे, इसलिए सबसे पहली जरूरत हमें विजुअल स्टूडियो की है। मैंने Visual Basic 2010 Express का उपयोग किया। जो अन्य विकास साधनों को अधिक पसंद करते हैं, जो लोग सिद्धांत को जानते हैं वे आसानी से कार्यक्रम को अपनी पसंद के अनुसार रीमेक करेंगे।

डेटा प्रारूप


दूसरी चीज जो हमें चाहिए वह है इनपुट। कच्चे, 16 बिट पीसीएम प्रारूप में। इस प्रारूप की एक फ़ाइल अहस्ताक्षरित 16-बिट हस्ताक्षरित पूर्णांक का एक क्रम है। नंबर इंटेल की शैली में लिखे गए हैं: पहले कम बाइट, उसके बाद हाई बाइट (जबकि बाइट्स में बिट्स का क्रम खुद उल्टा नहीं होता है)। प्रत्येक ऐसी संख्या इनपुट सिग्नल के मूल्य को संग्रहीत करती है। 32767 1, -32767 -1 से मेल खाती है, लेकिन 0 0. 0. -32768 है? चलो यह भी -1 - एक छोटी सी त्रुटि। इस तरह से फ़ाइल प्राप्त करने का एक तरीका है:
  1. विंडोज पीसीएम wav प्रारूप में एक साउंड एडिटर में एक फाइल बनाएं, प्रति नमूना 16 बिट, मोनो , बिना संपीड़न के। आपको किसी भी अतिरिक्त जानकारी के भंडारण को अक्षम करना होगा जो सीधे ध्वनि से संबंधित नहीं है (यदि हमारे ध्वनि संपादक के पास ऐसा कोई विकल्प है)।
  2. फ़ाइल से पहले 0x2C बाइट्स काटें। यह हेडलाइन है। सुनिश्चित करें कि बाइट्स की एक समान संख्या है, क्योंकि हमारे पास प्रति नमूना 2 बाइट्स हैं।
  3. फ़ाइल एक्सटेंशन को pcm में बदलें।

या आप शून्य के साथ शीर्षक को अधिलेखित कर सकते हैं - एजीसी सोचेंगे कि यह मौन है। या प्रोग्राम को थोड़ा जटिल करें ताकि यह इनपुट फ़ाइल से आउटपुट पर पहले 0x2C बाइट्स को बेवकूफ़ तरीके से कॉपी करे। फिर पहले से ही उसके wav को खिलाने के लिए संभव होगा, बिना pcm में परिवर्तित किए। लेकिन हम इसे एक उज्जवल भविष्य के लिए छोड़ देंगे, और अब हमारे पास इस तरह के इनपुट डेटा हैं:



यह मानव भाषण है, जिसमें एक शब्द विशेष रूप से सेंसरशिप के एक उच्च अधिनियम में दर्ज किया गया है। यह शब्द "एक्स" है, लेकिन "हबर" नहीं है।

बफरिंग


और तीसरी चीज जो हमें चाहिए वह है एंटीकलाइजिंग के लिए एक छोटा सा डेटा स्टोर करने का तरीका। एक सेकंड पर्याप्त है, क्योंकि अधिकांश वास्तविक ऑडियो संकेतों में, 0 से गुजरना प्रति सेकंड कई बार होता है। इस तरह के उद्देश्यों के लिए, एक तैयार सर्किलआर्चिव क्लास है। इसकी प्रतियां एक रिंग टेप के साथ एक टेप रिकॉर्डर के सिद्धांत पर काम करती हैं: आप जितना चाहें उतना डेटा हिला सकते हैं, और जब अतिप्रवाह हो जाता है, तो पुराने डेटा को नए डेटा के साथ ओवरराइट किया जाता है। यहाँ स्रोत है:
CircleArchive.vb
Public Class CircleArchive Private InternalCapacity As Integer Private InternalArray() As Object Private InternalLength As Integer Private InternalStart As Integer Public Sub New(ByVal setCap As UShort) If (setCap = 0) Then InternalCapacity = UShort.MaxValue + 1 Else InternalCapacity = setCap End If InternalStart = 0 InternalLength = 0 InternalArray = New Object(InternalCapacity - 1) {} 'need to specify maxindex, not size as the parameter End Sub Public Sub AddObject(ByVal ObjectToAdd As Object) Dim NewIndex As Integer If IsFull Then 'overwrite the oldest InternalArray(InternalStart) = ObjectToAdd InternalStart = (InternalStart + 1) Mod InternalCapacity Else NewIndex = (InternalStart + InternalLength) Mod InternalCapacity InternalArray(NewIndex) = ObjectToAdd InternalLength += 1 End If End Sub Public Function GetObjectFIFO(ByVal Index As Integer) As Object Dim r As Object = Nothing Dim TrueIndex As Integer If ((Index >= 0) AndAlso (Index < InternalLength)) Then TrueIndex = (InternalStart + Index) Mod InternalCapacity r = InternalArray(TrueIndex) ElseIf (Index < 0) Then Throw New IndexOutOfRangeException("got negative value: " & Index.ToString) Else Throw New IndexOutOfRangeException("got " & Index.ToString & " when " & InternalLength.ToString & " item(s) stored") End If Return r End Function Public Function GetObject(ByVal Index As Integer) As Object 'just an alias for GetObjectFIFO Return GetObjectFIFO(Index) End Function Public Function GetObjectLIFO(ByVal Index As Integer) As Object Dim r As Object = Nothing Dim TrueIndex As Integer If ((Index >= 0) AndAlso (Index < InternalLength)) Then TrueIndex = InternalLength - 1 - Index 'invert TrueIndex = (InternalStart + TrueIndex) Mod InternalCapacity r = InternalArray(TrueIndex) ElseIf (Index < 0) Then Throw New IndexOutOfRangeException("got negative value: " & Index.ToString) Else Throw New IndexOutOfRangeException("got " & Index.ToString & " when " & InternalLength.ToString & " item(s) stored") End If Return r End Function Public Sub Clear() Dim i As Integer Dim TrueIndex As Integer For i = 0 To (InternalLength - 1) 'nullify existing items TrueIndex = (InternalStart + i) Mod InternalCapacity InternalArray(TrueIndex) = Nothing Next InternalLength = 0 End Sub 'Public Sub QuickClear() ' InternalLength = 0 'End Sub Public ReadOnly Property Capacity As Integer Get Return InternalCapacity End Get End Property Public ReadOnly Property Length As Integer Get Return InternalLength End Get End Property Public ReadOnly Property IsFull As Boolean Get Return (InternalLength = InternalCapacity) End Get End Property 'additional features Public Sub RemoveObjects(ByVal Index As Integer, ByVal Count As Integer) Dim r As Object = Nothing Dim TrueIndexSrc As Integer Dim TrueIndexDst As Integer Dim TrueCount As Integer Dim i As Integer If ((Index < 0) OrElse (Index >= InternalLength)) Then Exit Sub End If If (Count <= 0) Then Exit Sub End If If (Count < (InternalLength - Index)) Then TrueCount = Count Else TrueCount = InternalLength - Index End If If (TrueCount = InternalLength) Then 'need to delete all Clear() Else 'need to delete part of the items For i = Index To (Index + TrueCount - 1) TrueIndexSrc = (InternalStart + i) Mod InternalCapacity InternalArray(TrueIndexSrc) = Nothing Next 'nullification loop If (Index = 0) Then 'the beginning has been deleted InternalStart = (InternalStart + TrueCount) Mod InternalCapacity 'just move the start position ElseIf ((Index + TrueCount) < InternalLength) Then 'need array shift 'decide what direction it will be faster to shift If ((InternalLength - Index - TrueCount) <= Index) Then 'shift the end For i = (Index + TrueCount) To (InternalLength - 1) TrueIndexSrc = (InternalStart + i) Mod InternalCapacity TrueIndexDst = (InternalStart + i - TrueCount) Mod InternalCapacity InternalArray(TrueIndexDst) = InternalArray(TrueIndexSrc) InternalArray(TrueIndexSrc) = Nothing Next Else 'shift the beginning i = Index - 1 While (i >= 0) TrueIndexSrc = (InternalStart + i) Mod InternalCapacity TrueIndexDst = (InternalStart + i + TrueCount) Mod InternalCapacity InternalArray(TrueIndexDst) = InternalArray(TrueIndexSrc) InternalArray(TrueIndexSrc) = Nothing i -= 1 End While InternalStart = (InternalStart + TrueCount) Mod InternalCapacity 'move the start position End If 'array shift direction switch End If 'the third case is the end has been deleted: we don't need neither start movement nor array shift InternalLength -= TrueCount End If '(not) TrueCount = InternalLength End Sub 'RemoveObjects Public Sub RemoveFirst(ByVal Count As Integer) RemoveObjects(0, Count) End Sub Public Sub RemoveLast(ByVal Count As Integer) RemoveObjects((InternalLength - Count), Count) End Sub Public Sub InsertObject(ByVal ObjectToInsert As Object, ByVal InsBefore As Integer) Dim TrueIndexSrc As Integer Dim TrueIndexDst As Integer Dim i As Integer Dim FirstElementBuf As Object If ((InsBefore >= 0) AndAlso (InsBefore < InternalLength)) Then If (InsBefore = 0) Then If (Not IsFull) Then 'no need array shift, just move the start position 1 step backward InternalStart = (InternalStart + InternalCapacity - 1) Mod InternalCapacity InternalArray(InternalStart) = ObjectToInsert 'and increase length InternalLength += 1 End If 'Not IsFull Else 'need array shift 'decide what direction it will be faster to shift If (InsBefore > (InternalLength \ 2)) Then 'shift the end i = InternalLength - 1 While (i >= InsBefore) TrueIndexSrc = (InternalStart + i) Mod InternalCapacity TrueIndexDst = (InternalStart + i + 1) Mod InternalCapacity InternalArray(TrueIndexDst) = InternalArray(TrueIndexSrc) i -= 1 End While TrueIndexDst = (InternalStart + InsBefore) Mod InternalCapacity InternalArray(TrueIndexDst) = ObjectToInsert If IsFull Then 'the oldest was overwritten, need to move the start position 1 step forward InternalStart = (InternalStart + 1) Mod InternalCapacity Else InternalLength += 1 End If '(not) IsFull Else 'shift the beginning FirstElementBuf = InternalArray(InternalStart) For i = 1 To (InsBefore - 1) TrueIndexSrc = (InternalStart + i) Mod InternalCapacity TrueIndexDst = (InternalStart + i - 1) Mod InternalCapacity InternalArray(TrueIndexDst) = InternalArray(TrueIndexSrc) Next TrueIndexDst = (InternalStart + InsBefore - 1) Mod InternalCapacity InternalArray(TrueIndexDst) = ObjectToInsert If (Not IsFull) Then 'move the start position InternalStart = (InternalStart + InternalCapacity - 1) Mod InternalCapacity InternalArray(InternalStart) = FirstElementBuf InternalLength += 1 End If 'Not IsFull End If 'array shift direction switch End If '(not) InsBefore = 0 ElseIf (InsBefore < 0) Then Throw New IndexOutOfRangeException("got negative value: " & InsBefore.ToString) Else Throw New IndexOutOfRangeException("got " & InsBefore.ToString & " when " & InternalLength.ToString & " item(s) stored") End If End Sub 'InsertObject Public Sub ReplaceObject(ByVal Index As Integer, ByVal NewObject As Object) Dim TrueIndex As Integer If ((Index >= 0) AndAlso (Index < InternalLength)) Then TrueIndex = (InternalStart + Index) Mod InternalCapacity InternalArray(TrueIndex) = NewObject ElseIf (Index < 0) Then Throw New IndexOutOfRangeException("got negative value: " & Index.ToString) Else Throw New IndexOutOfRangeException("got " & Index.ToString & " when " & InternalLength.ToString & " item(s) stored") End If End Sub 'ReplaceObject End Class 

लड़ाई के लिए!


हम विजुअल स्टूडियो शुरू करते हैं, जल्दी में हम विंडोज फॉर्म एप्लीकेशन बनाते हैं, हम विंडो में नियंत्रणों का एक गुच्छा बनाते हैं।

यहां तक ​​कि टारगेट वॉल्यूम जोड़ने में कामयाब रहे। यह वह आयाम है जिससे हम बढ़ेंगे। 1 समावेशी तक सकारात्मक मूल्यों की अनुमति है। अब हम प्रोजेक्ट में CircleArchive क्लास को जोड़ते हैं और निस्वार्थ रूप से कोड लिखते हैं।
Form1.vb
 Public Class Form1 Private Structure AGCBufferElement Public InputPCMVal As Short ' ,    Public OutputPCMVal As Short ' ,      End Structure Private Sub ButtonAGC_Click(sender As System.Object, e As System.EventArgs) Handles ButtonAGC.Click Dim InputFileName As String = My.Application.Info.DirectoryPath & "\Input.pcm" Dim OutputFileName As String = My.Application.Info.DirectoryPath & "\Output.pcm" Dim InputFileStream As System.IO.FileStream = Nothing Dim OutputFileStream As System.IO.FileStream = Nothing Dim NSamples As Long '    Dim SampleIndex As Long Dim OneSecBufIndex As Integer '       anticlipping Dim kmax As Double = Decimal.ToDouble(NumericUpDownMaxGain.Value) Dim TargetVolume As Double = Decimal.ToDouble(NumericUpDownTargetVol.Value) '     Dim vmin As Double '  Dim AGCLeap As Boolean '  Dim k As Double '  Dim b As Double '  Dim CurrBuf As AGCBufferElement '  PCM Dim PrevBuf As AGCBufferElement Dim u As Double '  Dim v As Double ' ,        Dim OneSecBuf As CircleArchive '       anticlipping Dim NegHalfwave As Boolean '     0 '  Try If (My.Computer.FileSystem.FileExists(InputFileName)) Then InputFileStream = New System.IO.FileStream(InputFileName, IO.FileMode.Open) OutputFileStream = New System.IO.FileStream(OutputFileName, IO.FileMode.Create) End If Catch ex As Exception End Try If ((InputFileStream IsNot Nothing) AndAlso (OutputFileStream IsNot Nothing)) Then ' vmin = TargetVolume / kmax b = 1.0 - Math.Pow(10.0, (-1.0 / Decimal.ToDouble(Decimal.Multiply(NumericUpDownSampleRate.Value, NumericUpDownFalloffTime.Value)))) v = vmin OneSecBuf = New CircleArchive(CUShort(NumericUpDownSampleRate.Value)) InputFileStream.Position = 0 NSamples = InputFileStream.Length \ 2 '2 bytes per sample '! For SampleIndex = 0 To (NSamples - 1) '  PCM   CurrBuf.InputPCMVal = CShort(InputFileStream.ReadByte) 'LSB first (Intel manner) CurrBuf.InputPCMVal = CurrBuf.InputPCMVal Or (CShort(InputFileStream.ReadByte) << 8) 'MSB last (Intel manner) If (CurrBuf.InputPCMVal = Short.MinValue) Then CurrBuf.InputPCMVal += 1 '     -32767 .. 32767 End If '  Double    If (CurrBuf.InputPCMVal < 0) Then u = -CurrBuf.InputPCMVal / Short.MaxValue Else u = CurrBuf.InputPCMVal / Short.MaxValue End If '   ' If (u < vmin) Then u = vmin End If '   '  AGCLeap = (u > v) If AGCLeap Then v = u End If '   ,      k = TargetVolume / v '   '   If (AGCLeap AndAlso CheckBoxAnticlipping.Checked) Then ' anticlipping:            0 NegHalfwave = (CurrBuf.InputPCMVal < 0) '     ? OneSecBufIndex = OneSecBuf.Length - 1 While (OneSecBufIndex >= 0) PrevBuf = CType(OneSecBuf.GetObjectFIFO(OneSecBufIndex), AGCBufferElement) '   0 If (PrevBuf.InputPCMVal = 0) Then Exit While ElseIf (NegHalfwave Xor (PrevBuf.InputPCMVal < 0)) Then Exit While End If '     ,    0   PrevBuf.OutputPCMVal = PrevBuf.InputPCMVal * k '    PCM        (   ,  k  ) OneSecBuf.ReplaceObject(OneSecBufIndex, PrevBuf) '  OneSecBufIndex -= 1 ' ,  ,    End While '   OneSecBuf End If ' anticlipping CurrBuf.OutputPCMVal = CurrBuf.InputPCMVal * k '     If OneSecBuf.IsFull Then '             ,    PrevBuf = CType(OneSecBuf.GetObjectFIFO(0), AGCBufferElement) Try '   OutputFileStream.WriteByte(CByte(PrevBuf.OutputPCMVal And Byte.MaxValue)) 'LSB first (Intel manner) OutputFileStream.WriteByte(CByte((PrevBuf.OutputPCMVal >> 8) And Byte.MaxValue)) 'MSB last (Intel manner) Catch ex As Exception End Try End If 'OneSecBuf.IsFull OneSecBuf.AddObject(CurrBuf) '     OneSecBuf ' ,      If (Not AGCLeap) Then v = v - b * (v - u) '  v      End If Next '     ' OneSecBuf For OneSecBufIndex = 0 To (OneSecBuf.Length - 1) PrevBuf = CType(OneSecBuf.GetObjectFIFO(OneSecBufIndex), AGCBufferElement) Try OutputFileStream.WriteByte(CByte(PrevBuf.OutputPCMVal And Byte.MaxValue)) 'LSB first (Intel manner) OutputFileStream.WriteByte(CByte((PrevBuf.OutputPCMVal >> 8) And Byte.MaxValue)) 'MSB last (Intel manner) Catch ex As Exception End Try Next '   OneSecBuf End If '     If (OutputFileStream IsNot Nothing) Then OutputFileStream.Close() End If If (InputFileStream IsNot Nothing) Then InputFileStream.Close() End If MsgBox("The end.") End Sub End Class 

जैसा कि आप कोड से देख सकते हैं, प्रोग्राम अपने एक्स के बगल में Input.pcm फ़ाइल लेगा (इसे वहां डाल दें यदि आपने अभी तक ऐसा नहीं किया है) और वहां काम के परिणाम के साथ Output.pcm बनाएं। हम शुरू करते हैं। फ़ॉलऑफ़ टाइम 10 सेकंड सेट करें (यह प्रतिक्रिया समय है), अधिकतम लाभ 20, लक्ष्य मात्रा 0.95 (इसकी सभी महिमा में एंटीक्लिपिंग देखने के लिए)। नमूना दर के बारे में मत भूलना, क्योंकि यह कच्चे डेटा फ़ाइल में संग्रहीत नहीं है। एंटीक्लिपिंग चालू करें और बटन दबाएं। क्या आपको Output.pcm मिला है? हाँ हाँ! हम इसे वापस लहराते हुए परिवर्तित करते हैं, शीर्षक को उस स्थान पर लौटाते हैं, हम सुनते हैं। हम ध्वनि संपादक में लोड करते हैं और देखते हैं:



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



यह ठीक उसी जगह है जहां सेंसरशिप शुरू होती है। वैसे, अंत में anticlipping'e के बारे में थोड़ा और ...

एक निष्कर्ष के बजाय

हमारा एंटीक्लिपिंग एल्गोरिथ्म इष्टतम से बहुत दूर है। सामान्य कूद, मुझे याद है, एक पंक्ति में कई नमूने हैं। उनमें से प्रत्येक को संसाधित करना, हम फिर से और फिर से समय में वापस जाते हैं और सीपीयू के समय को बर्बाद करते हुए महत्वपूर्ण मात्रा में डेटा को पुन: प्राप्त करते हैं। इसके बजाय, आपको केवल छलांग लगाने की ज़रूरत है, जब डंपिंग पर आगे बढ़ने से पहले। और यह अनुमान लगाने के लिए कि इसके लिए आपको कोड जोड़ना होगा, यह हमारे लिए मुश्किल नहीं होगा।

Source: https://habr.com/ru/post/In166441/


All Articles