วิธีสร้างเครื่องกำเนิดคลื่นไซน์ที่สามารถเปลี่ยนระหว่างความถี่ได้อย่างราบรื่น


27

ฉันสามารถเขียนเครื่องกำเนิดคลื่นไซน์พื้นฐานสำหรับเสียงได้ แต่ฉันต้องการให้สามารถเปลี่ยนจากความถี่หนึ่งไปเป็นอีกความถี่ได้อย่างราบรื่น หากฉันเพิ่งหยุดสร้างความถี่หนึ่งและเปลี่ยนไปใช้ความถี่อื่นทันทีจะมีความไม่ต่อเนื่องของสัญญาณและจะได้ยิน "คลิก"

คำถามของฉันคืออะไรอัลกอริทึมที่ดีในการสร้างคลื่นที่เริ่มต้นคือพูด 250Hz แล้วเปลี่ยนเป็น 300Hz โดยไม่ต้องคลิกใด ๆ หากอัลกอริทึมมีเวลาร่อน / พอร์ตเสริมที่เป็นทางเลือกดังนั้นดีกว่ามาก

ฉันสามารถนึกถึงวิธีที่เป็นไปได้สองสามอย่างเช่นการสุ่มตัวอย่างมากเกินไปตามด้วยตัวกรองความถี่ต่ำหรืออาจใช้ wavetable แต่ฉันแน่ใจว่านี่เป็นปัญหาที่พบได้บ่อยพอที่มีวิธีมาตรฐานในการแก้ปัญหา


2
ทำไมคุณไม่ใช้การเปลี่ยนความถี่เชิงเส้นตลอดช่วงการเปลี่ยนภาพ ตัวอย่างเช่นคุณต้องเปลี่ยนจากความถี่ f0 ในเวลา t0 ถึงความถี่ f1 ณ เวลา t1 แล้วทำไมไม่เพียงแค่แนะนำการเปลี่ยนความถี่ f (t) = f0 * (1-q) + f1 * q โดยที่ q = (t -t0) / (t1-t0) จากนั้นสร้างสัญญาณ A (t) = sin (2 * Pi * f (t) * t)?
mbaitoff

คำตอบ:


24

วิธีการหนึ่งที่ฉันใช้ในอดีตคือการรักษาเฟสแอคคูเลเตอร์ที่ใช้เป็นดัชนีในตารางการค้นหารูปคลื่น ค่าเฟส delta ถูกเพิ่มเข้าไปในตัวสะสมในแต่ละช่วงเวลาตัวอย่าง:

phase_index += phase_delta

ในการเปลี่ยนความถี่คุณเปลี่ยนเฟสของเดลต้าที่เพิ่มไปยังเฟสแอคคูเลเตอร์ที่แต่ละตัวอย่างเช่น

phase_delta = N * f / Fs

ที่อยู่:

phase_delta is the number of LUT samples to increment
freq is the desired output frequency
Fs is the sample rate

สิ่งนี้รับประกันว่ารูปคลื่นของเอาต์พุตจะต่อเนื่องแม้ว่าคุณจะเปลี่ยน phase_delta แบบไดนามิกเช่นสำหรับการเปลี่ยนแปลงความถี่, FM และอื่น ๆ

สำหรับการเปลี่ยนแปลงความถี่ที่ราบรื่นขึ้น (portamento) คุณสามารถเพิ่มค่า phase_delta ระหว่างค่าเก่ากับค่าใหม่ในช่วงเวลาที่เหมาะสมแทนการเปลี่ยนทันที

โปรดทราบว่าphase_indexและphase_deltaทั้งคู่มีจำนวนเต็มและส่วนประกอบที่เป็นเศษส่วนนั่นคือพวกเขาต้องเป็นทศนิยมหรือจุดคงที่ ส่วนจำนวนเต็มของ phase_index (ขนาดตาราง modulo) ถูกใช้เป็นดัชนีในรูปแบบของคลื่น LUT และส่วนที่เป็นเศษส่วนอาจใช้เป็นทางเลือกสำหรับการแก้ไขระหว่างค่า LUT ที่อยู่ติดกันเพื่อคุณภาพผลผลิตที่สูงขึ้นและ / หรือขนาด LUT ที่เล็กลง


ขอบคุณฉันคาดหวังว่าคำตอบอาจเกี่ยวข้องกับ LUT ฉันกำลังคิดว่าจะไปกับ LUT ที่มีหนึ่งรูปคลื่นที่ 1Hz (เช่นรายการ Fs) มีกฎของหัวแม่มือที่ควบคุมขนาดที่เหมาะสมของ LUT หรือไม่?

4
ขึ้นอยู่กับปัจจัยต่าง ๆ : สิ่งที่ SNR คุณกำลังมองหาไม่ว่าจะเป็นคลื่นไซน์บริสุทธิ์หรือรูปคลื่นที่ซับซ้อนมากขึ้นไม่ว่าคุณวางแผนที่จะแก้ไขระหว่างรายการ LUT ที่อยู่ติดกันหรือเพียงแค่ตัดทอน ฯลฯ ก็ขึ้นอยู่กับว่าคุณกำลังจะ มีตาราง Quadrant เดียวและจัดการเลขคณิตการจัดทำดัชนีและเข้าสู่ระบบผกผันด้วยตัวเองหรือมีตาราง Quadrant เต็มสี่ โดยส่วนตัวแล้วฉันจะเริ่มต้นด้วย 1024 จุด (NB: 2 ^ N เป็นสิ่งที่ดีสำหรับการทำดัชนีโมดูโล) ตารางสี่ Quadrant ที่ไม่มีการแก้ไขเพราะนี่เป็นเรื่องง่ายมากและควรให้ผลลัพธ์ที่ดีสำหรับเสียง 16 บิต "ผู้บริโภค"
พอล R

1
คำตอบที่ดีพอล นอกจากนี้ยังมีคำถามที่คล้ายกันในหัวข้อที่โพสต์ไปพักหนึ่ง ข้อมูลเพิ่มเติมจะช่วยได้เสมอ
Jason R

4
อีกวิธีหนึ่งในการดูวิธีนี้คือการจำลองของออสซิลเลเตอร์ควบคุมแรงดันไฟฟ้า (VCO) ความถี่เอาท์พุทของ VCO ขึ้นอยู่กับแรงดันไฟฟ้าอินพุต (โดยปกติคือฟังก์ชันเชิงเส้นของแรงดันไฟฟ้าอินพุต) แต่สัญญาณเอาต์พุตมีเฟสต่อเนื่องแม้ว่าแรงดันอินพุตจะเปลี่ยนทันที ผลลัพธ์คือโดยที่คืออย่างต่อเนื่องฟังก์ชั่นของเวลาในขณะที่ความถี่ออกเป็นอนุพันธ์ของเฟสและเท่ากับที่ความถี่นิ่ง
sin(ϕ(t))=sin(0tω0+kx(τ)dτ)
ϕ(t)
ω0+kx(t)
ω0
Dilip Sarwate

1
ฉันมีปัญหาเดียวกันขอขอบคุณสำหรับแนวคิดแบบสะสม (ฉันใช้การคำนวณโดยตรงซึ่งไม่ได้ผลเนื่องจากการประมาณ): jsfiddle.net/sebpiq/p3ND5/12
sebpiq

12

หนึ่งในวิธีที่ดีที่สุดในการสร้างคลื่นไซน์คือการใช้เฟสเซอร์ที่ซับซ้อนพร้อมการอัพเดตซ้ำ กล่าวคือ

z[n+1]=z[n]Ω

ที่ z [n] คือเฟสเซอร์โดยที่เป็นความถี่เชิงมุมของออสซิลเลเตอร์ในเรเดียนและดัชนีตัวอย่าง ทั้งส่วนจริงและจินตภาพของเป็นคลื่นไซน์พวกมันอยู่นอกช่วง 90 องศา สะดวกมากถ้าคุณต้องการทั้งไซน์และโคไซน์ การคำนวณตัวอย่างเดียวต้องการเพียง 4 ทวีคูณและ 4 เพิ่มเท่านั้นและราคาถูกกว่าสิ่งใด ๆ ที่มี sin () cos () หรือตารางการค้นหา ปัญหาที่อาจเกิดขึ้นคือแอมพลิจูดอาจลอยไปตามกาลเวลาเนื่องจากปัญหาความแม่นยำเชิงตัวเลข อย่างไรก็ตามมีการส่งซ่อมตรงไปตรงมา สมมติว่าJB เรารู้ว่าควรมีขนาดเป็นเอกภาพเช่น Ω=exp(jω)ωnz[n]z[n]=a+jbz[n]

aa+bb=1

ดังนั้นเราสามารถตรวจสอบทุกครั้งในกรณีที่ยังคงเป็นกรณีและแก้ไขให้ถูกต้อง การแก้ไขที่แน่นอนจะเป็น

z[n]=z[n]aa+bb

นั่นคือการคำนวณที่น่าอึดอัดใจ แต่เนื่องจากใกล้เคียงกับเอกภาพมากคุณสามารถประมาณเงื่อนไขด้วยการขยายเทย์เลอร์รอบและเราได้aa+bb1/xx=1

1x3x2

ดังนั้นการแก้ไขจึงง่ายขึ้น

z[n]=z[n]3a2b22

การใช้การแก้ไขอย่างง่ายทุก ๆ ร้อยตัวอย่างจะทำให้ออสซิลเลเตอร์เสถียรตลอดไป

หากต้องการเปลี่ยนแปลงความถี่อย่างต่อเนื่องตัวคูณ W จะต้องได้รับการอัปเดตตามลำดับ แม้แต่การเปลี่ยนแปลงแบบไม่ต่อเนื่องในตัวคูณจะยังคงรักษาฟังก์ชันออสซิลเลเตอร์อย่างต่อเนื่อง หากต้องการ ramping ความถี่การอัปเดตอาจแบ่งออกเป็นสองขั้นตอนหรือคุณสามารถใช้อัลกอริทึม oscillator เดียวกันเพื่ออัปเดตตัวทวีคูณตัวเอง


ขอบคุณสำหรับคำตอบนี้อาจจะใช้เวลาสักครู่ที่ฉันจะเข้าใจดีพอที่จะกลายเป็นรหัสโลกจริง แต่ดูเหมือนว่าเป็นทางเลือกที่น่าสนใจที่จะลอง
Mark Heath

2
ฉันใช้โซลูชันนี้ใน golang สำหรับการอ้างอิง: github.com/rmichela/Acoustico/blob/
Ryan Michela

นี่เป็นวิธีแก้ปัญหาที่สวยงามที่น่าเสียดายที่ทำงานได้ดีถ้าใช้ฐานเวลาคงที่เท่านั้น ถ้าไม่คุณต้องคำนวณบาปและ cos เพื่อคำนวณการหมุนแบบซับซ้อนที่ถูกต้อง
คาเมรอน Tacklind

2

จากเว็บไซต์นี้ :

เพื่อสร้างการเปลี่ยนแปลงที่ราบรื่นจากความถี่หนึ่งไปยังอีกหรือหนึ่งแอมพลิจูดหนึ่งไปอีกคลื่นไซน์ที่ไม่สมบูรณ์จะต้องแก้ไขด้วยส่วนที่ต่อท้ายเพื่อให้คลื่นที่เกิดขึ้นหลังจากการวนซ้ำของลูปสิ้นสุดลงที่แกน x

ดูเหมือนว่ามันจะทำงานได้ดี

(ที่จริงแล้วถ้าทั้งคู่ซิงโครไนซ์ที่แกน x เมื่อมีการเปลี่ยนแปลงฉันคิดว่าไม่จำเป็นต้องมีการเปลี่ยนทีละน้อย)


1
นี้กล่าวว่าการรอคอยสำหรับ sinusoid ปัจจุบันที่ความถี่กับวงจรที่สมบูรณ์หนึ่งและมาถึงที่และจากนั้นสลับไป sinusoid อื่น ๆ ที่ความถี่\สิ่งนี้รักษาความต่อเนื่องของเฟสได้อย่างมีประสิทธิภาพและน่าจะโอเคสำหรับแอปพลิเคชั่นเสียงที่หน่วงเวลาไม่กี่มิลลิวินาทีหรือไมโครวินาทีระหว่างเวลาสวิทช์ที่ต้องการ (ตอนนี้) และเวลาสวิทช์ที่นำมาใช้ อย่างไรก็ตามความแตกต่างอาจทำให้เกิดปัญหาในการใช้งานอื่น ๆ เพียงจำไว้ว่าไซน์ซอยด์เป็นสองครั้งในรอบเดียว 0 ω 1 0ω00ω10
Dilip Sarwate

2

ฉันเห็นด้วยกับคำแนะนำก่อนหน้านี้ของการใช้ตัวสะสมเฟส โดยพื้นฐานแล้วอินพุตควบคุมคือจำนวนของเฟสล่วงหน้าต่อขั้นตอนหรือต่อช่วงเวลาของนาฬิกา (หรือต่อขัดจังหวะหรืออะไรก็ตาม) ดังนั้นการเปลี่ยนค่านั้นจะเปลี่ยนความถี่โดยไม่หยุดชะงักในเฟส คลื่นแอมพลิจูดจะถูกกำหนดจากค่าเฟสสะสมผ่านทาง LUT หรือเพียงแค่คำนวณบาป (theta) หรือ cos (theta)

นี่คือสิ่งที่เป็นที่รู้จักกันทั่วไปว่าเป็น Oscillator ที่ควบคุมเชิงตัวเลข (NCO) หรือ Direct Digital Synthesizer (DDS) การค้นหาเว็บโดยใช้คำเหล่านั้นอาจจะให้ผลมากกว่าที่คุณต้องการรู้ในทฤษฎีและการฝึกฝนเพื่อทำให้มันทำงานได้ดี

การเพิ่มตัวสะสมเพิ่มเติมสามารถอนุญาตให้มีการเปลี่ยนระหว่างความถี่ตามที่คุณแนะนำถ้าต้องการเช่นกันโดยการควบคุมอัตราการเปลี่ยนแปลงของค่าล่วงหน้าของเฟส บางครั้งสิ่งนี้เรียกว่า Digital Differential Analyzer หรือ DDA


ข้อมูลเพิ่มเติมที่ดี ดีใจที่ได้พบคุณที่นี่เอริค; เราสามารถใช้อัลกอริธึมของรัฐมนตรี
Jason R

1

อันดับที่ 1 คุณควรปรับเฟสเริ่มต้นของความถี่ไซน์ไซด์ใหม่เพื่อให้เป็นเช่นเดียวกับเฟสไซน์ก่อนหน้านี้ที่จุดเปลี่ยนตัวอย่างที่ 1 คำนวณความถี่แรกและใช้เฟสของมันสำหรับความถี่ที่สอง

ตัวเลือกที่สองอาจเป็นทางลาด d_phase จากความถี่หนึ่งไปยังอีกตัวอย่างหนึ่ง สิ่งนี้จะทำความสะอาดความต่อเนื่องของอนุพันธ์อันดับที่ 1 และให้การร่อน

ตัวเลือกที่ 3 อาจเป็นการใช้หน้าต่างที่ราบเรียบเช่นอัตราการเพิ่มของโคไซน์ที่เพิ่มขึ้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.