บทความก่อนหน้า ใน Audio Worklet มีรายละเอียดแนวคิดและการใช้งานเบื้องต้น ตั้งแต่เปิดตัวใน Chrome 66 ก็มีคำขอตัวอย่างเพิ่มเติมเกี่ยวกับวิธีใช้ในแอปจริงๆ หลายครั้ง Audio Worklet ปลดล็อกศักยภาพสูงสุดของ WebAudio แต่การใช้ประโยชน์จาก WebAudio อาจเป็นเรื่องท้าทายเนื่องจากต้องเข้าใจการเขียนโปรแกรมที่เกิดขึ้นพร้อมกันซึ่งรวมไว้ด้วย JS API หลายรายการ แม้แต่นักพัฒนาซอฟต์แวร์ที่คุ้นเคยกับ WebAudio แต่การผสานรวม Audio Worklet กับ API อื่นๆ (เช่น WebAssembly) ก็อาจเป็นเรื่องยาก
บทความนี้จะทำให้ผู้อ่านเข้าใจวิธีใช้ Worklet เสียงในสภาพแวดล้อมการใช้งานจริงได้ดีขึ้น และให้คำแนะนำในการใช้ผลงานอย่างเต็มศักยภาพ อย่าลืมดู ตัวอย่างโค้ดและการสาธิตแบบสด ด้วย
สรุป: Worklet เสียง
ก่อนที่จะเจาะลึก เรามาทบทวนคำศัพท์และข้อเท็จจริงเกี่ยวกับระบบของ Worklet เสียงที่เคยเปิดตัวในโพสต์นี้กันก่อน
- BaseAudioContext: ออบเจ็กต์หลักของ Web Audio API
- โปรแกรมเสียง: ตัวโหลดไฟล์สคริปต์พิเศษสำหรับการดำเนินการ Audio Worklet เป็นของ BaseAudioContext BaseAudioContext มี Audio Worklet ได้ 1 รายการ ไฟล์สคริปต์ที่โหลดจะได้รับการประเมินใน AudioWorkletGlobalScope และใช้ในการสร้างอินสแตนซ์ AudioWorkletProcessor
- AudioWorkletGlobalScope : ขอบเขต JS พิเศษที่ครอบคลุมสำหรับการทำงานของ Audio Worklet ทำงานในเทรดการแสดงผลเฉพาะสำหรับ WebAudio BaseAudioContext สามารถมี AudioWorkletGlobalScope ได้ 1 ตัว
- AudioWorkletNode: AudioNode ที่ออกแบบมาสำหรับการดำเนินการ Audio Worklet ยกตัวอย่างจาก BaseAudioContext BaseAudioContext สามารถมี AudioWorkletNodes ได้หลายอัน คล้ายกับ AudioNodes แบบเนทีฟ
- AudioWorkletProcessor : คู่กันของ AudioWorkletNode ความกล้าจริงๆ ของ AudioWorkletNode ประมวลผลสตรีมเสียงโดยใช้โค้ดที่ผู้ใช้จัดหา โดยจะอินสแตนซ์ใน AudioWorkletGlobalScope เมื่อมีการสร้าง AudioWorkletNode AudioWorkletNode มี AudioWorkletProcessor ที่ตรงกันได้ 1 เครื่อง
ออกแบบลวดลาย
การใช้ Worklet Audio กับ WebAssembly
WebAssembly เป็นแอปที่ใช้ร่วมกันได้ สำหรับ AudioWorkletProcessor การใช้ทั้ง 2 ฟีเจอร์ร่วมกันนำมาซึ่งข้อได้เปรียบมากมายในการประมวลผลเสียงในเว็บ แต่ประโยชน์ 2 ข้อที่สําคัญที่สุดคือ ก) นําโค้ดการประมวลผลเสียง C/C++ ที่มีอยู่มาใช้ในระบบนิเวศของ WebAudio และ ข) หลีกเลี่ยงค่าใช้จ่ายในการรวม JIT ใน JS และการเก็บข้อมูลขยะในโค้ดการประมวลผลเสียง
ประเภทแรกสำคัญต่อนักพัฒนาซอฟต์แวร์ที่ลงทุนกับโค้ดและไลบรารีการประมวลผลเสียงอยู่แล้ว แต่นโยบายหลังมีความสำคัญต่อผู้ใช้ API เกือบทั้งหมด ในโลกของ WebAudio งบประมาณด้านเวลาสำหรับสตรีมเสียงที่เสถียรนั้นค่อนข้างมีความต้องการอยู่มาก โดยคิดเป็น 3 มิลลิวินาทีที่อัตราการสุ่มตัวอย่าง 44.1 Khz แม้แต่เกิดข้อขัดข้องเล็กน้อยในโค้ดการประมวลผลเสียงก็อาจทำให้เกิดข้อบกพร่องได้ นักพัฒนาซอฟต์แวร์ต้องเพิ่มประสิทธิภาพโค้ดเพื่อให้ประมวลผลได้เร็วขึ้นและลดจำนวนขยะ JS ที่สร้างขึ้นด้วย การใช้ WebAssembly อาจเป็นโซลูชันที่แก้ไขปัญหาทั้ง 2 อย่างพร้อมกันได้ ซึ่งเร็วกว่าและไม่สร้างขยะจากโค้ด
หัวข้อถัดไปจะอธิบายว่า WebAssembly สามารถใช้กับ Audio Worklet ได้อย่างไร และดูตัวอย่างโค้ดที่มาพร้อมกับได้ที่นี่ สำหรับบทแนะนำพื้นฐานเกี่ยวกับวิธีใช้ Emscripten และ WebAssembly (โดยเฉพาะโค้ดกาว Emscripten) โปรดดูบทความนี้
การตั้งค่า
ฟังดูเข้าท่าดี แต่เราจำเป็นต้องมีโครงสร้างเล็กน้อยเพื่อตั้งค่าสิ่งต่างๆ ให้ถูกต้อง คำถามด้านการออกแบบข้อแรกคือวิธีและตำแหน่งที่อินสแตนซ์ของโมดูล WebAssembly หลังจากดึงข้อมูล Glue ของ Emscripten แล้ว การสร้างโมดูลจะมี 2 เส้นทางดังนี้
- จำลองโมดูล WebAssembly โดยโหลดโค้ดกาวลงใน AudioWorkletGlobalScope ผ่าน
audioContext.audioWorklet.addModule()
- สร้างอินสแตนซ์ของโมดูล WebAssembly ในขอบเขตหลัก จากนั้นโอนโมดูลผ่านตัวเลือกตัวสร้างของ AudioWorkletNode
การตัดสินใจส่วนใหญ่จะขึ้นอยู่กับการออกแบบและความชอบของคุณ แต่แนวคิดคือโมดูล WebAssembly สามารถสร้างอินสแตนซ์ WebAssembly ใน AudioWorkletGlobalScope ซึ่งจะกลายเป็นเคอร์เนลในการประมวลผลเสียงภายในอินสแตนซ์ AudioWorkletProcessor
Emscripten ต้องใช้ตัวเลือก 2-3 อย่างในการสร้างโค้ดกาว WebAssembly ที่ถูกต้องสำหรับการกำหนดค่า เพื่อให้รูปแบบ A ทำงานได้อย่างถูกต้อง
-s BINARYEN_ASYNC_COMPILATION=0 -s SINGLE_FILE=1 --post-js mycode.js
ตัวเลือกเหล่านี้ช่วยให้การรวบรวมโมดูล WebAssembly ใน AudioWorkletGlobalScope พร้อมกันจะช่วยให้เกิดการรวบรวมโมดูล WebAssembly และยังเพิ่มคำจำกัดความคลาสของ AudioWorkletProcessor ใน mycode.js
เพื่อให้โหลดได้หลังจากเริ่มต้นโมดูลแล้ว
เหตุผลหลักในการใช้การรวบรวมแบบซิงโครนัสคือ audioWorklet.addModule()
ไม่ได้รอการแก้ปัญหาของคำสัญญาใน AudioWorkletGlobalScope โดยทั่วไปเราไม่แนะนำให้โหลดหรือคอมไพล์แบบพร้อมกันในเทรดหลัก เนื่องจากจะเป็นการบล็อกงานอื่นๆ ในเทรดเดียวกัน แต่เราสามารถข้ามกฎนี้ได้เพราะการคอมไพล์เกิดขึ้นใน AudioWorkletGlobalScope ซึ่งทำงานนอกเทรดหลัก (ดู
หน้านี้
สำหรับข้อมูลเพิ่มเติม)
รูปแบบ B จะมีประโยชน์หากจำเป็นต้องใช้การยกหนักแบบไม่พร้อมกัน โดยใช้เทรดหลักเพื่อดึงข้อมูล Glue Code จากเซิร์ฟเวอร์และคอมไพล์โมดูล จากนั้นจะโอนโมดูล WASM ผ่านตัวสร้างของ AudioWorkletNode รูปแบบนี้จะเหมาะสมยิ่งขึ้นเมื่อคุณต้องโหลดโมดูลแบบไดนามิกหลังจากที่ AudioWorkletGlobalScope เริ่มแสดงผลสตรีมเสียง การคอมไพล์โมดูลตรงกลางการแสดงผลอาจทำให้เกิดข้อบกพร่องในสตรีมได้ ทั้งนี้ขึ้นอยู่กับขนาดของโมดูล
ข้อมูลฮีปและเสียง WASM
โค้ด WebAssembly จะทำงานในหน่วยความจำที่จัดสรรภายในฮีป WASM โดยเฉพาะเท่านั้น ในการใช้ประโยชน์จากเทคโนโลยีนี้ ต้องมีการโคลนข้อมูลเสียงกลับไปกลับมาระหว่างฮีป WASM และอาร์เรย์ข้อมูลเสียง คลาส HeapAudioBuffer ในโค้ดตัวอย่างจัดการกับการดำเนินการนี้อย่างเหมาะสม
มีข้อเสนอก่อนหน้านี้อยู่ระหว่างการพูดคุยเพื่อผสานรวมฮีป WASM เข้ากับระบบ Audio Worklet โดยตรง การกำจัดการโคลนข้อมูลที่ซ้ำซ้อนระหว่างหน่วยความจำ JS และฮีป WASM อาจดูเป็นเรื่องปกติทั่วไป แต่ต้องปรับปรุงรายละเอียดที่เฉพาะเจาะจง
การจัดการขนาดบัฟเฟอร์ไม่ตรงกัน
คู่ AudioWorkletNode และ AudioWorkletProcessor ออกแบบมาให้ทำงานเหมือน AudioNode ทั่วไป ส่วน AudioWorkletNode จะจัดการการโต้ตอบกับโค้ดอื่นๆ ส่วน AudioWorkletProcessor จะจัดการการประมวลผลเสียงภายใน เนื่องจาก AudioNode ปกติประมวลผลครั้งละ 128 เฟรม ทำให้ AudioWorkletProcessor ต้องทำแบบเดียวกันเพื่อให้เป็นฟีเจอร์หลักได้ ข้อได้เปรียบอย่างหนึ่งของการออกแบบ Audio Worklet ทำให้ไม่มีเวลาในการตอบสนองเพิ่มเนื่องจากมีการบัฟเฟอร์ภายในภายใน AudioWorkletProcessor แต่ก็อาจเป็นปัญหาหากฟังก์ชันการประมวลผลต้องใช้ขนาดบัฟเฟอร์ต่างจาก 128 เฟรม วิธีแก้ปัญหาที่พบได้ทั่วไปสำหรับกรณีดังกล่าวคือการใช้บัฟเฟอร์แหวน หรือที่เรียกว่าบัฟเฟอร์วงกลมหรือ FIFO
นี่คือแผนภาพของ AudioWorkletProcessor ที่ใช้บัฟเฟอร์วงแหวน 2 รายการด้านในเพื่อรองรับฟังก์ชัน WASM ที่ใช้เฟรม 512 เข้าและออก (หมายเลข 512 ตรงนี้เลือกมาแบบสุ่ม)
อัลกอริทึมสำหรับแผนภาพจะเป็น:
- AudioWorkletProcessor จะพุช 128 เฟรมลงใน Input RingBuffer จากอินพุต
- ทำตามขั้นตอนต่อไปนี้เฉพาะในกรณีที่ Input RingBuffer มีเฟรมมากกว่าหรือเท่ากับ 512 เฟรม
- ดึงเฟรม 512 จาก Input RingBuffer
- ประมวลผลเฟรม 512 ด้วยฟังก์ชัน WASM ที่กำหนด
- พุชเฟรม 512 ไปยังเอาต์พุต RingBuffer
- AudioWorkletProcessor จะดึงเฟรม 128 เฟรมจากเอาต์พุต RingBuffer เพื่อใส่ในเอาต์พุต
ดังที่แสดงในแผนภาพ ระบบจะสะสมเฟรมอินพุตลงในอินพุต RingBuffer เสมอ และจะจัดการกับบัฟเฟอร์ล้นโดยเขียนทับบล็อกเฟรมที่เก่าที่สุดในบัฟเฟอร์ ซึ่งเป็นเรื่องที่สมเหตุสมผลสำหรับ แอปพลิเคชันเสียงแบบเรียลไทม์ ในทํานองเดียวกัน ระบบจะดึงบล็อกเฟรมเอาต์พุตเสมอ การไหลล้นของบัฟเฟอร์ (ข้อมูลไม่เพียงพอ) ในเอาต์พุต RingBuffer จะทําให้เกิดข้อบกพร่องในสตรีม
รูปแบบนี้มีประโยชน์เมื่อแทนที่ ScriptProcessorNode (SPN) ด้วย AudioWorkletNode เนื่องจาก SPN ให้นักพัฒนาซอฟต์แวร์เลือกขนาดบัฟเฟอร์ระหว่าง 256 ถึง 16384 เฟรมได้ ดังนั้นการแทนที่แบบดร็อปอินของ SPN ด้วย AudioWorkletNode อาจทำได้ยาก และการใช้บัฟเฟอร์วงแหวนจึงเป็นวิธีแก้ปัญหาเบื้องต้นที่ดี โปรแกรมอัดเสียงก็เป็นตัวอย่างที่ดีซึ่งสร้างได้จากการออกแบบนี้
อย่างไรก็ตาม คุณต้องเข้าใจว่าการออกแบบนี้จะปรับเฉพาะขนาดบัฟเฟอร์ที่ไม่ตรงกันเท่านั้น และจะไม่ทำให้มีเวลามากขึ้นในการเรียกใช้โค้ดสคริปต์ที่ระบุ หากโค้ดทำงานไม่เสร็จสิ้นภายในระยะเวลาของควอนตัมในการแสดงผล (ประมาณ 3 มิลลิวินาทีที่ 44.1 กิโลเฮิร์ตซ์) ก็จะส่งผลต่อเวลาเริ่มต้นของฟังก์ชันเรียกกลับที่ตามมา และทำให้เกิดข้อบกพร่องในที่สุด
การผสมผสานการออกแบบนี้กับ WebAssembly อาจซับซ้อนเนื่องจากการจัดการหน่วยความจำเกี่ยวกับฮีป WASM เวลาที่เขียนข้อมูลที่ต้องเข้าและออกจากฮีป WASM จะต้องมีการโคลน แต่เราสามารถใช้คลาส HeapAudioBuffer เพื่อทำให้การจัดการหน่วยความจำง่ายขึ้นเล็กน้อย จะมีการกล่าวถึงแนวคิดในการใช้หน่วยความจำที่จัดสรรโดยผู้ใช้เพื่อลดการโคลนข้อมูลที่ซ้ำซ้อนกันในอนาคต
คุณดูคลาส RingBuffer ได้ที่นี่
WebAudio Powerhouse: Worklet เสียงและ SharedArrayBuffer
รูปแบบการออกแบบล่าสุดในบทความนี้คือการใส่ API ล้ำสมัยหลายรายการไว้ในที่เดียว ซึ่งได้แก่ Audio Worklet, SharedArrayBuffer, Atomics และ Worker การตั้งค่าที่ไม่สำคัญนี้ช่วยปลดล็อกเส้นทางให้ซอฟต์แวร์เสียงที่มีอยู่ซึ่งเขียนด้วย C/C++ ทำงานในเว็บเบราว์เซอร์ ในขณะเดียวกันก็ยังคงรักษาประสบการณ์ของผู้ใช้ที่ราบรื่น
ข้อได้เปรียบสูงสุดของการออกแบบนี้คือสามารถใช้ DedicatedWorkerGlobalScope สำหรับการประมวลผลเสียงเพียงอย่างเดียว ใน Chrome WorkerGlobalScope จะทำงานในเทรดที่มีลำดับความสำคัญต่ำกว่าเทรดการแสดงผล WebAudio แต่มีข้อดีมากกว่า AudioWorkletGlobalScope DedicatedWorkerGlobalScope จะมีข้อจำกัดน้อยกว่าในแง่ของแพลตฟอร์ม API ที่มีอยู่ในขอบเขต คุณจะได้รับการสนับสนุนที่ดียิ่งขึ้นจาก Emscripten เนื่องจาก Worker API มีให้บริการมาหลายปีแล้ว
SharedArrayBuffer มีบทบาทสำคัญเพื่อให้การออกแบบนี้ทำงานได้อย่างมีประสิทธิภาพ แม้ว่าทั้ง Worker และ AudioWorkletProcessor จะมีการรับส่งข้อความแบบไม่พร้อมกัน (MessagePort) แต่การประมวลผลเสียงแบบเรียลไทม์อาจไม่ดีเท่าที่ควรเนื่องจากการจัดสรรหน่วยความจำและเวลาในการตอบสนองของการรับส่งข้อความซ้ำ เราจึงจัดสรรบล็อกหน่วยความจำไว้ด้านหน้า ซึ่งเข้าถึงได้จากเทรดทั้งสองเพื่อการโอนข้อมูลแบบ 2 ทิศทางอย่างรวดเร็ว
จากมุมมองของ Web Audio API ทั้งหมด การออกแบบนี้อาจดูด้อยประสิทธิภาพเนื่องจากใช้ Audio Worklet เป็น "ซิงก์เสียง" ง่ายๆ และทำทุกอย่างในผู้ปฏิบัติงาน แต่เมื่อพิจารณาถึงต้นทุนของการเขียนโปรเจ็กต์ C/C++ ใหม่ใน JavaScript อาจเป็นสิ่งที่ไม่อนุญาตหรืออาจเป็นไปไม่ได้เลย การออกแบบนี้อาจเป็นเส้นทางการติดตั้งใช้งานที่มีประสิทธิภาพมากที่สุดสำหรับโปรเจ็กต์ดังกล่าว
สถานะและ Atomics ที่ใช้ร่วมกัน
เมื่อใช้หน่วยความจำที่ใช้ร่วมกันสำหรับข้อมูลเสียง การเข้าถึงจากทั้ง 2 ฝั่งต้องสอดคล้องกัน การแชร์สถานะที่เข้าถึงได้ในระดับอะตอมเป็นทางออกสำหรับปัญหาดังกล่าว เราสามารถใช้ประโยชน์จาก Int32Array
ที่ได้รับการสนับสนุนจาก SAB สำหรับวัตถุประสงค์นี้
กลไกการซิงค์: SharedArrayBuffer และ Atomics
แต่ละฟิลด์ของอาร์เรย์รัฐจะแสดงข้อมูลสำคัญเกี่ยวกับบัฟเฟอร์ที่ใช้ร่วมกัน ตัวเลือกที่สำคัญที่สุดคือช่องสำหรับการซิงค์ (REQUEST_RENDER
) แนวคิดคือให้ผู้ปฏิบัติงานรอให้ AudioWorkletProcessor เข้ามาแตะช่องนี้ และประมวลผลเสียงเมื่อเริ่มทำงาน นอกจาก SharedArrayBuffer (SAB) แล้ว Atomics API ยังทําให้กลไกนี้ทําได้
โปรดทราบว่าการซิงค์ 2 เทรดนั้นค่อนข้างหลวม การเริ่มต้นของ Worker.process()
จะทริกเกอร์โดยเมธอด AudioWorkletProcessor.process()
แต่ AudioWorkletProcessor จะไม่รอให้ Worker.process()
ทำงานเสร็จ ระบบนี้ออกแบบให้ AudioWorkletProcessor ทำงานด้วยโค้ดเรียกกลับของเสียง ดังนั้นจึงต้องไม่ถูกบล็อกแบบพร้อมกัน ในกรณีที่แย่ที่สุด สตรีมเสียงอาจพบปัญหาที่ซ้ำกันหรือหายไป แต่ระบบจะกู้คืนได้ในที่สุดเมื่อประสิทธิภาพการแสดงผลมีความเสถียร
การตั้งค่าและการเรียกใช้
ดังที่แสดงในแผนภาพด้านบน การออกแบบนี้มีองค์ประกอบหลายอย่างในการจัดเรียง ได้แก่ DedicatedWorkerGlobalScope (DWGS), AudioWorkletGlobalScope (AWGS), SharedArrayBuffer และเทรดหลัก ขั้นตอนต่อไปนี้จะอธิบายสิ่งที่ควรเกิดขึ้นในระยะเริ่มต้น
การเริ่มต้น
- [Main] เครื่องมือสร้าง AudioWorkletNode ได้รับการเรียกใช้
- สร้างผู้ปฏิบัติงาน
- ระบบจะสร้าง AudioWorkletProcessor ที่เชื่อมโยง
- [DWGS] ผู้ปฏิบัติงานสร้าง SharedArrayBuffer 2 รายการ (ช่องหนึ่งสำหรับสถานะที่แชร์และอีกช่องหนึ่ง สำหรับข้อมูลเสียง)
- [DWGS] Worker ส่งการอ้างอิง SharedArrayBuffer ไปยัง AudioWorkletNode
- [Main] AudioWorkletNode ส่งการอ้างอิง SharedArrayBuffer ไปยัง AudioWorkletProcessor
- [AWGS] AudioWorkletProcessor จะแจ้ง AudioWorkletNode ว่าการตั้งค่าเสร็จสมบูรณ์แล้ว
เมื่อการเริ่มต้นเสร็จสมบูรณ์แล้ว AudioWorkletProcessor.process()
จะเริ่มถูกเรียกใช้ ต่อไปนี้เป็นสิ่งที่ควรเกิดขึ้นในการวนซ้ำการแสดงผลแต่ละครั้ง
ลูปการแสดงผล
- [AWGS] ระบบจะเรียกใช้
AudioWorkletProcessor.process(inputs, outputs)
สำหรับ ควอนตัมในการแสดงผลทั้งหมดinputs
จะพุชไปยัง Input SAB- ระบบจะเติม
outputs
โดยการใช้ข้อมูลเสียงในเอาต์พุต SAB - อัปเดต สถานะ SAB ด้วยดัชนีบัฟเฟอร์ใหม่ตามความเหมาะสม
- หากเอาต์พุต SAB เข้าใกล้เกณฑ์ต่ำกว่าเกณฑ์ ให้ Wake Worker แสดงผลข้อมูลเสียงเพิ่มเติม
- [DWGS] ผู้ปฏิบัติงานรอ (อยู่ในโหมดสลีป) เพื่อให้สัญญาณปลุกตั้งแต่วันที่
AudioWorkletProcessor.process()
เมื่อตื่นนอน ให้ทำดังนี้- ดึงข้อมูลดัชนีบัฟเฟอร์จาก States SAB
- เรียกใช้ฟังก์ชันประมวลผลด้วยข้อมูลจาก Input SAB เพื่อเติม Export SAB
- อัปเดต สถานะ SAB ที่มีดัชนีบัฟเฟอร์ตามนั้น
- เข้าสู่โหมดสลีปและรอสัญญาณถัดไป
ดูโค้ดตัวอย่างได้ที่นี่ แต่โปรดทราบว่าคุณต้องเปิดใช้แฟล็กการทดสอบ SharedArrayBuffer เพื่อให้การสาธิตนี้ทำงานได้ โค้ดนี้เขียนขึ้นด้วยโค้ด JS ที่แท้จริงเพื่อให้ง่ายต่อการใช้งาน แต่สามารถแทนที่โค้ด WebAssembly ได้หากจำเป็น คุณควรจัดการเคสดังกล่าวด้วยความระมัดระวังเป็นพิเศษโดยการรวมการจัดการหน่วยความจำด้วยคลาส HeapAudioBuffer
บทสรุป
เป้าหมายสูงสุดของ Audio Worklet คือการทำให้ Web Audio API "ขยายได้" อย่างแท้จริง เราต้องใช้เวลาหลายปีในการออกแบบเพื่อให้สามารถนำ Web Audio API ส่วนที่เหลือไปใช้กับ Audio Worklet ได้ ตอนนี้การออกแบบของเรามีความซับซ้อนมากขึ้นและอาจเป็นความท้าทายที่คาดไม่ถึง
โชคดีที่เหตุผลของความซับซ้อนดังกล่าวเป็นเพียงการส่งเสริมนักพัฒนาแอปเท่านั้น การที่สามารถเรียกใช้ WebAssembly บน AudioWorkletGlobalScope จะปลดล็อกศักยภาพอันยิ่งใหญ่สำหรับการประมวลผลเสียงประสิทธิภาพสูงบนเว็บ สำหรับแอปพลิเคชันเสียงขนาดใหญ่ที่เขียนด้วย C หรือ C++ การใช้ Audio Worklet กับ SharedArrayBuffers และ Workers ก็อาจเป็นทางเลือกที่น่าสนใจในการทดลอง
เครดิต
ขอขอบคุณเป็นพิเศษสำหรับ Chris Wilson, Jason Miller, Joshua Bell และ Raymond Toy ที่ตรวจสอบฉบับร่างของบทความนี้และให้ความคิดเห็นเชิงลึก