"عندما تضرب الساعة اثني عشر". أو إكليل في المتصفح

لنفترض أن لدينا العديد من المراقبين. وأردنا استخدام هذه الشاشات كإكليل. على سبيل المثال ، اجعلها وميض في نفس الوقت. أو ربما يغير اللون بشكل متزامن وفقًا لنوع من الخوارزميات الذكية. وماذا لو كنت تفعل ذلك في المتصفح - ثم يمكنك توصيل الهواتف الذكية والأجهزة اللوحية بهذا. كل ما في متناول اليد.



وبما أننا نستخدم متصفحًا ، يمكنك أيضًا إضافة تصميم صوت. بعد كل شيء ، إذا كان دقيقًا بما يكفي لمزامنة الأجهزة في الوقت المناسب ، فيمكنك تشغيل الأصوات على كل منها كما لو كان نظام واحد متعدد القنوات يبدو.


ما الذي يمكن مواجهته عند مزامنة Web Audio وساعات اللعب داخل تطبيق javascript ؛ كم من "ساعات" مختلفة هناك في javasctipt (ثلاثة!) ولماذا كل منهم مطلوبة ، وكذلك تطبيق جاهز ل node.js تحت القط.

تحقق على مدار الساعة


لأي إكليل مشروط عبر الإنترنت ، يلزم التزامن الدقيق للساعة. بعد كل شيء ، ثم يمكنك تجاهل أي تأخير (حتى متقطع) في الشبكة. يكفي تزويد أوامر التحكم بختم زمني وإنشاء هذه الأوامر قليلاً "في المستقبل". على العملاء ، سيتم تخزينها مؤقتًا ثم تنفيذها بشكل متزامن وفي الوقت المحدد.

أو يمكنك حتى الذهاب أبعد من ذلك - خذ خوارزمية عشوائية حتمية قديمة جيدة واستخدام بذرة واحدة مشتركة (الصادرة عن الخادم مرة واحدة ، عند الاتصال) على جميع الأجهزة. إذا كنت تستخدم مثل هذه البذرة مع الوقت المحدد ، فيمكنك تحديد سلوك الخوارزمية مسبقًا على جميع الأجهزة. فقط تخيل: في الواقع ، لا تحتاج إلى شبكة أو خادم لتغيير الحالة بشكل فريد ومتزامن. تحتوي البذور بالفعل على "تسجيل الفيديو" الكامل (غير المشروط) للإجراءات مقدماً. الشيء الرئيسي هو الوقت المحدد.



كل طريقة لها حدود قابلية التطبيق. مع إدخال المستخدم الفوري ، بالطبع ، لا يوجد شيء يجب القيام به ، يبقى لنقله "كما هو". ولكن كل ما يمكن حسابه هو أن يحسب. في تطبيقي ، أستخدم الطرق الثلاثة ، اعتمادًا على الموقف.

ذاتي "في نفس الوقت"


من الناحية المثالية ، يجب أن يبدو كل شيء "في نفس الوقت" - لا يلزم أكثر من 10 مللي ثانية من التناقض لأسوأ زوج بين الأجهزة المدمجة. لا يمكنك الاعتماد على هذه الدقة من وقت النظام ، والطرق القياسية لمزامنة الوقت باستخدام بروتوكول NTP غير متوفرة في المستعرض. لذلك ، سوف نقود خادم التزامن لدينا. المبدأ بسيط: خوذة "الأصوات" وقبول "كرة الطاولة" مع الطابع الزمني للخادم. إذا قمت بذلك عدة مرات متتالية ، فيمكنك تسوية الخطأ إحصائيًا والحصول على متوسط ​​وقت التأخير.

الرمز: حساب وقت الخادم على العميل
let pingClientTime = 1; // performace.now() time when ping started let pongClientTime = 3; // performace.now() time when pong received let pongServerTime = 20; // server timstamp in pong answer let clientServerRawOffset = pongServerTime - pongClientTime; let pingPongOffset = pongClientTime - pingClientTime; // roundtrip let estimatedPingOffset = pingPongOffset / 2; // one-way let offset = clientServerRawOffset + estimatedPingOffset; console.log(estimatedPingOffset) // 1 console.log(offset); // 18 let sharedServerTime = performace.now() + offset; 



تعتبر مجموعات الويب والحلول المستندة إليها أكثر ملائمة لأنهم لا يحتاجون إلى وقت لإنشاء اتصال TCP ، ويمكنك "التواصل" معهم في كلا الاتجاهين. لا UDP أو ICMP ، بالطبع ، ولكن أسرع بشكل لا يضاهى من اتصال بارد معتاد باستخدام HTTP API. لذلك ، socket.io. كل شيء سهل جدا هناك:

كود: socket.io التنفيذ
 // server socket.on('ping', (pongCallback) => { let pongServerTime = performace.now(); pongCallback(pongServerTime); }); //client const binSize = 100; let clientServerCalculatedOffset; function ping() { socket.emit('ping', pongCallback); const pingClientTime = performace.now(); function pongCallback(pongServerTime) { const pongClientTime = performace.now(); const clientServerRawOffset = pongServerTime - pongClientTime; const pingPongOffset = pongClientTime - pingClientTime; // roundtrip const estimatedPingOffset = pingPongOffset / 2; // one-way const offset = clientServerRawOffset + estimatedPingOffset; offsets.unshift(offset); offsets.splice(binSize); let offsetSum = 0; offsets.forEach((offset) => { offsetSum += offset; }); clientServerCalculatedOffset = offsetSum / offset.length(); } } 

سيكون من الجيد ، بدلاً من حساب المتوسط ​​، حساب الوسيط - سيؤدي ذلك إلى تحسين الدقة مع اتصال غير مستقر. اختيار أساليب التصفية متروك للقارئ. أنا عمدا تبسيط الكود هنا لصالح الخطط. يمكن العثور على الحل الكامل الخاص بي في المستودع.


الأداء (الآن)


اسمح لي أن أذكرك بأن كائن performance هو واجهة برمجة التطبيقات توفر الوصول إلى جهاز ضبط الوقت عالي الدقة. دعونا نقارن:

  • Date.now() عدد المللي ثانية منذ 1 يناير 1970 ، ويقوم بذلك في شكل عدد صحيح . وهذا هو ، الخطأ فقط من التقريب هو 0.5 مللي في المتوسط. على سبيل المثال ، في عملية الطرح واحدة ab يمكنك "الفقد" حتى 2 مللي ثانية بنجاح. بالإضافة إلى ذلك ، من الناحية التاريخية والمفاهيمية ، لا يضمن عداد الوقت نفسه دقة عالية ويتم شحذه للعمل بمقياس زمني أكبر.
  • إرجاع performance.now() عدد المللي ثانية منذ فتح صفحة الويب .
    هذا هو API الحديثة نسبيا ، "شحذ" خصيصا لقياس دقيق للفواصل الزمنية. يُرجع قيمة الفاصلة العائمة ، مع إعطاء نظريًا مستوى دقة قريبًا من إمكانيات نظام التشغيل نفسه.


أعتقد أن هذه المعلومات معروفة لكل مطوري جافا سكريبت تقريبًا. ولكن ليس الجميع يعلم ذلك ...

شبح


نظرًا لهجوم توقيت Specter المثير في عام 2018 ، يذهب كل شيء إلى درجة أن الموقت عالي الدقة سوف يكون خشنًا بشكل مصطنع إذا لم يكن هناك حل آخر لمشكلة الضعف. يبدأ فايرفوكس ، بدءًا من الإصدار 60 ، في تقريب قيمة هذا المؤقت إلى مللي ثانية ، أما الحافة ، فهي أسوأ.

إليك ما تقوله MDN :

الطابع الزمني ليس في الواقع عالية الدقة. لتخفيف تهديدات الأمان مثل Specter ، تقوم المتصفحات حاليًا بتجميع النتائج بدرجات متفاوتة. (بدأ تقريب Firefox إلى 1 ميلي ثانية في Firefox 60.) قد تقوم بعض المتصفحات أيضًا بتخصيص الطابع الزمني قليلاً. قد تتحسن الدقة مرة أخرى في الإصدارات المستقبلية ؛ لا يزال مطورو المتصفح يبحثون في هجمات التوقيت هذه وأفضل السبل لتخفيفها.

دعنا نجري الاختبار ونلقي نظرة على الرسوم البيانية. هذا هو نتيجة الاختبار على الفاصل الزمني من 10 مللي ثانية:

رمز الاختبار: قياس الوقت في دورة
 function measureTimesLoop(length) { const d = new Array(length); const p = new Array(length); for (let i = 0; i < length; i++) { d[i] = Date.now(); p[i] = performance.now(); } return { d, p } } 


Date.now()
performance.now()

حافة



إحصائيات
نسخة المتصفح: 44.17763.771.0

Date.now ()

متوسط ​​الفاصل الزمني: 1.0538336052202284 مللي
الانحراف عن متوسط ​​الفترة الزمنية ، RMS: 0.7547819181245603 مللي
متوسط ​​الفاصل: 1 مللي ثانية

الأداء (الآن)

متوسط ​​الفاصل الزمني: 1.567100970873786 مللي
الانحراف عن متوسط ​​الفترة الزمنية ، RMS: 0.6748006785171455 ms
متوسط ​​الفاصل الزمني: 1.5015000000003056 مللي ثانية


فايرفوكس



إحصائيات
نسخة المتصفح: 71.0

Date.now ()

متوسط ​​الفاصل الزمني: 1.0168350168350169 مللي
الانحراف عن متوسط ​​الفاصل الزمني ، RMS: 0.21645930182417966 مللي
متوسط ​​الفاصل: 1 مللي ثانية

الأداء (الآن)

متوسط ​​الفاصل الزمني: 1.0134453781512605 مللي ثانية
الانحراف عن متوسط ​​الفترة الزمنية ، RMS: 0.1734108492762375 مللي
متوسط ​​الفاصل: 1 مللي ثانية


الكروم



إحصائيات
نسخة المتصفح: 79.0.3945.88

Date.now ()

متوسط ​​الفاصل الزمني: 1.02442996742671 مللي
الانحراف عن متوسط ​​الفترة الزمنية ، RMS: 0.49858684744444 ms
متوسط ​​الفاصل: 1 مللي ثانية

الأداء (الآن)

متوسط ​​الفاصل الزمني: 0.005555847229948915 مللي
الانحراف عن متوسط ​​الفترة الزمنية ، RMS: 0.027497846727194235 مللي
متوسط ​​الفاصل الزمني: 0.0050000089686363935 ms


حسنًا ، Chrome ، تكبير إلى 1 ميللي ثانية.



لذلك ، لا يزال Chrome مستمرًا ، ولم يتم خنق تطبيقه performance.now() والخطوة جميلة 0.005 مللي ثانية. تحت الحافة ، يكون مؤقت المؤقت performance.now() أكثر صرامة من Date.now() ! في Firefox ، يتمتع كلا المؤقتين بنفس دقة المللي ثانية.

في هذه المرحلة ، يمكن بالفعل استخلاص بعض الاستنتاجات. ولكن هناك توقيت آخر في جافا سكريبت (والتي بدونها لا يمكننا الاستغناء عنها).

WebAudio API الموقت


هذا وحش مختلف قليلاً. يتم استخدامه لقوائم انتظار الصوت المتأخرة. الحقيقة هي أن الأحداث الصوتية (تشغيل الملاحظات ، إدارة التأثيرات) لا يمكن أن تعتمد على أدوات جافا سكريبت غير متزامنة قياسية: setInterval و setTimeout - بسبب setTimeout الكبير للغاية. وهذا ليس مجرد خطأ في قيم المؤقت (الذي تعاملنا معه سابقًا) ، بل هو الخطأ الذي ينفّذ به آلة الأحداث الأحداث. وهو بالفعل شيء حوالي 5-25 مللي ثانية ، حتى في ظروف الاحتباس الحراري.

الرسوم البيانية للحالة غير المتزامنة تحت المفسد
نتيجة الاختبار على فاصل زمني قدره 100 مللي ثانية:

رمز الاختبار: قياس الوقت في دورة غير متزامن
 function pause(duration) { return new Promise((resolve) => { setInterval(() => { resolve(); }, duration); }); } async function measureTimesInAsyncLoop(length) { const d = new Array(length); const p = new Array(length); for (let i = 0; i < length; i++) { d[i] = Date.now(); p[i] = performance.now(); await pause(1); } return { d, p } } 


Date.now()
performance.now()

حافة



إحصائيات
نسخة المتصفح: 44.17763.771.0

Date.now ()

متوسط ​​الفاصل الزمني: 25.595959595959595 مللي
الانحراف عن متوسط ​​الفترة الزمنية ، RMS: 10.12639235162126 ms
متوسط ​​الفاصل: 28 مللي ثانية

الأداء (الآن)

متوسط ​​الفاصل الزمني: 25.862596938775525 مللي
الانحراف عن متوسط ​​الفترة الزمنية ، RMS: 10.123711255512573 ms
متوسط ​​الفاصل: 27.027099999999336 مللي


فايرفوكس



إحصائيات
نسخة المتصفح: 71.0

Date.now ()

متوسط ​​الفاصل الزمني: 1.6914893617021276 مللي
الانحراف عن متوسط ​​الفترة الزمنية ، RMS: 0.6018870280772611 مللي
متوسط ​​الفاصل: 2 مللي ثانية

الأداء (الآن)

متوسط ​​الفاصل الزمني: 1.7865168539325842 مللي
الانحراف عن متوسط ​​الفترة الزمنية ، RMS: 0.6442818510935484 ms
متوسط ​​الفاصل: 2 مللي ثانية


الكروم



إحصائيات
نسخة المتصفح: 79.0.3945.88

Date.now ()

متوسط ​​الفترة الزمنية: 4.7878787878787888 ، ms
الانحراف عن متوسط ​​الفترة الزمنية ، RMS: 0.7557553886872682 ms
متوسط ​​الفاصل: 5 مللي ثانية

الأداء (الآن)

متوسط ​​الفاصل الزمني: 4.783989898979516 مللي
الانحراف عن متوسط ​​الفاصل الزمني ، RMS: 0.6483716900974945 مللي
متوسط ​​الفاصل الزمني: 4.750000000058208 مللي ثانية



ربما يتذكر شخص ما أول تطبيقات صوتية تجريبية لـ HTML. قبل وصول WebAudio الكامل إلى المتصفحات - بدت جميعها تبدو كأنها في حالة سكر قليلاً. فقط لأنهم استخدموا setTimeout .

بالمقابل ، يقدم WebAudio API الحديث دقة مضمونة تصل إلى 0.02 مللي ثانية (تكهنات تستند إلى تردد أخذ العينات يبلغ 44100 هرتز). هذا يرجع إلى حقيقة أنه يتم استخدام آلية مختلفة لتشغيل الصوت المتأخر من setTimeout :

 source.start(when); 

في الواقع ، فإن أي استنساخ لعينة الصوت هو "تأخر". فقط لفقدانها "لم يتم تأجيلها" ، تحتاج إلى تأجيلها "حتى الآن".

 source.start(audioCtx.currentTime); 

حول الموسيقى في الوقت الحقيقي ولدت البرمجيات
إذا كنت تلعب لحن توليفه برنامج من الملاحظات ، ثم تحتاج إلى إضافة هذه الملاحظات إلى قائمة انتظار التشغيل مقدما قليلا. بعد ذلك ، على الرغم من جميع القيود والمخالفات غير الأساسية الخاصة بالوقت ، فإن اللحن سوف يلعب بسلاسة تامة.

بمعنى آخر ، لا ينبغي "اختراع" اللحن الذي تم توليفه في الوقت الفعلي في الوقت الفعلي ، ولكن مقدمًا بقليل.


توقيت واحد للحكم عليهم جميعا


نظرًا لأن audioCtx.currentTime مستقر ودقيق للغاية ، فربما يتعين علينا استخدامه كمصدر رئيسي للوقت النسبي؟ دعنا نجري الاختبار مرة أخرى.

رمز الاختبار: قياس قياس الوقت متزامن في دورة
 function measureTimesInLoop(length) { const d = new Array(length); const p = new Array(length); const a = new Array(length); for (let i = 0; i < length; i++) { d[i] = Date.now(); p[i] = performance.now(); a[i] = audioCtx.currentTime * 1000; } return { d, p, a } } 


Date.now()
performance.now()
audioCtx.currentTime

حافة



إحصائيات
نسخة المتصفح: 44.17763.771.0

Date.now ()

متوسط ​​الفاصل الزمني: 1.037037037037037 مللي
الانحراف عن متوسط ​​الفترة الزمنية ، RMS: 0.6166609846299806 ms
متوسط ​​الفاصل: 1 مللي ثانية

الأداء (الآن)

متوسط ​​الفاصل الزمني: 1.5447103117505993 مللي
الانحراف عن متوسط ​​الفترة الزمنية ، RMS: 0.4390514285320851 ms
متوسط ​​الفاصل الزمني: 1.5015000000000782 مللي ثانية

audioCtx.currentTime

متوسط ​​الفاصل الزمني: 2.955751134714949 مللي
الانحراف عن متوسط ​​الفترة الزمنية ، RMS: 0.6193645611529503 ms
متوسط ​​الفاصل الزمني: 2.902507781982422 مللي



فايرفوكس



إحصائيات
نسخة المتصفح: 71.0

Date.now ()

متوسط ​​الفاصل الزمني: 1.005128205128205 مللي ثانية
الانحراف عن متوسط ​​الفترة الزمنية ، RMS: 0.12392867665225249 ms
متوسط ​​الفاصل: 1 مللي ثانية

الأداء (الآن)

متوسط ​​الفاصل الزمني: 1.00513698630137 مللي
الانحراف عن متوسط ​​الفترة الزمنية ، RMS: 0.07148844433269844 ms
متوسط ​​الفاصل: 1 مللي ثانية

audioCtx.currentTime

لا يقوم Firefox بتحديث قيمة مؤقت الصوت في حلقة المزامنة



الكروم



إحصائيات
نسخة المتصفح: 79.0.3945.88

Date.now ()

متوسط ​​الفاصل الزمني: 1.0207612456747406 مللي
الانحراف عن متوسط ​​الفترة الزمنية ، RMS: 0.49870223457982504 مللي
متوسط ​​الفاصل: 1 مللي ثانية

الأداء (الآن)

متوسط ​​الفاصل الزمني: 0.005414502034674972 مللي
الانحراف عن متوسط ​​الفترة الزمنية ، RMS: 0.027441293974958335 ms
متوسط ​​الفاصل الزمني: 0.004999999873689376 ms

audioCtx.currentTime

متوسط ​​الفاصل الزمني: 3.0877599266656963 مللي
الانحراف عن متوسط ​​الفترة الزمنية ، RMS: 1.1445555956407658 ms
متوسط ​​الفاصل الزمني: 2.9024943310650997 مللي



الرسوم البيانية للحالة غير المتزامنة تحت المفسد
رمز الاختبار: قياس الوقت في دورة غير متزامن
نتيجة الاختبار على فاصل زمني قدره 100 مللي ثانية:

 function pause(duration) { return new Promise((resolve) => { setInterval(() => { resolve(); }, duration); }); } async function measureTimesInAsyncLoop(length) { const d = new Array(length); const p = new Array(length); const a = new Array(length); for (let i = 0; i < length; i++) { d[i] = Date.now(); p[i] = performance.now(); await pause(1); } return { d, p } } 



Date.now()
performance.now()
audioCtx.currentTime

حافة



إحصائيات
نسخة المتصفح: 44.17763.771.0

Date.now ():

متوسط ​​الفاصل الزمني: 24.505050505050505 مللي ثانية
الانحراف عن متوسط ​​الفترة الزمنية: 11.513166584195204 مللي ثانية
متوسط ​​الفاصل: 26 مللي ثانية

الأداء. الآن ():

متوسط ​​الفاصل الزمني: 24.50935757575754 مللي ثانية
الانحراف عن متوسط ​​الفترة الزمنية: 11.679091435527388 مللي
متوسط ​​الفاصل: 25.525499999999738 مللي

audioCtx.currentTime:

متوسط ​​الفاصل الزمني: 24.76005164944396 مللي
الانحراف عن متوسط ​​الفترة الزمنية: 11.311571546205316 مللي
متوسط ​​الفاصل: 26.121139526367187 مللي


فايرفوكس



إحصائيات
نسخة المتصفح: 71.0

Date.now ():

متوسط ​​الفاصل الزمني: 1.6875 مللي ثانية
الانحراف عن متوسط ​​الفترة الزمنية: 0.6663410663216448 مللي
متوسط ​​الفاصل: 2 مللي ثانية

الأداء. الآن ():

متوسط ​​الفاصل الزمني: 1.7234042553191489 مللي
الانحراف عن متوسط ​​الفترة الزمنية: 0.6588877688171075 مللي
متوسط ​​الفاصل: 2 مللي ثانية

audioCtx.currentTime:

متوسط ​​الفاصل الزمني: 10.158730158730123 مللي
الانحراف عن متوسط ​​الفترة الزمنية: 1.4512471655330046 مللي
متوسط ​​الفاصل الزمني: 8.707482993195299 مللي ثانية


الكروم



إحصائيات
نسخة المتصفح: 79.0.3945.88

Date.now ():

متوسط ​​الفاصل الزمني: 4.585858585858586 مللي
الانحراف عن متوسط ​​الفاصل الزمني: 0.9102125516015199 مللي
متوسط ​​الفاصل: 5 مللي ثانية

الأداء. الآن ():

متوسط ​​الفاصل الزمني: 4.592424242424955 مللي
الانحراف عن متوسط ​​الفترة الزمنية: 0.719936993603155 مللي
متوسط ​​الفاصل الزمني: 4.605000001902226 مللي

audioCtx.currentTime:

متوسط ​​الفاصل الزمني: 10.12648022171832 مللي
الانحراف عن متوسط ​​الفترة الزمنية: 1.4508887886499262 مللي ثانية
متوسط ​​الفاصل الزمني: 8.707482993197118 مللي



حسنًا ، لن ينجح الأمر. "خارج" ، هذا الموقت هو الأكثر دقة. لا يقوم Firefox بتحديث قيمة المؤقت داخل الحلقة. ولكن بشكل عام: القرار هو 3 مللي ثانية وغضب أسوأ وملحوظ. ربما تعكس قيمة audioCtx.currentTime الموضع في المخزن المؤقت الحلقي لبرنامج تشغيل بطاقة الصوت. بمعنى آخر ، يُظهر الحد الأدنى من الوقت الذي لا يزال من الممكن تأخير التشغيل بأمان.

وماذا تفعل؟ بعد كل شيء ، نحن بحاجة إلى كل من توقيت دقيق للتزامن مع الخادم وإطلاق أحداث جافا سكريبت على الشاشة ، ومؤقت صوتي لأحداث الصوت!

اتضح أنك تحتاج إلى مزامنة جميع أجهزة ضبط الوقت مع بعضها البعض:

  • audioCtx.currentTime مع العميل audioCtx.currentTime performance.now() على العميل.
  • و client.now performance.now() مع performance.now() جانب الخادم.

متزامن ، متزامن


بشكل عام ، هذا أمر مضحك للغاية إذا فكرت في الأمر: يمكن أن يكون لديك مصدران جيدان للوقت A و B ، يكون كل منهما خشنًا وصاخبًا جدًا في الإخراج (A '= A + err A ؛ B' = B + err B ) حتى يتمكن من ذلك حتى تكون غير صالحة للاستعمال من تلقاء نفسها. لكن الفرق بين المصادر الأصلية غير المزعجة يمكن استعادته بدقة شديدة.

نظرًا لأن المسافة الزمنية الحقيقية بين الساعات المثالية ثابتة ، مع أخذ القياسات n مرة ، فسوف نقوم بتقليل خطأ القياس err n times ، على التوالي. ما لم يكن ، بالطبع ، تعمل الساعة بنفس السرعة.

نعم غير متزامن


الأخبار السيئة هي أنه لا ، لا يذهبون بنفس السرعة. وأنا لا أتحدث عن اختلاف الساعات على الخادم والعميل - وهذا أمر مفهوم ومتوقع. ما هو غير متوقع أكثر: audioCtx.currentTime تدريجياً عن performance.now() . إنه داخل العميل. قد لا نلاحظ ، ولكن في بعض الأحيان ، تحت الحمل ، قد لا يبتلع نظام الصوت جزءًا صغيرًا من البيانات و (خلافًا لطبيعة المخزن المؤقت الحلقي) فإن وقت الصوت سوف يتغير بالنسبة إلى وقت النظام. لا يحدث هذا في حالات نادرة ، لا يتعلق الأمر بالكثير من الأشخاص: لكن ، على سبيل المثال ، إذا قمت بتشغيل مقطعين فيديو على YouTube في نفس الوقت في نفس الوقت على أجهزة كمبيوتر مختلفة ، فليس من الواقع أن يتوقفوا عن اللعب في وقت واحد. وهذه النقطة ، بالطبع ، ليست في الإعلان.

وبالتالي ، لعملية مستقرة ومتزامن. نحتاج إلى إعادة التحقق بانتظام من جميع الساعات مع بعضها البعض ، وذلك باستخدام وقت الخادم - كمرجع. ثم تظهر المقايضة في عدد القياسات التي يجب استخدامها لقياس المتوسط: كلما زادت الدقة ، زادت فرصة حدوث قفزة حادة في audioCtx.currentTime في نافذة الوقت التي نقوم فيها بتصفية القيم. ثم ، إذا استخدمنا ، على سبيل المثال ، نافذة الدقيقة ، فحينئذٍ سيكون لدينا الوقت المنقضي. اختيار المرشحات واسع: الأسي ، الوسيط ، مرشح كالمان ، إلخ. لكن هذه المقايضة هي على أي حال.

نافذة الوقت


في حالة مزامنة audioCtx.currentTime مع performance.now() ، في حلقة غير متزامنة ، حتى لا تتداخل مع واجهة المستخدم ، يمكننا اتخاذ قياس واحد ، على سبيل المثال ، 100 مللي ثانية.
افترض أن خطأ القياس يخطئ = errA + errB = 1 + 3 = 4 مللي ثانية
وفقًا لذلك ، في 1 ثانية ، يمكننا تقليله إلى 0.4 مللي ثانية ، وفي 10 ثوانٍ إلى 0.04 مللي ثانية. التحسين الإضافي للنتيجة ليس له معنى ، وستكون النافذة الجيدة للتصفية هي: من 1 إلى 10 ثوانٍ.

في حالة مزامنة الشبكة ، تعتبر التأخيرات والأخطاء أكثر أهمية بالفعل ، ولكن لا توجد قفزة حادة في الوقت ، كما هو الحال في audioCtx.currentTime . ويمكنك السماح لنفسك بتجميع إحصائيات رائعة حقًا. بعد كل شيء ، يمكن أن يصل الخطأ بينغ إلى 500 مللي ثانية. والقياسات نفسها لا يمكننا القيام به في كثير من الأحيان.

عند هذه النقطة ، أقترح التوقف. إذا كان أي شخص مهتمًا ، فسيسعدني إخبارك بكيفية "رسم بقية البومة". لكن كجزء من القصة حول أجهزة ضبط الوقت ، أعتقد أن قصتي قد انتهت.

وأريد أن أشارك ما حصلت عليه. كل نفس ، السنة الجديدة.

ماذا حدث


إخلاء المسئولية: من الناحية الفنية ، هذا موقع للعلاقات العامة على موقع Habré ، لكن هذا مشروع مفتوح المصدر غير ربحي تمامًا ، وأعدك أبدًا بعدم وضع إعلانات أو كسب المال بأي طريقة أخرى. على العكس من ذلك ، لقد قمت بجمع المزيد من الحالات من أموالي الآن من أجل البقاء على قيد الحياة من الصداع المحتمل. لذلك ، من فضلك ، أهل الخير ، لا تحطمني ولا تصلني. هذا هو كل متعة بحتة.

سنة جديدة سعيدة يا هبر!



snowtime.fun

يمكنك تحريف المقابض والتحكم في التصور والموسيقى والمؤثرات الصوتية. إذا كانت لديك بطاقة فيديو عادية ، فانتقل إلى الإعدادات واضبط عدد الجزيئات على 100٪.

يتطلب WebAudio و WebGL.




محدث: لا يعمل في Safari تحت macOS Mojave. لسوء الحظ ، لا توجد طريقة لمعرفة ما يحدث بسرعة ، بسبب عدم وجود Safari نفسه. يبدو أن نظام التشغيل iOS يعمل.

UPD2: إذا لم يستجب snowtime.fun و web.snowtime.fun ، حاول النطاق الفرعي habr .snowtime.fun الجديد. قام بنقل الخادم إلى مركز بيانات آخر ، وتم تخزين IP القديم في DNS مؤقتًا ، expire=1w . :(

مستودع: بيتبوكيت
عند كتابة هذا المقال ، تم استخدام الرسوم التوضيحية الخاصة بـ macrovector / Freepik .

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


All Articles